Compare commits

...

249 Commits
0.6.0 ... 0.7.5

Author SHA1 Message Date
Kevin Hester
e80de4eba0 Merge pull request #179 from geeksville/dev
omg so good
2020-06-13 16:55:32 -07:00
Kevin Hester
85f69cb908 Merge branch 'master' into dev 2020-06-13 16:49:44 -07:00
geeksville
112a94e572 0.7.5 2020-06-13 16:48:34 -07:00
geeksville
13307c502f misc debug output 2020-06-13 16:29:53 -07:00
geeksville
8a1754efe8 leave the software update service off for now - no one is using ityet 2020-06-13 11:36:45 -07:00
geeksville
8caa075bc6 used fixed pool allocator for now - since that's how we've been testing 2020-06-13 11:05:36 -07:00
geeksville
d5deb49d20 use executeDelete to prevent leaking BLE handles 2020-06-13 11:05:13 -07:00
geeksville
575a15e135 remove more dead rev1 protocol code 2020-06-13 08:29:41 -07:00
geeksville
dc7469c64b useful bluetooth debugging output 2020-06-13 08:29:25 -07:00
geeksville
f54b18f733 each tx packet might have a retransmission/ack copy, make pool bigger 2020-06-13 08:27:44 -07:00
geeksville
db66e4dc00 ensure we never get null from malloc 2020-06-13 08:27:25 -07:00
geeksville
47e614c7d6 fix #172
We need our own branch because we need this fix and associated pullrequest
https://github.com/espressif/arduino-esp32/pull/4085
2020-06-13 08:26:48 -07:00
geeksville
03cb3c2145 basic stack debugging - we are okay for now 2020-06-12 16:37:03 -07:00
geeksville
a8d4b5479d don't start the BLE update service for now - the android side isn't ready 2020-06-12 15:48:24 -07:00
geeksville
de37e1bbab todo notes 2020-06-12 15:40:36 -07:00
geeksville
f0b8f10665 Fix #149: Use a simple heap allocator for now, after 1.0 we can go to
fixed sized pools to protect against fragmentation.
2020-06-12 12:11:18 -07:00
geeksville
88b91de197 Prepare to make MemoryDynamic 2020-06-12 11:56:13 -07:00
Kevin Hester
f22bb34c07 Merge pull request #173 from geeksville/master
todo updates
2020-06-12 09:09:19 -07:00
Kevin Hester
2d1cb7a43a Merge branch 'master' into master 2020-06-12 09:02:11 -07:00
geeksville
dc169675e2 Update TODO list 2020-06-12 09:01:28 -07:00
geeksville
99f8253637 protobuf updates 2020-06-12 08:59:48 -07:00
Kevin Hester
f3244caac1 Merge pull request #171 from geeksville/master
doc updates
2020-06-11 21:19:58 -07:00
geeksville
6edaadf5d8 Update BLE docs 2020-06-11 21:14:53 -07:00
geeksville
1f668046a0 if we can't sleep, at least have the processor block for 100ms 2020-06-10 18:23:20 -07:00
Kevin Hester
cb2aa3b29f Merge pull request #168 from geeksville/usb
misc bug fixes, see below
2020-06-10 15:38:45 -07:00
geeksville
8ccd59a7d8 Fix #115: wake from light sleep if a character arrives on the serial port
Note - we do this not by using the uart wake feature, but by the lower
power GPIO edge feature.  Recommend sending "Z" 0x5A - because that has
many edges.  Send the character 4 times to make sure the device is awake
2020-06-10 15:36:38 -07:00
geeksville
ddfdae64bf Fix #167 while in light sleep, allow loop() to still run occasionally 2020-06-10 14:11:56 -07:00
geeksville
21a90a42e5 move flutter ideas into own project 2020-06-10 14:02:53 -07:00
geeksville
712d6e5c1e Merge remote-tracking branch 'root/master' into usb 2020-06-09 18:24:05 -07:00
geeksville
00d55c9daa require min app version 172 2020-06-09 18:20:06 -07:00
geeksville
a05e45f84b make txQueue private 2020-06-09 15:47:05 -07:00
Kevin Hester
e650033f2c Merge pull request #166 from geeksville/usb
Changes to support USB
2020-06-09 11:52:48 -07:00
geeksville
846fc14b4a 0.7.4 2020-06-09 10:35:13 -07:00
geeksville
a8a5e036f5 turn off serial debug output once we are using the protocol on the stream 2020-06-09 10:35:06 -07:00
geeksville
009f05b61d temp workaround for sleep bug #167 2020-06-09 06:38:09 -07:00
geeksville
d8a6f82459 Merge remote-tracking branch 'root/master' into usb 2020-06-08 16:42:45 -07:00
Kevin Hester
185fe8520a Merge pull request #164 from rradar/patch-1
Update build-instructions.md to use code tags
2020-06-08 16:42:31 -07:00
geeksville
ce9bac34d6 add a new SERIAL psm state, to ensure device doesn't sleep while
connected to the phone over USB.

In support of

https://github.com/meshtastic/Meshtastic-Android/issues/38
2020-06-08 16:37:49 -07:00
Kevin Hester
e80673ed15 Merge branch 'master' into patch-1 2020-06-08 16:32:43 -07:00
geeksville
bdbaf9c655 remove old BLE api 2020-06-08 16:08:02 -07:00
geeksville
7473a6c27a unify activity detection in PhoneAPI, turn off BLE API while serial API in use 2020-06-08 16:06:59 -07:00
Kevin Hester
4919129bbc Merge pull request #165 from slavino/patch-1
Update README.md
2020-06-08 13:53:12 -07:00
Slavomir Hustaty
a02175cec0 Update README.md
https://www.everythingrf.com/community/lora-frequency-in-europe

The LoRa Alliance has defined two frequency bands for the usage of LoRa technology in Europe. These bands are EU433 from 433.05 to 434.79 MHz and EU863 from 863 to 870 MHz.

EU433 (433.05 to 434.79 MHz)

The end devices in EU433 band operate from 433.05 to 434.79 MHz and use a channel data structure to support at least 16 channels.

and so on...
https://lora-alliance.org/sites/default/files/2018-04/lorawantm_regional_parameters_v1.1rb_-_final.pdf
2020-06-08 21:34:02 +02:00
rradar
63affdd2e7 Update build-instructions.md to use code tags
Update build-instructions.md to make (more) use of code tags
2020-06-08 10:55:03 +01:00
Kevin Hester
351687e5cd Merge pull request #160 from geeksville/longaddr
PROTOCOL CHANGE! activate 32 bit nodenums/packetids
2020-06-07 22:23:31 -07:00
geeksville
739f497bbd Merge branch 'master' into longaddr 2020-06-07 22:16:43 -07:00
geeksville
2d2ed591e9 set num_bits for nodenum and packet id after loading save file 2020-06-07 22:12:06 -07:00
geeksville
871a85d688 force all devices to discard old settings 2020-06-07 17:22:07 -07:00
geeksville
a5f05019db fix build instructions 2020-06-06 14:30:15 -07:00
geeksville
e124d2094f PROTOCOL CHANGE! activate 32 bit nodenums/packetids 2020-06-06 13:16:36 -07:00
Kevin Hester
5d874cd43b Merge pull request #159 from geeksville/master
hotfix release for reboot bug
2020-06-06 08:48:14 -07:00
geeksville
9f6cdadd3e Merge remote-tracking branch 'root/master' 2020-06-06 08:43:10 -07:00
geeksville
6a3853ef35 0.6.8 2020-06-06 08:33:20 -07:00
geeksville
9ea65c6793 Fix #153 - details below
Somehow nodenum was getting reset to zero (and saved to flash - which is
bad because it makes the failure permanent).  So I've changed nodenum
selection to occur after we load the saved preferences (and we try to keep
nodenum stable in that case).

I'm puzzled as to how it ever got set to zero (unless there *shudder*
is some errant pointer that clobbered it).  But next week I'm turning
4 byte nodenums back on, which will make this moot - because they
will always be based on macaddr and the current process where nodes
haggle with the mesh to pick a unique one-byte nodenum will be gone.
2020-06-06 08:30:01 -07:00
geeksville
8d14e97dfa oops - we were not saving radio state 2020-06-06 08:07:21 -07:00
Kevin Hester
420b7d48d9 Merge pull request #157 from geeksville/master
update webpage
2020-06-05 11:38:17 -07:00
geeksville
5915669f6f Merge remote-tracking branch 'root/master' 2020-06-05 11:33:58 -07:00
geeksville
52b01db306 announce beta 2020-06-05 11:33:19 -07:00
Kevin Hester
a0d6ecb331 Merge pull request #156 from geeksville/master
minor
2020-06-05 11:09:00 -07:00
geeksville
0271b02d50 Merge remote-tracking branch 'root/master' 2020-06-05 11:06:40 -07:00
geeksville
9f61c78c0e doc merge 2020-06-05 11:05:36 -07:00
geeksville
4b5cfaf9ba changes from bringing up PPR 2020-06-05 11:00:18 -07:00
Kevin Hester
c014c1bafe Merge pull request #155 from geeksville/master
now in beta
2020-06-04 11:28:39 -07:00
geeksville
96594516af now in beta 2020-06-04 11:25:06 -07:00
Kevin Hester
bed20bf1fa Merge pull request #151 from geeksville/longaddr
Longaddr
2020-06-04 10:41:39 -07:00
geeksville
4e5a445d8b 0.6.7 2020-06-04 10:37:08 -07:00
geeksville
99437d931e fix #153 2020-06-03 16:08:11 -07:00
geeksville
5166717298 confirm randomSeed is set correctly 2020-06-03 14:24:34 -07:00
geeksville
49b5738f4f add min_app_version so apps can warn if they are too old 2020-06-03 13:57:30 -07:00
geeksville
c753ea7cd1 don't use a fixed randomSeed. 2020-06-03 13:51:53 -07:00
geeksville
5b1488ddf0 Allow packet and nodenums to be 32 bits long (but don't change yet) 2020-06-03 13:46:31 -07:00
geeksville
a34cfb0ee0 Populate metainfo for apps to allow 32 bit node and packet ids 2020-06-03 13:15:45 -07:00
Kevin Hester
d8ff605d32 Merge pull request #147 from geeksville/nrf52
my DSR changes broke acks for flood routing also.  Fix #146
2020-06-03 12:57:56 -07:00
geeksville
8031c47602 put nrf52 on back burner for a couple of days 2020-06-03 12:55:55 -07:00
geeksville
e522e47544 Full DSR WIP 2020-06-03 12:49:36 -07:00
geeksville
ed0f79b683 Merge branch 'master' into nrf52 2020-05-27 15:54:44 -07:00
Kevin Hester
de1337d351 Merge pull request #148 from geeksville/master
fixes for @dafeman's board
2020-05-27 15:52:00 -07:00
geeksville
1b34a0c6d8 Help make sx1262 go for @dafeman 's board. See below for details:
Hi, I think the problem you were having building for ESP32 was due to
a funny thing.  Notice the #define for INTERRUPT_ATTR.  That macro expands
to IRAM_ATTR - which is a special flag the ESP32 requires for _any_ code
that is going to be called from an ISR.  So that the code is guaranteed
to be in RAM (the ESP32 uses a clever scheme where the FLASH is actually
high speed serial flash and all reads/writes are actually only happening
to a small number of pages in RAM and they have a driver that is constantly
copying blocks they need into that ram.  This essentially how VM works
for desktop computers, but in their case they are paging to FLASH.

But for code that runs in an interrupt handler must _always_ be in RAM
because if you took a 'page fault' for that code being missing in RAM they
can't nicely do their clever VM scheme.

So that's all good.  The problem was - apparently GCC for the ESP32 has a
a bug when that attribute is applied in the class declaration.  So
I moved it out into the cpp file and all seems well now.
2020-05-27 15:47:59 -07:00
geeksville
313380381b no need for this old debug output 2020-05-27 15:40:47 -07:00
geeksville
f56ff2ca20 DSR WIP 2020-05-27 15:31:32 -07:00
geeksville
da2ef0ac61 misc nrf52 todo 2020-05-27 15:31:23 -07:00
geeksville
6a3033fa85 improve NRF52 debugging environment 2020-05-26 15:55:36 -07:00
geeksville
f3a1c5e679 Possible fix for https://meshtastic.discourse.group/t/a-note-about-limited-support-for-the-neo-8m-gps-boards/233/3?u=geeksville 2020-05-26 13:10:34 -07:00
geeksville
a96c8fd4db nrf52 debugging is supported as long as BLE is not advertising 2020-05-25 17:16:09 -07:00
geeksville
da3ed9926b add monitor mode debugging support 2020-05-25 15:56:06 -07:00
geeksville
fd386d9d7f UC1701 WIP 2020-05-25 15:55:38 -07:00
geeksville
5d1614989e Only add interfaces to the router if they can be initied 2020-05-25 15:47:45 -07:00
Kevin Hester
bae73a9526 Merge branch 'master' into nrf52 2020-05-25 11:56:38 -07:00
geeksville
f4b1678535 my DSR changes broke acks for flood routing also. Fix #146 2020-05-25 11:55:42 -07:00
Kevin Hester
943517a1e4 Merge pull request #145 from geeksville/nrf52
nrf52 and misc other wip changes
2020-05-25 10:48:25 -07:00
geeksville
03999e9d56 fix build for esp32 2020-05-25 10:46:26 -07:00
geeksville
d5f177b1ee begin UC1701 driver 2020-05-25 10:41:46 -07:00
geeksville
fdaed7e323 Fix MIN_BAT_MILLIVOLTS per @spattinson 2020-05-25 10:41:19 -07:00
geeksville
d39e775c95 make flash filesystem work on NRF52 2020-05-25 10:07:42 -07:00
geeksville
829e0b6e26 fix extra free 2020-05-25 08:19:14 -07:00
geeksville
1656c8d0cb use my Timer class on all platforms, it works better than the freertos version 2020-05-25 07:48:36 -07:00
geeksville
2770cc7de3 Use the SX1262 receive duty cycle mode to get radio current draw down to
about 2.5mA @ 3V while in receive mode.
2020-05-24 19:23:50 -07:00
geeksville
48de631e04 disable activelyReceiving for sx1262 for now - it doesn't yet work 2020-05-24 16:34:18 -07:00
geeksville
66b11bcbd7 print RF52 reset reason 2020-05-24 16:20:21 -07:00
geeksville
cda7487cbe add a NRF52 hardfault handler 2020-05-24 16:08:58 -07:00
geeksville
e8f6504ec4 Make an accelerated NRF52 implementation for AEX256-CTR crypto 2020-05-24 14:45:50 -07:00
geeksville
8f1b26bdda DSR wip still kinda busted (rx packets not working - even for regular router) 2020-05-24 12:59:10 -07:00
geeksville
5bd3e4bcd0 DSR WIP 2020-05-23 17:39:08 -07:00
geeksville
fb3b62f8f0 CSR WIP 2020-05-23 15:48:23 -07:00
geeksville
e89fe2f7d9 DSR WIP 2020-05-23 12:50:33 -07:00
geeksville
16812c3ee4 add ignore_incoming to user preferences, for automated testing of DSR topologies 2020-05-23 10:01:36 -07:00
geeksville
c9cb293bf2 cleanup virtual inheritence for Router/Reliable/Flooding/DSR 2020-05-23 09:24:22 -07:00
geeksville
8e2e154cdd Merge remote-tracking branch 'root/master' into nrf52 2020-05-22 19:06:35 -07:00
geeksville
b7505ab575 Merge branch 'nrf52' 2020-05-22 19:06:14 -07:00
geeksville
cc47e29fac released 0.6.4 already 2020-05-22 19:06:08 -07:00
geeksville
608f8349d9 todo updates 2020-05-22 19:05:29 -07:00
Kevin Hester
3ecd93dcf6 Merge pull request #144 from kalon33/update_fixes
thanks @kalon33!
2020-05-22 19:01:06 -07:00
geeksville
dd0209b192 Merge remote-tracking branch 'root/master' into nrf52
# Conflicts:
#	docs/software/nrf52-TODO.md
#	src/mesh/RadioLibInterface.cpp
#	src/mesh/mesh.pb.h
2020-05-22 11:12:22 -07:00
geeksville
ae92567383 notes 2020-05-22 11:09:10 -07:00
Nicolas Derive
9f2646ba03 writeflash is actually write_flash in esptool.py 2020-05-22 12:53:54 +02:00
geeksville
925e46da8c make serial console work on nrf52 2020-05-21 21:17:53 -07:00
geeksville
a6475ce848 experiment with CFG_DEBUG (it fails) 2020-05-21 21:17:38 -07:00
geeksville
9149912a2d adafruit includes segger by default 2020-05-21 21:17:19 -07:00
geeksville
2dadb4d7a2 make nrf52dk build again 2020-05-21 20:31:22 -07:00
Kevin Hester
1897d4703c Merge pull request #142 from geeksville/reliable
Reliable messages now also work for broadcasts
2020-05-21 17:55:31 -07:00
geeksville
a25cead7d7 Merge remote-tracking branch 'root/master' into reliable 2020-05-21 17:51:50 -07:00
geeksville
3d919b21f6 0.6.4 2020-05-21 17:51:35 -07:00
geeksville
d2de04d5b2 Fix #59 no need for broadcasts when showing new node pane 2020-05-21 17:21:44 -07:00
Kevin Hester
a753c942b9 Merge pull request #141 from geeksville/reliable
Reliable
2020-05-21 16:46:01 -07:00
geeksville
9dd88281af reliable broadcast now works 2020-05-21 16:34:16 -07:00
geeksville
e75561016b retransmissions work again 2020-05-21 15:55:57 -07:00
geeksville
e2cbccb133 add want_ack support for broadcast packets 2020-05-21 12:47:41 -07:00
geeksville
0271df0657 add beginnings of full DSR routing 2020-05-21 12:47:08 -07:00
Kevin Hester
e05e324a7c Merge pull request #136 from geeksville/reliable
WIP for reliable unicast and BLE software update
2020-05-19 16:09:06 -07:00
geeksville
71041e8674 reliable unicast 1 hop works! 2020-05-19 15:51:07 -07:00
geeksville
c65b518432 less logspam 2020-05-19 14:54:58 -07:00
geeksville
6ba960ce47 one hop reliable ready for testing 2020-05-19 14:54:47 -07:00
geeksville
8bf4919576 wip reliable unicast (1 hop) 2020-05-19 11:56:17 -07:00
geeksville
cca4867987 want_ack flag added 2020-05-19 10:27:28 -07:00
geeksville
7aa47cf93b Merge remote-tracking branch 'root/master' into reliable 2020-05-19 10:20:34 -07:00
Kevin Hester
3089de79ee thanks @dafeman for documentation updates!
README GUI update
2020-05-19 07:31:43 -07:00
Dafeman
6b020149f3 Update GUI Install 2020-05-19 13:50:07 +12:00
Dafeman
34fae52e98 Merge pull request #2 from meshtastic/master
Update
2020-05-19 13:30:21 +12:00
geeksville
976bdad067 sniffReceived now allows router to inspect packets not destined for this node 2020-05-18 17:57:58 -07:00
geeksville
26d3ef529e Use the hop_limit field of MeshPacket to limit max delivery depth in
the mesh.
2020-05-18 17:35:23 -07:00
geeksville
53c3d9baa2 doc updates 2020-05-18 17:02:51 -07:00
Kevin Hester
2a6858fa34 Update CNAME 2020-05-18 08:10:51 -07:00
Kevin Hester
5440cbec6a Update CNAME 2020-05-18 08:08:57 -07:00
geeksville
19f5a5ef79 oops - use correct battery shutoff voltage 2020-05-17 05:12:16 -07:00
geeksville
ef831a0b4d Fix leaving display on in deep sleep.
We shutoff screen immediately, rather than waiting for our loop call()
2020-05-17 05:11:32 -07:00
geeksville
efc239533c Fix #133 - force deep sleep if battery reaches 10% 2020-05-17 05:10:51 -07:00
geeksville
ef1463a6a9 have tbeam charge at max rate (450mA) 2020-05-17 04:44:48 -07:00
geeksville
95e952b896 todo update 2020-05-16 16:09:06 -07:00
geeksville
db72faca5e Merge remote-tracking branch 'root/master' 2020-05-15 11:25:47 -07:00
geeksville
6961853ed7 ble software update fixes 2020-05-15 10:16:10 -07:00
geeksville
5ec5248fe4 complete ble ota move 2020-05-14 14:22:11 -07:00
geeksville
14fdd33972 move bluetooth OTA back into main tree for now 2020-05-14 14:20:05 -07:00
geeksville
140e29840a fix rare gurumeditation if we are unlucky and some ISR code is in serial flash 2020-05-14 12:46:29 -07:00
Kevin Hester
d1d318fad5 Merge pull request #135 from mehuman/supported-hardware
Supported hardware README updates
2020-05-13 08:22:23 -07:00
Mark Huson
dfb6c0bb20 Merge branch 'master' into supported-hardware 2020-05-12 19:15:47 -07:00
Mark Huson
7339abbab5 Update README.md 2020-05-12 19:14:12 -07:00
Mark Huson
27db0e27e8 Update supported hardware 2020-05-12 19:11:16 -07:00
geeksville
a0b43b9a95 Send "unset" for hwver and swver if they were unset 2020-05-12 17:57:51 -07:00
geeksville
b6a202d68e runs again with new protobufs 2020-05-12 13:35:22 -07:00
Dafeman
125035d546 Merge pull request #1 from meshtastic/master
Update
2020-05-12 22:49:54 +12:00
geeksville
9f05ad2927 remove random delay hack from broadcast, since we now do that for all transmits 2020-05-11 16:19:44 -07:00
geeksville
86ae69d360 refactor so I can track and ignore recent packets of any type 2020-05-11 16:14:53 -07:00
geeksville
c12fb69ca2 update protos 2020-05-10 14:17:05 -07:00
geeksville
8b911aba7f Cleanup build for NRF52 targets 2020-05-10 12:33:17 -07:00
geeksville
2fa595523f minor fixups to get nrf52 building again 2020-05-09 21:02:56 -07:00
geeksville
190a3c2d6b filename typo 2020-05-09 20:27:08 -07:00
Kevin Hester
1bf9d052fc Merge pull request #132 from geeksville/crypto
Crypto
2020-05-09 19:14:01 -07:00
Kevin Hester
e7b99b0d78 Merge branch 'master' into crypto 2020-05-09 19:12:00 -07:00
geeksville
96313ee1c4 remove stale link 2020-05-09 19:11:06 -07:00
geeksville
3e356e5866 Crypto works! 2020-05-09 19:08:04 -07:00
geeksville
1cc24de787 stub encryptor seems nicely backwards compatible with old devices and apps 2020-05-09 17:51:20 -07:00
geeksville
e6875d559c Remove MeshRadio wrapper class - we don't need it anymore. 2020-05-09 16:32:26 -07:00
geeksville
b73dd5b23b misc todo 2020-05-09 16:15:16 -07:00
geeksville
28d21ecdcc begin work on crypto 2020-05-09 16:15:01 -07:00
Kevin Hester
42298abfad Merge pull request #131 from Dafeman/master
Pad Bluetooth passkey to 6 digits
2020-05-09 10:13:29 -07:00
Dafeman
c4a1fe0f36 Pad Bluetooth passkey to 6 digits 2020-05-09 23:09:36 +12:00
Mark Huson
0396f8f968 Merge pull request #1 from meshtastic/master
Merge upstream
2020-05-08 08:50:58 -07:00
Kevin Hester
f1b840dede Merge pull request #128 from mehuman/device-update
Device update
2020-05-07 14:42:42 -07:00
Mark Huson
4da5d79e88 add device-update to zip 2020-05-06 19:45:02 -07:00
Mark Huson
fc0c9bcfe3 add update script and README changes 2020-05-06 19:43:17 -07:00
Kevin Hester
32d0368f59 Merge pull request #127 from geeksville/nema-124
0.6.3 - fix the problem of BLE message receiption being busted in 0.6.2
2020-05-05 18:49:44 -07:00
geeksville
8bfe9fa8fc 0.6.3 - fix the problem of BLE message receiption being busted in 0.6.2 2020-05-05 18:40:17 -07:00
Kevin Hester
f10ad07f97 Merge pull request #125 from geeksville/nema-124
support ublox 8m gpses (I think)
2020-05-04 20:18:33 -07:00
geeksville
95df7dd8dc 0.6.2 2020-05-04 20:04:44 -07:00
geeksville
dcd1f7478a fix 124 - we now fallback to nema if we can't talk ublox protocol to
the GPS.  Though we are super power inefficient about it so TODO/FIXME
someday to decrease our power draw.
2020-05-04 20:02:43 -07:00
geeksville
c2be6c4068 WIP on #124 2020-05-04 17:39:57 -07:00
geeksville
101eef5495 oops lat/lon need to be signed ;-) 2020-05-04 11:21:24 -07:00
geeksville
933d5424da abstract out the UBlox GPS driver 2020-05-04 11:15:05 -07:00
geeksville
ecf528f9b6 move gps before refactoring 2020-05-04 10:23:47 -07:00
geeksville
9b309fe0a0 Use int based lat/long from now on in the device code
for https://github.com/meshtastic/Meshtastic-device/issues/124
2020-05-04 08:09:08 -07:00
Kevin Hester
29fd8dc7a5 Merge pull request #123 from geeksville/screen
fix missing carriage returns.  thanks to @gregwalters in #119
2020-05-02 20:24:03 -07:00
geeksville
624b95782d fix missing carriage returns. thanks to @gregwalters in #119 2020-05-02 20:21:42 -07:00
Kevin Hester
4fa25042c8 Merge pull request #122 from geeksville/dropped
changes to fix #121
2020-05-02 20:17:14 -07:00
Kevin Hester
9f9cb030ad Merge branch 'master' into dropped 2020-05-02 20:14:48 -07:00
geeksville
1d9290afc0 now that the rfinterfaces are smarter, no need to do backoff in
the flood router.  the interfaces will handle it.
2020-05-02 19:53:58 -07:00
geeksville
ad2f639195 don't leak messages if they are handled locally 2020-05-02 19:53:13 -07:00
geeksville
07b4eea037 fix log msg 2020-05-02 19:52:54 -07:00
geeksville
79c61cf0e0 limit max power on rf95 to 17 (rather than 20, because 20 can...
burn up parts if you exceed 1% duty cycle)
2020-05-02 19:52:37 -07:00
geeksville
80268ea56a send() is supposed to always free buffers, even if it returns an error 2020-05-02 19:51:55 -07:00
geeksville
bb9f595b8b Fix #11 2020-05-02 19:51:25 -07:00
geeksville
2ad314f150 we now always listen before transmit - even if we have just completed a packet 2020-05-02 08:29:51 -07:00
Kevin Hester
916e62398d Merge pull request #120 from geeksville/master
fix busted build on heltec style devices (0.6.1)
2020-05-01 17:01:52 -07:00
geeksville
4735b3ff5b 0.6.1 hotfix build for busted heltec style devices 2020-05-01 16:35:32 -07:00
geeksville
cfd6483ea5 oops - platform IO can have stale target specific builds without this 2020-05-01 16:34:16 -07:00
geeksville
4f7a25f562 remove unneeded include 2020-05-01 16:33:40 -07:00
geeksville
4176d79ee9 fix warnings 2020-05-01 16:33:26 -07:00
Kevin Hester
fad496378c Merge pull request #118 from geeksville/master
misc fri workqueue
2020-05-01 12:37:28 -07:00
geeksville
e084699704 SNR is now a float, fix the screen display 2020-05-01 12:31:36 -07:00
geeksville
50213d8323 move packet handling into its own thread 2020-05-01 12:11:04 -07:00
geeksville
763276a2c8 Merge branch 'radiolib' 2020-05-01 09:04:19 -07:00
geeksville
71fcdba017 cleanup directory structure 2020-05-01 09:04:00 -07:00
Kevin Hester
08227e79d0 Merge pull request #117 from geeksville/radiolib
Change to use Radiolib as the basis for our networking, and add RX1262 support
2020-05-01 08:55:17 -07:00
geeksville
5a4fab2506 start msg sequence numbers with a random number each boot 2020-05-01 08:51:53 -07:00
geeksville
31eb2f5337 very important: don't allow immediate sends if we have pending ISRs 2020-05-01 08:32:16 -07:00
geeksville
82c1752d85 less logspam 2020-05-01 08:31:52 -07:00
geeksville
49a13bbfd3 increase gps config timeout, could take up to 2.5 secs 2020-05-01 08:31:31 -07:00
geeksville
0096f54ae9 better debug output 2020-04-30 22:53:21 -07:00
geeksville
5af122b39d update todo list 2020-04-30 21:49:55 -07:00
geeksville
4e106f4098 remove radiohead 2020-04-30 21:42:11 -07:00
geeksville
1f1d683f4f add back the old code that checked if the radio was actvively receiving 2020-04-30 21:29:51 -07:00
geeksville
a8f64c3cc8 make a custom version fo rf95 class, so we can can deal with
chips that have bad version codes.
2020-04-30 21:11:03 -07:00
geeksville
968a2d7fbc store SNR in received packets 2020-04-30 19:58:10 -07:00
geeksville
1fab9c5aac temp hack to get new rf95 driver working 2020-04-30 19:31:17 -07:00
geeksville
22bca31ce3 properly set the RF95 iface 2020-04-30 18:05:06 -07:00
geeksville
48c045a253 move SPI init into main 2020-04-30 17:56:30 -07:00
geeksville
e9ca7792eb new RF95 driver is written 2020-04-30 17:43:29 -07:00
geeksville
b1a55b4576 old RF95 API works again 2020-04-30 16:47:56 -07:00
geeksville
dd7452ad96 old RF95 code builds again 2020-04-30 16:36:59 -07:00
geeksville
d7d8188093 implement most of sleep handling for the new radio stack 2020-04-30 15:50:07 -07:00
geeksville
62a893c760 SX1262 approximately works top-to-bottom, but need to add sleep modes 2020-04-30 15:43:41 -07:00
geeksville
a2ba9d3c44 new receive code works a little better 2020-04-30 13:50:40 -07:00
geeksville
22720e9f63 ex1262 receiving kinda works 2020-04-30 12:37:58 -07:00
geeksville
11b79a942d add todos 2020-04-30 10:54:53 -07:00
geeksville
3c3e722181 new sending kinda works 2020-04-30 10:00:40 -07:00
geeksville
fce31560c6 the mountain of changes needed to kinda make tx work compiles. 2020-04-30 09:44:16 -07:00
geeksville
074ac33b8a make a gdb "restart" command that allows restarting without rebuilding 2020-04-29 20:23:59 -07:00
geeksville
2982e197e0 radio settings now work on real sx1262 hw 2020-04-29 19:04:59 -07:00
geeksville
f69ddf168b we now hopefully apply the same radio settings as we did for the RF95 2020-04-29 18:46:32 -07:00
geeksville
8d985cfd37 cleanup so eventually rf95 can share common msg code with sx1262 2020-04-29 16:28:11 -07:00
geeksville
4693302d82 crummy sx1262 fake init kinda works 2020-04-29 16:06:23 -07:00
geeksville
814c126e67 ugly WIP on switching to RadioLib, still need to set freq etc... 2020-04-29 14:54:03 -07:00
geeksville
a7d153abcb CUSTOM GPIOs the SX1262MB2CAS shield when installed on the NRF52840-DK development board 2020-04-29 12:57:34 -07:00
geeksville
1b265eb48d switch from sx126x-arduino to radiolab 2020-04-29 10:50:50 -07:00
geeksville
94e80d3b44 mention python API 2020-04-28 20:51:02 -07:00
geeksville
f1ec95f49b update project name 2020-04-28 20:47:20 -07:00
Kevin Hester
7ee2643e9a Merge pull request #114 from geeksville/master
Add python API to webpage
2020-04-28 17:48:00 -07:00
geeksville
2ab34357d5 emit FromRadio.rebooted to serial test harness can detect reboots 2020-04-28 17:43:16 -07:00
geeksville
b53392ab73 Merge remote-tracking branch 'root/master' 2020-04-28 17:07:42 -07:00
geeksville
803d2dfefb add note about python API 2020-04-28 17:06:00 -07:00
151 changed files with 6001 additions and 12157 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "proto"]
path = proto
url = https://github.com/meshtastic/Meshtastic-protobufs.git
[submodule "sdk-nrfxlib"]
path = sdk-nrfxlib
url = https://github.com/nrfconnect/sdk-nrfxlib.git

32
.vscode/launch.json vendored
View File

@@ -1,32 +0,0 @@
// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY
// PIO Unified Debugger
//
// Documentation: https://docs.platformio.org/page/plus/debugging.html
// Configuration: https://docs.platformio.org/page/projectconf/section_env_debug.html
{
"version": "0.2.0",
"configurations": [
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug",
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/tbeam/firmware.elf",
"toolchainBinDir": "/home/kevinh/.platformio/packages/toolchain-xtensa32/bin",
"preLaunchTask": {
"type": "PlatformIO",
"task": "Pre-Debug"
},
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (skip Pre-Debug)",
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/tbeam/firmware.elf",
"toolchainBinDir": "/home/kevinh/.platformio/packages/toolchain-xtensa32/bin",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

View File

@@ -50,8 +50,15 @@
"cassert": "cpp"
},
"cSpell.words": [
"Blox",
"HFSR",
"Meshtastic",
"NEMAGPS",
"Ublox",
"bkpt",
"cfsr",
"descs",
"ocrypto",
"protobufs"
]
}

View File

@@ -1,10 +1,10 @@
# Meshtastic-esp32
# Meshtastic-device
This is the device side code for the [meshtastic.org](https://www.meshtastic.org) project.
![Continuous Integration](https://github.com/meshtastic/Meshtastic-esp32/workflows/Continuous%20Integration/badge.svg)
Meshtastic is a project that lets you use
Meshtastic is a project that lets you use
inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding -
essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other
members and any text messages sent to your group chat.
@@ -14,15 +14,34 @@ will optionally work with your phone, but no phone is required.
Typical time between recharging the radios should be about eight days.
This project is currently early-alpha, but if you have questions please [join our discussion forum](https://meshtastic.discourse.group/).
This project is is currently in beta-testing - if you have questions please [join our discussion forum](https://meshtastic.discourse.group/).
This software is 100% open source and developed by a group of hobbyist experimenters. No warranty is provided, if you'd like to improve it - we'd love your help. Please post in the chat.
## Supported hardware
We currently support three models of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4000119152086.html), [TTGO LORA32](https://www.banggood.com/LILYGO-TTGO-LORA32-868Mhz-SX1276-ESP32-Oled-Display-bluetooth-WIFI-Lora-Development-Module-Board-p-1248652.html?cur_warehouse=UK) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most users should buy the T-Beam and an 18650 battery (total cost less than \$35). Make sure to buy the frequency range which is legal for your country (915MHz for US/JP/AU/NZ, 470MHz for CN, 433MHz and 870MHz for EU). Getting a version that includes a screen is optional, but highly recommended.
We currently support three models of radios.
See (meshtastic.org) for 3D printable cases.
- 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)
- 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
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
**Make sure to get the frequency for your country**
- US/JP/AU/NZ - 915MHz
- CN - 470MHz
- EU - 868MHz, 433MHz
Getting a version that includes a screen is optional, but highly recommended.
## Firmware Installation
@@ -40,7 +59,8 @@ Please post comments on our [group chat](https://meshtastic.discourse.group/) if
6. In ESPHome Flasher, refresh the serial ports and select your board.
7. Browse to the previously downloaded firmware and select the correct firmware based on the board type, country and frequency.
8. Select Flash ESP.
9. Once finished, the terminal should start displaying debug messages including the Bluetooth passphrase when you try connect from your phone (handy if you dont have a screen).
9. Once complete, “Done! Flashing is complete!” will be shown.
10. Debug messages sent from the Meshtastic device can be viewed with a terminal program such as [PuTTY](https://www.putty.org/) (Windows only). Within PuTTY, click “Serial”, enter the “Serial line” com port (can be found at step 4), enter “Speed” as 921600, then click “Open”.
### Installing from a commandline
@@ -70,9 +90,12 @@ Hard resetting via RTS pin...
```
5. cd into the directory where the release zip file was expanded.
6. Install the correct firmware for your board with "device-install.sh firmware-_board_-_country_.bin". For instance "./device-install.sh firmware-HELTEC-US-0.0.3.bin".
6. Install the correct firmware for your board with `device-install.sh firmware-_board_-_country_.bin`.
- Example: `./device-install.sh firmware-HELTEC-US-0.0.3.bin`.
7. To update run `device-update.sh firmware-_board_-_country_.bin`
- Example: `./device-update.sh firmware-HELTEC-US-0.0.3.bin`.
Note: If you have previously installed meshtastic, you don't need to run this full script instead just run "esptool.py --baud 921600 write*flash 0x10000 firmware-\_board*-_country_.bin". This will be faster, also all of your current preferences will be preserved.
Note: If you have previously installed meshtastic, you don't need to run this full script instead just run `esptool.py --baud 921600 write_flash 0x10000 firmware-_board_-_country_-_version_.bin`. This will be faster, also all of your current preferences will be preserved.
You should see something like this:
@@ -143,14 +166,13 @@ Hard resetting via RTS pin...
7. The board will boot and show the Meshtastic logo.
8. Please post a comment on our chat so we know if these instructions worked for you ;-). If you find bugs/have-questions post there also - we will be rapidly iterating over the next few weeks.
## Meshtastic Android app
# Meshtastic Android app
The source code for the (optional) Meshtastic Android app is [here](https://github.com/meshtastic/Meshtastic-Android).
The companion (optional) Meshtastic Android app is [here](https://github.com/meshtastic/Meshtastic-Android). You can also download it on Google Play.
Alpha test builds are current available by opting into our alpha test group. See (www.meshtastic.org) for instructions.
# Python API
After our rate of change slows a bit, we will make beta builds available here (without needing to join the alphatest group):
[![Download at https://play.google.com/store/apps/details?id=com.geeksville.mesh](https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png)](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dgithub%26utm_medium%3Desp32-readme%26utm_campaign%3Dmeshtastic-esp32%2520readme%26anid%3Dadmob&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1)
We offer a [python API](https://github.com/meshtastic/Meshtastic-python) that makes it easy to use these devices to provide mesh networking for your custom projects.
# Development

View File

@@ -39,6 +39,12 @@ function do_build {
cp $SRCELF $OUTDIR/elfs/firmware-$ENV_NAME-$COUNTRY-$VERSION.elf
}
# Make sure our submodules are current
git submodule update
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio lib update
for COUNTRY in $COUNTRIES; do
for BOARD in $BOARDS; do
do_build $BOARD
@@ -61,6 +67,6 @@ Generated by bin/buildall.sh -->
XML
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
echo BUILT ALL

8
bin/device-update.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
FILENAME=$1
echo "Trying to update $FILENAME"
esptool.py --baud 921600 write_flash 0x10000 $FILENAME

View File

@@ -1 +1,4 @@
JLinkRTTViewer
# JLinkRTTViewer
JLinkRTTClient

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

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

View File

@@ -1,3 +1,3 @@
export VERSION=0.6.0
export VERSION=0.7.5

View File

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

View File

@@ -1,7 +1,7 @@
# What is Meshtastic?
Meshtastic is a project that lets you use
inexpensive (\$30 ish) GPS radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat.
Meshtastic is a project that lets you use
inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat.
The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required.
@@ -13,6 +13,7 @@ Note: Questions after reading this? See our new [forum](https://meshtastic.disco
- Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...)
- Secure long-range communication within groups without depending on cellular providers
- Finding your lost kids ;-)
- Through our [python API](https://pypi.org/project/meshtastic/) use these inexpensive radios to easily add mesh networking to your own projects.
[![Youtube video demo](desk-video-screenshot.png)](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo")
@@ -23,14 +24,14 @@ Not all of these features are fully implemented yet - see **important** disclaim
- Very long battery life (should be about eight days with the beta software)
- Built in GPS and [LoRa](https://en.wikipedia.org/wiki/LoRa) radio, but we manage the radio automatically for you
- Long range - a few miles per node but each node will forward packets as needed
- Secure - channels are encrypted by AES256 (But see important disclaimers below wrt this feature)
- 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. So if you have some application that needs long range low power networking, this might work for you.
- Eventually (within a couple of months) we should have a modified version of Signal that works with this project.
- 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 early alpha - if you have questions please [join our discussion forum](https://meshtastic.discourse.group/).
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/).
This software is 100% open source and developed by a group of hobbyist experimenters. No warranty is provided, if you'd like to improve it - we'd love your help. Please post in the [forum](https://meshtastic.discourse.group/).
@@ -38,6 +39,8 @@ 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/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.
- 03/03/2020 - 0.0.9 of the Android app and device code is released. Still an alpha but fairly functional.
- 02/25/2020 - 0.0.4 of the Android app is released. This is a very early alpha, see below to join the alpha-testers group.

View File

@@ -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 ;-)
- Encryption is turned off for now
- The encryption [implementation](software/crypto.md) has not been reviewed by an expert. (Are you an expert? Please help us)
- 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) ;-)

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -1,207 +1,68 @@
# High priority
Items to complete soon (next couple of alpha releases).
- 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.
- remeasure wake time power draws now that we run CPU down at 80MHz
# AXP192 tasks
- 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
- why is the net so chatty now?
- do a release
- device wakes, turns BLE on and phone doesn't notice (while phone was sitting in auto-connect)
- E22 bringup
- encryption review findings writeup
- turn on modem-sleep mode - https://github.com/espressif/arduino-esp32/issues/1142#issuecomment-512428852
# Medium priority
Items to complete before the first beta release.
- 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
- turn on watchdog timer (because lib code seems buggy)
- show battery level as % full
- 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
- add basic crypto - https://github.com/chegewara/esp32-mbedtls-aes-test/blob/master/main/main.c https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation - use ECB at first (though it is shit) because it doesn't require us to send 16 bytes of IV with each packet. Then OFB per example. Possibly do this crypto at the data payload level only, so that all of the packet routing metadata
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
# Pre-beta priority
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 ;-) )
During the beta timeframe the following improvements 'would be nice'
- 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
- 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.
- 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
# 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
- re-enable the bluetooth battery level service on the T-BEAM
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
- 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
Items after the first final candidate release.
- use variable length arduino Strings in protobufs (instead of current fixed buffers)
- 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.
- 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 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
- 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
- use https://platformio.org/lib/show/1260/OneButton if necessary
- don't send location packets if we haven't moved
- don't send location packets if we haven't moved significantly
- 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)
- 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
- 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
# Spinoff project ideas
- 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
- an open source version of https://www.burnair.ch/skynet/
- a paragliding app like http://airwhere.co.uk/
- How do avalanche beacons work? Could this do that as well? possibly by using beacon mode feature of the RF95?

View File

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

View File

@@ -6,10 +6,11 @@ in these instructions I describe use of their command line tool.
1. Purchase a suitable radio (see above)
2. Install [PlatformIO](https://platformio.org/platformio-ide)
3. Download this git repo and cd into it
4. 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. Pull-requests eagerly accepted for other countries.
5. Plug the radio into your USB port
6. 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).
7. 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.
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.
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.
## Decoding stack traces

37
docs/software/crypto.md Normal file
View File

@@ -0,0 +1,37 @@
# Encryption in Meshtastic
Cryptography is tricky, so we've tried to 'simply' apply standard crypto solutions to our implementation. However,
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.
## Notes on implementation
- 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).
```
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.
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.
Note that for both stategies, sizes are measured in blocks and that an AES block is 16 bytes.
```
## Remaining todo
- Have the app change the crypto key when the user generates a new channel

View File

@@ -1,19 +1,92 @@
# Mesh broadcast algorithm
FIXME - instead look for standard solutions. this approach seems really suboptimal, because too many nodes will try to rebroast. If
all else fails could always use the stock Radiohead solution - though super inefficient.
great source of papers and class notes: http://www.cs.jhu.edu/~cs647/
flood routing improvements
- DONE if we don't see anyone rebroadcast our want_ack=true broadcasts, retry as needed.
reliable messaging tasks (stage one for DSR):
- DONE generalize naive flooding
- DONE add a max hops parameter, use it for broadcast as well (0 means adjacent only, 1 is one forward etc...). Store as three bits in the header.
- DONE add a 'snoopReceived' hook for all messages that pass through our node.
- DONE use the same 'recentmessages' array used for broadcast msgs to detect duplicate retransmitted messages.
- DONE in the router receive path?, send an ack packet if want_ack was set and we are the final destination. FIXME, for now don't handle multihop or merging of data replies with these acks.
- DONE keep a list of packets waiting for acks
- DONE for each message keep a count of # retries (max of three). Local to the node, only for the most immediate hop, ignorant of multihop routing.
- DONE delay some random time for each retry (large enough to allow for acks to come in)
- 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
dsr tasks
- 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
optimizations / low priority:
- 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
- change nodenums and packetids in protobuf to be fixed32
- low priority: think more careful about reliable retransmit intervals
- make ReliableRouter.pending threadsafe
- bump up PacketPool size for all the new ack/nak/routing packets
- handle 51 day rollover in doRetransmissions
- use a priority queue for the messages waiting to send. Send acks first, then routing messages, then data messages, then broadcasts?
when we send a packet
- do "hop by hop" routing
- when sending, if destnodeinfo.next_hop is zero (and no message is already waiting for an arp for that node), startRouteDiscovery() for that node. Queue the message in the 'waiting for arp queue' so we can send it later when then the arp completes.
- otherwise, use next_hop and start sending a message (with ack request) towards that node (starting with next_hop).
when we receive any packet
- sniff and update tables (especially useful to find adjacent nodes). Update user, network and position info.
- if we need to route() that packet, resend it to the next_hop based on our nodedb.
- if it is broadcast or destined for our node, deliver locally
- handle routereply/routeerror/routediscovery messages as described below
- then free it
routeDiscovery
- if we've already passed through us (or is from us), then it ignore it
- use the nodes already mentioned in the request to update our routing table
- if they were looking for us, send back a routereply
- NOT DOING FOR NOW -if max_hops is zero and they weren't looking for us, drop (FIXME, send back error - I think not though?)
- if we receive a discovery packet, and we don't have next_hop set in our nodedb, we use it to populate next_hop (if needed) towards the requester (after decrementing max_hops)
- if we receive a discovery packet, and we have a next_hop in our nodedb for that destination we send a (reliable) we send a route reply towards the requester
when sending any reliable packet
- if timeout doing retries, send a routeError (nak) message back towards the original requester. all nodes eavesdrop on that packet and update their route caches.
when we receive a routereply packet
- update next_hop on the node, if the new reply needs fewer hops than the existing one (we prefer shorter paths). fixme, someday use a better heuristic
when we receive a routeError packet
- delete the route for that failed recipient, restartRouteDiscovery()
- if we receive routeerror in response to a discovery,
- fixme, eventually keep caches of possible other routes.
TODO:
- DONE reread the radiohead mesh implementation - hop to hop acknoledgement seems VERY expensive but otherwise it seems like DSR
- optimize our generalized flooding with heuristics, possibly have particular nodes self mark as 'router' nodes.
- DONE reread the radiohead mesh implementation - hop to hop acknowledgement seems VERY expensive but otherwise it seems like DSR
- DONE read about mesh routing solutions (DSR and AODV)
- DONE read about general mesh flooding solutions (naive, MPR, geo assisted)
- DONE reread the disaster radio protocol docs - seems based on Babel (which is AODVish)
- possibly dash7? https://www.slideshare.net/MaartenWeyn1/dash7-alliance-protocol-technical-presentation https://github.com/MOSAIC-LoPoW/dash7-ap-open-source-stack - does the opensource stack implement multihop routing? flooding? their discussion mailing list looks dead-dead
- REJECTED - seems dying - possibly dash7? https://www.slideshare.net/MaartenWeyn1/dash7-alliance-protocol-technical-presentation https://github.com/MOSAIC-LoPoW/dash7-ap-open-source-stack - does the opensource stack implement multihop routing? flooding? their discussion mailing list looks dead-dead
- update duty cycle spreadsheet for our typical usecase
- generalize naive flooding on top of radiohead or disaster.radio? (and fix radiohead to use my new driver)
a description of DSR: https://tools.ietf.org/html/rfc4728 good slides here: https://www.slideshare.net/ashrafmath/dynamic-source-routing
good description of batman protocol: https://www.open-mesh.org/projects/open-mesh/wiki/BATMANConcept
@@ -77,7 +150,6 @@ look into the literature for this idea specifically.
FIXME, merge into the above:
good description of batman protocol: https://www.open-mesh.org/projects/open-mesh/wiki/BATMANConcept
interesting paper on lora mesh: https://portal.research.lu.se/portal/files/45735775/paper.pdf

View File

@@ -1,51 +1,50 @@
# NRF52 TODO
## Misc work items
## Initial work items
Minimum items needed to make sure hardware is good.
- DONE select and install a bootloader (adafruit)
- DONE get old radio driver working on NRF52
- DONE basic test of BLE
- DONE get a debug 'serial' console working via the ICE passthrough feature
- switch to RadioLab? test it with current radio. https://github.com/jgromes/RadioLib
- use "variants" to get all gpio bindings
- plug in correct variants for the real board
- 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)
- test my hackedup bootloader on the real hardware
- Use the PMU driver on real hardware
- add a NEMA based GPS driver to test GPS
- Use new radio driver on real hardware - possibly start with https://os.mbed.com/teams/Semtech/code/SX126xLib/
- Use UC1701 LCD driver on real hardware. Still need to create at startup and probe on SPI
- Use new radio driver on real hardware
- Use UC1701 LCD driver on real hardware. Still need to create at startup and probe on SPI. Make sure SPI is atomic.
- set vbus voltage per https://infocenter.nordicsemi.com/topic/ps_nrf52840/power.html?cp=4_0_0_4_2
- test the LEDs
- test the buttons
- make a new boarddef with a variant.h file. Fix pins in that file. In particular (at least):
#define PIN_SPI_MISO (46)
#define PIN_SPI_MOSI (45)
#define PIN_SPI_SCK (47)
#define PIN_WIRE_SDA (26)
#define PIN_WIRE_SCL (27)
## Secondary work items
Needed to be fully functional at least at the same level of the ESP32 boards. At this point users would probably want them.
- enable BLE DFU somehow
- set appversion/hwversion
- report appversion/hwversion in BLE
- use new LCD driver from screen.cpp. Still need to hook it to a subclass of (poorly named) OLEDDisplay, and override display() to stream bytes out to the screen.
- DONE get serial API working
- get full BLE api working
- we need to enable the external xtal for the sx1262 (on dio3)
- figure out which regulator mode the sx1262 is operating in
- turn on security for BLE, make pairing work
- make power management/sleep work properly
- make a settimeofday implementation
- make a file system implementation (preferably one that can see the files the bootloader also sees) - use https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.3.0/lib_fds_usage.html?cp=7_5_0_3_55_3
- DONE increase preamble length? - will break other clients? so all devices must update
- DONE enable BLE DFU somehow
- report appversion/hwversion in BLE
- use new LCD driver from screen.cpp. Still need to hook it to a subclass of (poorly named) OLEDDisplay, and override display() to stream bytes out to the screen.
- we need to enable the external tcxo for the sx1262 (on dio3)?
- figure out which regulator mode the sx1262 is operating in
- turn on security for BLE, make pairing work
- make ble endpoints not require "start config", just have them start in config mode
- measure power management and confirm battery life
- use new PMU to provide battery voltage/% full to app (both bluetooth and screen)
- do initial power measurements
- do initial power measurements, measure effects of more preamble bits, measure power management and confirm battery life
- set UICR.CUSTOMER to indicate board model & version
## Items to be 'feature complete'
- check datasheet about sx1262 temperature compensation
- enable brownout detection and watchdog
- stop polling for GPS characters, instead stay blocked on read in a thread
- figure out what the correct current limit should be for the sx1262, currently we just use the default 100
- put sx1262 in sleepmode when processor gets shutdown (or rebooted), ideally even for critical faults (to keep power draw low). repurpose deepsleep state for this.
- good power management tips: https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/optimizing-power-on-nrf52-designs
- call PMU set_ADC_CONV(0) during sleep, to stop reading PMU adcs and decrease current draw
- do final power measurements
@@ -53,12 +52,21 @@ Needed to be fully functional at least at the same level of the ESP32 boards. At
- use the new buttons in the UX
- currently using soft device SD140, is that ideal?
- turn on the watchdog timer, require servicing from key application threads
- install a hardfault handler for null ptrs (if one isn't already installed)
- nrf52setup should call randomSeed(tbd)
- implement SYSTEMOFF behavior per https://infocenter.nordicsemi.com/topic/ps_nrf52840/power.html?cp=4_0_0_4_2
## Things to do 'someday'
Nice ideas worth considering someday...
- 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.
- Hook Segger RTT to the nordic logging framework. https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/debugging-with-real-time-terminal
- Use nordic logging for DEBUG_MSG
- use the Jumper simulator to run meshes of simulated hardware: https://docs.jumper.io/docs/install.html
- make/find a multithread safe debug logging class (include remote logging and timestamps and levels). make each log event atomic.
- turn on freertos stack size checking
- Currently we use Nordic's vendor ID, which is apparently okay: https://devzone.nordicsemi.com/f/nordic-q-a/44014/using-nordic-vid-and-pid-for-nrf52840 and I just picked a PID of 0x4403
- Use NRF logger module (includes flash logging etc...) instead of DEBUG_MSG
- Use "LED softblink" library on NRF52 to do nice pretty "breathing" LEDs. Don't whack LED from main thread anymore.
@@ -66,11 +74,15 @@ Nice ideas worth considering someday...
- in addition to the main CPU watchdog, use the PMU watchdog as a really big emergency hammer
- turn on 'shipping mode' in the PMU when device is 'off' - to cut battery draw to essentially zero
- make Lorro_BQ25703A read/write operations atomic, current version could let other threads sneak in (once we start using threads)
- turn on DFU assistance in the appload using the nordic DFU helper lib call
- make the segger logbuffer larger, move it to RAM that is preserved across reboots and support reading it out at runtime (to allow full log messages to be included in crash reports). Share this code with ESP32 (use gcc noinit attribute)
- convert hardfaults/panics/asserts/wd exceptions into fault codes sent to phone
- stop enumerating all i2c devices at boot, it wastes power & time
- consider using "SYSTEMOFF" deep sleep mode, without RAM retension. Only useful for 'truly off - wake only by button press' only saves 1.5uA vs SYSTEMON. (SYSTEMON only costs 1.5uA). Possibly put PMU into shipping mode?
- change the BLE protocol to be more symmetric. Have the phone _also_ host a GATT service which receives writes to
'fromradio'. This would allow removing the 'fromnum' mailbox/notify scheme of the current approach and decrease the number of packet handoffs when a packet is received.
- Using the preceeding, make a generalized 'nrf52/esp32 ble to internet' bridge service. To let nrf52 apps do MQTT/UDP/HTTP POST/HTTP GET operations to web services.
- lower advertise interval to save power, lower ble transmit power to save power
- the SX126x class does SPI transfers on a byte by byte basis, which is very ineffecient. Much better to do block writes/reads.
## Old unorganized notes
@@ -81,10 +93,39 @@ Nice ideas worth considering someday...
- Currently using Nordic PCA10059 Dongle hardware
- https://community.platformio.org/t/same-bootloader-same-softdevice-different-board-different-pins/11411/9
- To make Segger JLink more reliable, turn off its fake filesystem. "JLinkExe MSDDisable" per https://learn.adafruit.com/circuitpython-on-the-nrf52/nrf52840-bootloader
## Done
- DONE add "DFU trigger library" to application load
- DONE: using this: Possibly use this bootloader? https://github.com/adafruit/Adafruit_nRF52_Bootloader
- DONE select and install a bootloader (adafruit)
- DONE get old radio driver working on NRF52
- DONE basic test of BLE
- DONE get a debug 'serial' console working via the ICE passthrough feature
- DONE switch to RadioLab? test it with current radio. https://github.com/jgromes/RadioLib
- DONE change rx95 to radiolib
- DONE track rxbad, rxgood, txgood
- DONE neg 7 error code from receive
- DONE remove unused sx1262 lib from github
- at boot we are starting our message IDs at 1, rather we should start them at a random number. also, seed random based on timer. this could be the cause of our first message not seen bug.
- add a NEMA based GPS driver to test GPS
- DONE use "variants" to get all gpio bindings
- DONE plug in correct variants for the real board
- turn on DFU assistance in the appload using the nordic DFU helper lib call
- make a new boarddef with a variant.h file. Fix pins in that file. In particular (at least):
#define PIN_SPI_MISO (46)
#define PIN_SPI_MOSI (45)
#define PIN_SPI_SCK (47)
#define PIN_WIRE_SDA (26)
#define PIN_WIRE_SCL (27)
- customize the bootloader to use proper button bindings
- remove the MeshRadio wrapper - we don't need it anymore, just do everything in RadioInterface subclasses.
- DONE use SX126x::startReceiveDutyCycleAuto to save power by sleeping and briefly waking to check for preamble bits. Change xmit rules to have more preamble bits.
- scheduleOSCallback doesn't work yet - it is way too fast (causes rapid polling of busyTx, high power draw etc...)
- find out why we reboot while debugging - it was bluetooth/softdevice
- make a file system implementation (preferably one that can see the files the bootloader also sees) - preferably https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/InternalFileSytem/examples/Internal_ReadWrite/Internal_ReadWrite.ino else use https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.3.0/lib_fds_usage.html?cp=7_5_0_3_55_3
- change packet numbers to be 32 bits
```

View File

@@ -36,6 +36,10 @@ From lower to higher power consumption.
onEntry: setBluetoothOn(true), screen.setOn(true)
onExit: screen.setOn(false)
- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off
onEntry: setBluetooth off, screen on
onExit:
## Behavior
### events that increase CPU activity
@@ -51,9 +55,11 @@ From lower to higher power consumption.
- While in DARK/ON: If we receive EVENT_BLUETOOTH_PAIR we transition to ON and start our screen_on_secs timeout
- While in NB/DARK/ON: If we receive EVENT_NODEDB_UPDATED we transition to ON (so the new screen can be shown)
- While in DARK: While the phone talks to us over BLE (EVENT_CONTACT_FROM_PHONE) reset any sleep timers and stay in DARK (needed for bluetooth sw update and nice user experience if the user is reading/replying to texts)
- while in LS/NB/DARK: if SERIAL_CONNECTED, go to serial
### events that decrease cpu activity
- While in SERIAL: if SERIAL_DISCONNECTED, go to NB
- While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess
- While in ON: If it has been more than screen_on_secs since a press, lower to DARK
- While in DARK: If time since last contact by our phone exceeds phone_timeout_secs (15 minutes), we transition down into NB mode

19
gdbinit Normal file
View File

@@ -0,0 +1,19 @@
# Setup Monitor Mode Debugging
# Per .platformio/packages/framework-arduinoadafruitnrf52-old/cores/nRF5/linker/nrf52840_s140_v6.ld
# our appload starts at 0x26000
# Disable for now because our version on board doesn't support monitor mode debugging
# mon exec SetMonModeDebug=1
# mon exec SetMonModeVTableAddr=0x26000
# echo setting RTTAddr
# eval "monitor exec SetRTTAddr %p", &_SEGGER_RTT
# the jlink debugger seems to want a pause after reset before we tell it to start running
define restart
echo Restarting
monitor reset
shell sleep 1
cont
end

View File

@@ -1,33 +0,0 @@
#pragma once
#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this
#include "BLECharacteristic.h"
/**
* This mixin just lets the power management state machine know the phone is still talking to us
*/
class BLEKeepAliveCallbacks : public BLECharacteristicCallbacks
{
public:
void onRead(BLECharacteristic *c)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
}
void onWrite(BLECharacteristic *c)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
}
};
/**
* A characterstic with a set of overridable callbacks
*/
class CallbackCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks
{
public:
CallbackCharacteristic(const char *uuid, uint32_t btprops)
: BLECharacteristic(uuid, btprops)
{
setCallbacks(this);
}
};

View File

@@ -1,34 +0,0 @@
Important - Read carefully:
SEGGER RTT - Real Time Transfer for embedded targets
All rights reserved.
SEGGER strongly recommends to not make any changes
to or modify the source code of this software in order to stay
compatible with the RTT protocol and J-Link.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
condition is met:
o Redistributions of source code must retain the above copyright
notice, this condition and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
(c) 2014 - 2016 SEGGER Microcontroller GmbH
www.segger.com

View File

@@ -1,20 +0,0 @@
README.txt for the SEGGER RTT Implementation Pack.
Included files:
===============
Root Directory
- Examples
- Main_RTT_InputEchoApp.c - Sample application which echoes input on Channel 0.
- Main_RTT_MenuApp.c - Sample application to demonstrate RTT bi-directional functionality.
- Main_RTT_PrintfTest.c - Sample application to test RTT small printf implementation.
- Main_RTT_SpeedTestApp.c - Sample application for measuring RTT performance. embOS needed.
- RTT
- SEGGER_RTT.c - The RTT implementation.
- SEGGER_RTT.h - Header for RTT implementation.
- SEGGER_RTT_Conf.h - Pre-processor configuration for the RTT implementation.
- SEGGER_RTT_Printf.c - Simple implementation of printf to write formatted strings via RTT.
- Syscalls
- RTT_Syscalls_GCC.c - Low-level syscalls to retarget printf() to RTT with GCC / Newlib.
- RTT_Syscalls_IAR.c - Low-level syscalls to retarget printf() to RTT with IAR compiler.
- RTT_Syscalls_KEIL.c - Low-level syscalls to retarget printf() to RTT with KEIL/uVision compiler.
- RTT_Syscalls_SES.c - Low-level syscalls to retarget printf() to RTT with SEGGER Embedded Studio.

View File

@@ -1,121 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT_Syscalls_GCC.c
Purpose : Low-level functions for using printf() via RTT in GCC.
To use RTT for printf output, include this file in your
application.
Revision: $Rev: 17697 $
----------------------------------------------------------------------
*/
#if (defined __GNUC__) && !(defined __SES_ARM) && !(defined __CROSSWORKS_ARM)
#include "SEGGER_RTT.h"
#include <reent.h> // required for _write_r
/*********************************************************************
*
* Types
*
**********************************************************************
*/
//
// If necessary define the _reent struct
// to match the one passed by the used standard library.
//
struct _reent;
/*********************************************************************
*
* Function prototypes
*
**********************************************************************
*/
int _write(int file, char *ptr, int len);
// _ssize_t _write_r(struct _reent *r, int file, const void *ptr, int len);
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* _write()
*
* Function description
* Low-level write function.
* libc subroutines will use this system routine for output to all files,
* including stdout.
* Write data via RTT.
*/
int _write(int file, char *ptr, int len)
{
(void)file; /* Not used, avoid warning */
SEGGER_RTT_Write(0, ptr, len);
return len;
}
/*********************************************************************
*
* _write_r()
*
* Function description
* Low-level reentrant write function.
* libc subroutines will use this system routine for output to all files,
* including stdout.
* Write data via RTT.
*/
_ssize_t _write_r(struct _reent *r, int file, const void *ptr, int len)
{
(void)file; /* Not used, avoid warning */
(void)r; /* Not used, avoid warning */
SEGGER_RTT_Write(0, ptr, len);
return len;
}
#endif
/****** End Of File *************************************************/

View File

@@ -1,115 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT_Syscalls_IAR.c
Purpose : Low-level functions for using printf() via RTT in IAR.
To use RTT for printf output, include this file in your
application and set the Library Configuration to Normal.
Revision: $Rev: 17697 $
----------------------------------------------------------------------
*/
#ifdef __IAR_SYSTEMS_ICC__
//
// Since IAR EWARM V8 and EWRX V4, yfuns.h is considered as deprecated and LowLevelIOInterface.h
// shall be used instead. To not break any compatibility with older compiler versions, we have a
// version check in here.
//
#if ((defined __ICCARM__) && (__VER__ >= 8000000)) || ((defined __ICCRX__) && (__VER__ >= 400))
#include <LowLevelIOInterface.h>
#else
#include <yfuns.h>
#endif
#include "SEGGER_RTT.h"
#pragma module_name = "?__write"
/*********************************************************************
*
* Function prototypes
*
**********************************************************************
*/
size_t __write(int handle, const unsigned char * buffer, size_t size);
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* __write()
*
* Function description
* Low-level write function.
* Standard library subroutines will use this system routine
* for output to all files, including stdout.
* Write data via RTT.
*/
size_t __write(int handle, const unsigned char * buffer, size_t size) {
(void) handle; /* Not used, avoid warning */
SEGGER_RTT_Write(0, (const char*)buffer, size);
return size;
}
/*********************************************************************
*
* __write_buffered()
*
* Function description
* Low-level write function.
* Standard library subroutines will use this system routine
* for output to all files, including stdout.
* Write data via RTT.
*/
size_t __write_buffered(int handle, const unsigned char * buffer, size_t size) {
(void) handle; /* Not used, avoid warning */
SEGGER_RTT_Write(0, (const char*)buffer, size);
return size;
}
#endif
/****** End Of File *************************************************/

View File

@@ -1,386 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : RTT_Syscalls_KEIL.c
Purpose : Retargeting module for KEIL MDK-CM3.
Low-level functions for using printf() via RTT
Revision: $Rev: 17697 $
----------------------------------------------------------------------
*/
#ifdef __CC_ARM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rt_sys.h>
#include <rt_misc.h>
#include "SEGGER_RTT.h"
/*********************************************************************
*
* #pragmas
*
**********************************************************************
*/
#pragma import(__use_no_semihosting)
#ifdef _MICROLIB
#pragma import(__use_full_stdio)
#endif
/*********************************************************************
*
* Defines non-configurable
*
**********************************************************************
*/
/* Standard IO device handles - arbitrary, but any real file system handles must be
less than 0x8000. */
#define STDIN 0x8001 // Standard Input Stream
#define STDOUT 0x8002 // Standard Output Stream
#define STDERR 0x8003 // Standard Error Stream
/*********************************************************************
*
* Public const
*
**********************************************************************
*/
#if __ARMCC_VERSION < 5000000
//const char __stdin_name[] = "STDIN";
const char __stdout_name[] = "STDOUT";
const char __stderr_name[] = "STDERR";
#endif
/*********************************************************************
*
* Public code
*
**********************************************************************
*/
/*********************************************************************
*
* _ttywrch
*
* Function description:
* Outputs a character to the console
*
* Parameters:
* c - character to output
*
*/
void _ttywrch(int c) {
fputc(c, stdout); // stdout
fflush(stdout);
}
/*********************************************************************
*
* _sys_open
*
* Function description:
* Opens the device/file in order to do read/write operations
*
* Parameters:
* sName - sName of the device/file to open
* OpenMode - This parameter is currently ignored
*
* Return value:
* != 0 - Handle to the object to open, otherwise
* == 0 -"device" is not handled by this module
*
*/
FILEHANDLE _sys_open(const char * sName, int OpenMode) {
(void)OpenMode;
// Register standard Input Output devices.
if (strcmp(sName, __stdout_name) == 0) {
return (STDOUT);
} else if (strcmp(sName, __stderr_name) == 0) {
return (STDERR);
} else
return (0); // Not implemented
}
/*********************************************************************
*
* _sys_close
*
* Function description:
* Closes the handle to the open device/file
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
*
* Return value:
* 0 - device/file closed
*
*/
int _sys_close(FILEHANDLE hFile) {
(void)hFile;
return 0; // Not implemented
}
/*********************************************************************
*
* _sys_write
*
* Function description:
* Writes the data to an open handle.
* Currently this function only outputs data to the console
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
* pBuffer - Pointer to the data that shall be written
* NumBytes - Number of bytes to write
* Mode - The Mode that shall be used
*
* Return value:
* Number of bytes *not* written to the file/device
*
*/
int _sys_write(FILEHANDLE hFile, const unsigned char * pBuffer, unsigned NumBytes, int Mode) {
int r = 0;
(void)Mode;
if (hFile == STDOUT) {
SEGGER_RTT_Write(0, (const char*)pBuffer, NumBytes);
return 0;
}
return r;
}
/*********************************************************************
*
* _sys_read
*
* Function description:
* Reads data from an open handle.
* Currently this modules does nothing.
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
* pBuffer - Pointer to buffer to store the read data
* NumBytes - Number of bytes to read
* Mode - The Mode that shall be used
*
* Return value:
* Number of bytes read from the file/device
*
*/
int _sys_read(FILEHANDLE hFile, unsigned char * pBuffer, unsigned NumBytes, int Mode) {
(void)hFile;
(void)pBuffer;
(void)NumBytes;
(void)Mode;
return (0); // Not implemented
}
/*********************************************************************
*
* _sys_istty
*
* Function description:
* This function shall return whether the opened file
* is a console device or not.
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
*
* Return value:
* 1 - Device is a console
* 0 - Device is not a console
*
*/
int _sys_istty(FILEHANDLE hFile) {
if (hFile > 0x8000) {
return (1);
}
return (0); // Not implemented
}
/*********************************************************************
*
* _sys_seek
*
* Function description:
* Seeks via the file to a specific position
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
* Pos -
*
* Return value:
* int -
*
*/
int _sys_seek(FILEHANDLE hFile, long Pos) {
(void)hFile;
(void)Pos;
return (0); // Not implemented
}
/*********************************************************************
*
* _sys_ensure
*
* Function description:
*
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
*
* Return value:
* int -
*
*/
int _sys_ensure(FILEHANDLE hFile) {
(void)hFile;
return (-1); // Not implemented
}
/*********************************************************************
*
* _sys_flen
*
* Function description:
* Returns the length of the opened file handle
*
* Parameters:
* hFile - Handle to a file opened via _sys_open
*
* Return value:
* Length of the file
*
*/
long _sys_flen(FILEHANDLE hFile) {
(void)hFile;
return (0); // Not implemented
}
/*********************************************************************
*
* _sys_tmpnam
*
* Function description:
* This function converts the file number fileno for a temporary
* file to a unique filename, for example, tmp0001.
*
* Parameters:
* pBuffer - Pointer to a buffer to store the name
* FileNum - file number to convert
* MaxLen - Size of the buffer
*
* Return value:
* 1 - Error
* 0 - Success
*
*/
int _sys_tmpnam(char * pBuffer, int FileNum, unsigned MaxLen) {
(void)pBuffer;
(void)FileNum;
(void)MaxLen;
return (1); // Not implemented
}
/*********************************************************************
*
* _sys_command_string
*
* Function description:
* This function shall execute a system command.
*
* Parameters:
* cmd - Pointer to the command string
* len - Length of the string
*
* Return value:
* == NULL - Command was not successfully executed
* == sCmd - Command was passed successfully
*
*/
char * _sys_command_string(char * cmd, int len) {
(void)len;
return cmd; // Not implemented
}
/*********************************************************************
*
* _sys_exit
*
* Function description:
* This function is called when the application returns from main
*
* Parameters:
* ReturnCode - Return code from the main function
*
*
*/
void _sys_exit(int ReturnCode) {
(void)ReturnCode;
while (1); // Not implemented
}
#if __ARMCC_VERSION >= 5000000
/*********************************************************************
*
* stdout_putchar
*
* Function description:
* Put a character to the stdout
*
* Parameters:
* ch - Character to output
*
*
*/
int stdout_putchar(int ch) {
(void)ch;
return ch; // Not implemented
}
#endif
#endif
/*************************** End of file ****************************/

View File

@@ -1,247 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT_Syscalls_SES.c
Purpose : Reimplementation of printf, puts and __getchar using RTT
in SEGGER Embedded Studio.
To use RTT for printf output, include this file in your
application.
Revision: $Rev: 18539 $
----------------------------------------------------------------------
*/
#if (defined __SES_ARM) || (defined __SES_RISCV) || (defined __CROSSWORKS_ARM)
#include "SEGGER_RTT.h"
#include <stdarg.h>
#include <stdio.h>
#include "limits.h"
#include "__libc.h"
#include "__vfprintf.h"
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
//
// Select string formatting implementation.
//
// RTT printf formatting
// - Configurable stack usage. (SEGGER_RTT_PRINTF_BUFFER_SIZE in SEGGER_RTT_Conf.h)
// - No maximum string length.
// - Limited conversion specifiers and flags. (See SEGGER_RTT_printf.c)
// Standard library printf formatting
// - Configurable formatting capabilities.
// - Full conversion specifier and flag support.
// - Maximum string length has to be known or (slightly) slower character-wise output.
//
// #define PRINTF_USE_SEGGER_RTT_FORMATTING 0 // Use standard library formatting
// #define PRINTF_USE_SEGGER_RTT_FORMATTING 1 // Use RTT formatting
//
#ifndef PRINTF_USE_SEGGER_RTT_FORMATTING
#define PRINTF_USE_SEGGER_RTT_FORMATTING 0
#endif
//
// If using standard library formatting,
// select maximum output string buffer size or character-wise output.
//
// #define PRINTF_BUFFER_SIZE 0 // Use character-wise output
// #define PRINTF_BUFFER_SIZE 128 // Default maximum string length
//
#ifndef PRINTF_BUFFER_SIZE
#define PRINTF_BUFFER_SIZE 128
#endif
#if PRINTF_USE_SEGGER_RTT_FORMATTING // Use SEGGER RTT formatting implementation
/*********************************************************************
*
* Function prototypes
*
**********************************************************************
*/
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
/*********************************************************************
*
* Global functions, printf
*
**********************************************************************
*/
/*********************************************************************
*
* printf()
*
* Function description
* print a formatted string using RTT and SEGGER RTT formatting.
*/
int printf(const char *fmt,...) {
int n;
va_list args;
va_start (args, fmt);
n = SEGGER_RTT_vprintf(0, fmt, &args);
va_end(args);
return n;
}
#elif PRINTF_BUFFER_SIZE == 0 // Use standard library formatting with character-wise output
/*********************************************************************
*
* Static functions
*
**********************************************************************
*/
static int _putchar(int x, __printf_tag_ptr ctx) {
(void)ctx;
SEGGER_RTT_Write(0, (char *)&x, 1);
return x;
}
/*********************************************************************
*
* Global functions, printf
*
**********************************************************************
*/
/*********************************************************************
*
* printf()
*
* Function description
* print a formatted string character-wise, using RTT and standard
* library formatting.
*/
int printf(const char *fmt, ...) {
int n;
va_list args;
__printf_t iod;
va_start(args, fmt);
iod.string = 0;
iod.maxchars = INT_MAX;
iod.output_fn = _putchar;
SEGGER_RTT_LOCK();
n = __vfprintf(&iod, fmt, args);
SEGGER_RTT_UNLOCK();
va_end(args);
return n;
}
#else // Use standard library formatting with static buffer
/*********************************************************************
*
* Global functions, printf
*
**********************************************************************
*/
/*********************************************************************
*
* printf()
*
* Function description
* print a formatted string using RTT and standard library formatting.
*/
int printf(const char *fmt,...) {
int n;
char aBuffer[PRINTF_BUFFER_SIZE];
va_list args;
va_start (args, fmt);
n = vsnprintf(aBuffer, sizeof(aBuffer), fmt, args);
if (n > (int)sizeof(aBuffer)) {
SEGGER_RTT_Write(0, aBuffer, sizeof(aBuffer));
} else if (n > 0) {
SEGGER_RTT_Write(0, aBuffer, n);
}
va_end(args);
return n;
}
#endif
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* puts()
*
* Function description
* print a string using RTT.
*/
int puts(const char *s) {
return SEGGER_RTT_WriteString(0, s);
}
/*********************************************************************
*
* __putchar()
*
* Function description
* Write one character via RTT.
*/
int __putchar(int x, __printf_tag_ptr ctx) {
(void)ctx;
SEGGER_RTT_Write(0, (char *)&x, 1);
return x;
}
/*********************************************************************
*
* __getchar()
*
* Function description
* Wait for and get a character via RTT.
*/
int __getchar() {
return SEGGER_RTT_WaitKey();
}
#endif
/****** End Of File *************************************************/

View File

@@ -1,43 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
--------- END-OF-HEADER --------------------------------------------
File : Main_RTT_MenuApp.c
Purpose : Sample application to demonstrate RTT bi-directional functionality
*/
#define MAIN_C
#include <stdio.h>
#include "SEGGER_RTT.h"
volatile int _Cnt;
volatile int _Delay;
static char r;
/*********************************************************************
*
* main
*/
void main(void) {
SEGGER_RTT_WriteString(0, "SEGGER Real-Time-Terminal Sample\r\n");
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
do {
r = SEGGER_RTT_WaitKey();
SEGGER_RTT_Write(0, &r, 1);
r++;
} while (1);
}
/*************************** End of file ****************************/

View File

@@ -1,70 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
--------- END-OF-HEADER --------------------------------------------
File : Main_RTT_MenuApp.c
Purpose : Sample application to demonstrate RTT bi-directional functionality
*/
#define MAIN_C
#include <stdio.h>
#include "SEGGER_RTT.h"
volatile int _Cnt;
volatile int _Delay;
/*********************************************************************
*
* main
*/
void main(void) {
int r;
int CancelOp;
do {
_Cnt = 0;
SEGGER_RTT_WriteString(0, "SEGGER Real-Time-Terminal Sample\r\n");
SEGGER_RTT_WriteString(0, "Press <1> to continue in blocking mode (Application waits if necessary, no data lost)\r\n");
SEGGER_RTT_WriteString(0, "Press <2> to continue in non-blocking mode (Application does not wait, data lost if fifo full)\r\n");
do {
r = SEGGER_RTT_WaitKey();
} while ((r != '1') && (r != '2'));
if (r == '1') {
SEGGER_RTT_WriteString(0, "\r\nSelected <1>. Configuring RTT and starting...\r\n");
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
} else {
SEGGER_RTT_WriteString(0, "\r\nSelected <2>. Configuring RTT and starting...\r\n");
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
}
CancelOp = 0;
do {
//for (_Delay = 0; _Delay < 10000; _Delay++);
SEGGER_RTT_printf(0, "Count: %d. Press <Space> to get back to menu.\r\n", _Cnt++);
r = SEGGER_RTT_HasKey();
if (r) {
CancelOp = (SEGGER_RTT_GetKey() == ' ') ? 1 : 0;
}
//
// Check if user selected to cancel the current operation
//
if (CancelOp) {
SEGGER_RTT_WriteString(0, "Operation cancelled, going back to menu...\r\n");
break;
}
} while (1);
SEGGER_RTT_GetKey();
SEGGER_RTT_WriteString(0, "\r\n");
} while (1);
}
/*************************** End of file ****************************/

View File

@@ -1,118 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
--------- END-OF-HEADER --------------------------------------------
File : Main_RTT_MenuApp.c
Purpose : Sample application to demonstrate RTT bi-directional functionality
*/
#define MAIN_C
#include <stdio.h>
#include "SEGGER_RTT.h"
volatile int _Cnt;
/*********************************************************************
*
* main
*/
void main(void) {
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
SEGGER_RTT_WriteString(0, "SEGGER Real-Time-Terminal Sample\r\n\r\n");
SEGGER_RTT_WriteString(0, "###### Testing SEGGER_printf() ######\r\n");
SEGGER_RTT_printf(0, "printf Test: %%c, 'S' : %c.\r\n", 'S');
SEGGER_RTT_printf(0, "printf Test: %%5c, 'E' : %5c.\r\n", 'E');
SEGGER_RTT_printf(0, "printf Test: %%-5c, 'G' : %-5c.\r\n", 'G');
SEGGER_RTT_printf(0, "printf Test: %%5.3c, 'G' : %-5c.\r\n", 'G');
SEGGER_RTT_printf(0, "printf Test: %%.3c, 'E' : %-5c.\r\n", 'E');
SEGGER_RTT_printf(0, "printf Test: %%c, 'R' : %c.\r\n", 'R');
SEGGER_RTT_printf(0, "printf Test: %%s, \"RTT\" : %s.\r\n", "RTT");
SEGGER_RTT_printf(0, "printf Test: %%s, \"RTT\\r\\nRocks.\" : %s.\r\n", "RTT\r\nRocks.");
SEGGER_RTT_printf(0, "printf Test: %%u, 12345 : %u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%+u, 12345 : %+u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%.3u, 12345 : %.3u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%.6u, 12345 : %.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%6.3u, 12345 : %6.3u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%8.6u, 12345 : %8.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%08u, 12345 : %08u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%08.6u, 12345 : %08.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%0u, 12345 : %0u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-.6u, 12345 : %-.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-6.3u, 12345 : %-6.3u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-8.6u, 12345 : %-8.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-08u, 12345 : %-08u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-08.6u, 12345 : %-08.6u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%-0u, 12345 : %-0u.\r\n", 12345);
SEGGER_RTT_printf(0, "printf Test: %%u, -12345 : %u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%+u, -12345 : %+u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%.3u, -12345 : %.3u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%.6u, -12345 : %.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%6.3u, -12345 : %6.3u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%8.6u, -12345 : %8.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%08u, -12345 : %08u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%08.6u, -12345 : %08.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%0u, -12345 : %0u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-.6u, -12345 : %-.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-6.3u, -12345 : %-6.3u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-8.6u, -12345 : %-8.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-08u, -12345 : %-08u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-08.6u, -12345 : %-08.6u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-0u, -12345 : %-0u.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%d, -12345 : %d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%+d, -12345 : %+d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%.3d, -12345 : %.3d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%.6d, -12345 : %.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%6.3d, -12345 : %6.3d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%8.6d, -12345 : %8.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%08d, -12345 : %08d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%08.6d, -12345 : %08.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%0d, -12345 : %0d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-.6d, -12345 : %-.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-6.3d, -12345 : %-6.3d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-8.6d, -12345 : %-8.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-08d, -12345 : %-08d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-08.6d, -12345 : %-08.6d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%-0d, -12345 : %-0d.\r\n", -12345);
SEGGER_RTT_printf(0, "printf Test: %%x, 0x1234ABC : %x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%+x, 0x1234ABC : %+x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%.3x, 0x1234ABC : %.3x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%.6x, 0x1234ABC : %.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%6.3x, 0x1234ABC : %6.3x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%8.6x, 0x1234ABC : %8.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%08x, 0x1234ABC : %08x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%08.6x, 0x1234ABC : %08.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%0x, 0x1234ABC : %0x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-.6x, 0x1234ABC : %-.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-6.3x, 0x1234ABC : %-6.3x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-8.6x, 0x1234ABC : %-8.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-08x, 0x1234ABC : %-08x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-08.6x, 0x1234ABC : %-08.6x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%-0x, 0x1234ABC : %-0x.\r\n", 0x1234ABC);
SEGGER_RTT_printf(0, "printf Test: %%p, &_Cnt : %p.\r\n", &_Cnt);
SEGGER_RTT_WriteString(0, "###### SEGGER_printf() Tests done. ######\r\n");
do {
_Cnt++;
} while (1);
}
/*************************** End of file ****************************/

View File

@@ -1,69 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
--------- END-OF-HEADER --------------------------------------------
File : Main_RTT_SpeedTestApp.c
Purpose : Sample program for measuring RTT performance.
*/
#include "RTOS.h"
#include "BSP.h"
#include "SEGGER_RTT.h"
#include <stdio.h>
OS_STACKPTR int StackHP[128], StackLP[128]; /* Task stacks */
OS_TASK TCBHP, TCBLP; /* Task-control-blocks */
static void HPTask(void) {
while (1) {
//
// Measure time needed for RTT output
// Perform dummy write with 0 characters, so we know the overhead of toggling LEDs and RTT in general
//
// Set BP here. Then start sampling on scope
BSP_ClrLED(0);
SEGGER_RTT_Write(0, 0, 0);
BSP_SetLED(0);
BSP_ClrLED(0);
SEGGER_RTT_Write(0, "01234567890123456789012345678901234567890123456789012345678901234567890123456789\r\n", 82);
BSP_SetLED(0);
// Set BP here. Then stop sampling on scope
OS_Delay(200);
}
}
static void LPTask(void) {
while (1) {
BSP_ToggleLED(1);
OS_Delay (500);
}
}
/*********************************************************************
*
* main
*
*********************************************************************/
int main(void) {
OS_IncDI(); /* Initially disable interrupts */
OS_InitKern(); /* Initialize OS */
OS_InitHW(); /* Initialize Hardware for OS */
BSP_Init(); /* Initialize LED ports */
BSP_SetLED(0);
/* You need to create at least one task before calling OS_Start() */
OS_CREATETASK(&TCBHP, "HP Task", HPTask, 100, StackHP);
OS_CREATETASK(&TCBLP, "LP Task", LPTask, 50, StackLP);
OS_Start(); /* Start multitasking */
return 0;
}

View File

@@ -1,321 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT.h
Purpose : Implementation of SEGGER real-time transfer which allows
real-time communication on targets which support debugger
memory accesses while the CPU is running.
Revision: $Rev: 17697 $
----------------------------------------------------------------------
*/
#ifndef SEGGER_RTT_H
#define SEGGER_RTT_H
#include "SEGGER_RTT_Conf.h"
/*********************************************************************
*
* Defines, defaults
*
**********************************************************************
*/
#ifndef RTT_USE_ASM
#if (defined __SES_ARM) // SEGGER Embedded Studio
#define _CC_HAS_RTT_ASM_SUPPORT 1
#elif (defined __CROSSWORKS_ARM) // Rowley Crossworks
#define _CC_HAS_RTT_ASM_SUPPORT 1
#elif (defined __GNUC__) // GCC
#define _CC_HAS_RTT_ASM_SUPPORT 1
#elif (defined __clang__) // Clang compiler
#define _CC_HAS_RTT_ASM_SUPPORT 1
#elif (defined __IASMARM__) // IAR assembler
#define _CC_HAS_RTT_ASM_SUPPORT 1
#elif (defined __ICCARM__) // IAR compiler
#define _CC_HAS_RTT_ASM_SUPPORT 1
#else
#define _CC_HAS_RTT_ASM_SUPPORT 0
#endif
#if (defined __ARM_ARCH_7M__) // Cortex-M3/4
#define _CORE_HAS_RTT_ASM_SUPPORT 1
#elif (defined __ARM_ARCH_7EM__) // Cortex-M7
#define _CORE_HAS_RTT_ASM_SUPPORT 1
#elif (defined __ARM_ARCH_8M_MAIN__) // Cortex-M33
#define _CORE_HAS_RTT_ASM_SUPPORT 1
#elif (defined __ARM7M__) // IAR Cortex-M3/4
#if (__CORE__ == __ARM7M__)
#define _CORE_HAS_RTT_ASM_SUPPORT 1
#else
#define _CORE_HAS_RTT_ASM_SUPPORT 0
#endif
#elif (defined __ARM7EM__) // IAR Cortex-M7
#if (__CORE__ == __ARM7EM__)
#define _CORE_HAS_RTT_ASM_SUPPORT 1
#else
#define _CORE_HAS_RTT_ASM_SUPPORT 0
#endif
#else
#define _CORE_HAS_RTT_ASM_SUPPORT 0
#endif
//
// If IDE and core support the ASM version, enable ASM version by default
//
#if (_CC_HAS_RTT_ASM_SUPPORT && _CORE_HAS_RTT_ASM_SUPPORT)
#define RTT_USE_ASM (1)
#else
#define RTT_USE_ASM (0)
#endif
#endif
#ifndef SEGGER_RTT_ASM // defined when SEGGER_RTT.h is included from assembly file
#include <stdlib.h>
#include <stdarg.h>
/*********************************************************************
*
* Defines, fixed
*
**********************************************************************
*/
/*********************************************************************
*
* Types
*
**********************************************************************
*/
//
// Description for a circular buffer (also called "ring buffer")
// which is used as up-buffer (T->H)
//
typedef struct {
const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4"
char* pBuffer; // Pointer to start of buffer
unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty.
unsigned WrOff; // Position of next item to be written by either target.
volatile unsigned RdOff; // Position of next item to be read by host. Must be volatile since it may be modified by host.
unsigned Flags; // Contains configuration flags
} SEGGER_RTT_BUFFER_UP;
//
// Description for a circular buffer (also called "ring buffer")
// which is used as down-buffer (H->T)
//
typedef struct {
const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4"
char* pBuffer; // Pointer to start of buffer
unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty.
volatile unsigned WrOff; // Position of next item to be written by host. Must be volatile since it may be modified by host.
unsigned RdOff; // Position of next item to be read by target (down-buffer).
unsigned Flags; // Contains configuration flags
} SEGGER_RTT_BUFFER_DOWN;
//
// RTT control block which describes the number of buffers available
// as well as the configuration for each buffer
//
//
typedef struct {
char acID[16]; // Initialized to "SEGGER RTT"
int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2)
int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2)
SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host
SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target
} SEGGER_RTT_CB;
/*********************************************************************
*
* Global data
*
**********************************************************************
*/
extern SEGGER_RTT_CB _SEGGER_RTT;
/*********************************************************************
*
* RTT API functions
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" {
#endif
int SEGGER_RTT_AllocDownBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags);
int SEGGER_RTT_AllocUpBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags);
int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags);
int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags);
int SEGGER_RTT_GetKey (void);
unsigned SEGGER_RTT_HasData (unsigned BufferIndex);
int SEGGER_RTT_HasKey (void);
unsigned SEGGER_RTT_HasDataUp (unsigned BufferIndex);
void SEGGER_RTT_Init (void);
unsigned SEGGER_RTT_Read (unsigned BufferIndex, void* pBuffer, unsigned BufferSize);
unsigned SEGGER_RTT_ReadNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize);
int SEGGER_RTT_SetNameDownBuffer (unsigned BufferIndex, const char* sName);
int SEGGER_RTT_SetNameUpBuffer (unsigned BufferIndex, const char* sName);
int SEGGER_RTT_SetFlagsDownBuffer (unsigned BufferIndex, unsigned Flags);
int SEGGER_RTT_SetFlagsUpBuffer (unsigned BufferIndex, unsigned Flags);
int SEGGER_RTT_WaitKey (void);
unsigned SEGGER_RTT_Write (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_WriteNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_ASM_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_WriteString (unsigned BufferIndex, const char* s);
void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_PutChar (unsigned BufferIndex, char c);
unsigned SEGGER_RTT_PutCharSkip (unsigned BufferIndex, char c);
unsigned SEGGER_RTT_PutCharSkipNoLock (unsigned BufferIndex, char c);
unsigned SEGGER_RTT_GetAvailWriteSpace (unsigned BufferIndex);
unsigned SEGGER_RTT_GetBytesInBuffer (unsigned BufferIndex);
//
// Function macro for performance optimization
//
#define SEGGER_RTT_HASDATA(n) (_SEGGER_RTT.aDown[n].WrOff - _SEGGER_RTT.aDown[n].RdOff)
#if RTT_USE_ASM
#define SEGGER_RTT_WriteSkipNoLock SEGGER_RTT_ASM_WriteSkipNoLock
#endif
/*********************************************************************
*
* RTT transfer functions to send RTT data via other channels.
*
**********************************************************************
*/
unsigned SEGGER_RTT_ReadUpBuffer (unsigned BufferIndex, void* pBuffer, unsigned BufferSize);
unsigned SEGGER_RTT_ReadUpBufferNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize);
unsigned SEGGER_RTT_WriteDownBuffer (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
unsigned SEGGER_RTT_WriteDownBufferNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes);
#define SEGGER_RTT_HASDATA_UP(n) (_SEGGER_RTT.aUp[n].WrOff - _SEGGER_RTT.aUp[n].RdOff)
/*********************************************************************
*
* RTT "Terminal" API functions
*
**********************************************************************
*/
int SEGGER_RTT_SetTerminal (unsigned char TerminalId);
int SEGGER_RTT_TerminalOut (unsigned char TerminalId, const char* s);
/*********************************************************************
*
* RTT printf functions (require SEGGER_RTT_printf.c)
*
**********************************************************************
*/
int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...);
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
#ifdef __cplusplus
}
#endif
#endif // ifndef(SEGGER_RTT_ASM)
/*********************************************************************
*
* Defines
*
**********************************************************************
*/
//
// Operating modes. Define behavior if buffer is full (not enough space for entire message)
//
#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0) // Skip. Do not block, output nothing. (Default)
#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1) // Trim: Do not block, output as much as fits.
#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2) // Block: Wait until there is space in the buffer.
#define SEGGER_RTT_MODE_MASK (3)
//
// Control sequences, based on ANSI.
// Can be used to control color, and clear the screen
//
#define RTT_CTRL_RESET "\x1B[0m" // Reset to default colors
#define RTT_CTRL_CLEAR "\x1B[2J" // Clear screen, reposition cursor to top left
#define RTT_CTRL_TEXT_BLACK "\x1B[2;30m"
#define RTT_CTRL_TEXT_RED "\x1B[2;31m"
#define RTT_CTRL_TEXT_GREEN "\x1B[2;32m"
#define RTT_CTRL_TEXT_YELLOW "\x1B[2;33m"
#define RTT_CTRL_TEXT_BLUE "\x1B[2;34m"
#define RTT_CTRL_TEXT_MAGENTA "\x1B[2;35m"
#define RTT_CTRL_TEXT_CYAN "\x1B[2;36m"
#define RTT_CTRL_TEXT_WHITE "\x1B[2;37m"
#define RTT_CTRL_TEXT_BRIGHT_BLACK "\x1B[1;30m"
#define RTT_CTRL_TEXT_BRIGHT_RED "\x1B[1;31m"
#define RTT_CTRL_TEXT_BRIGHT_GREEN "\x1B[1;32m"
#define RTT_CTRL_TEXT_BRIGHT_YELLOW "\x1B[1;33m"
#define RTT_CTRL_TEXT_BRIGHT_BLUE "\x1B[1;34m"
#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "\x1B[1;35m"
#define RTT_CTRL_TEXT_BRIGHT_CYAN "\x1B[1;36m"
#define RTT_CTRL_TEXT_BRIGHT_WHITE "\x1B[1;37m"
#define RTT_CTRL_BG_BLACK "\x1B[24;40m"
#define RTT_CTRL_BG_RED "\x1B[24;41m"
#define RTT_CTRL_BG_GREEN "\x1B[24;42m"
#define RTT_CTRL_BG_YELLOW "\x1B[24;43m"
#define RTT_CTRL_BG_BLUE "\x1B[24;44m"
#define RTT_CTRL_BG_MAGENTA "\x1B[24;45m"
#define RTT_CTRL_BG_CYAN "\x1B[24;46m"
#define RTT_CTRL_BG_WHITE "\x1B[24;47m"
#define RTT_CTRL_BG_BRIGHT_BLACK "\x1B[4;40m"
#define RTT_CTRL_BG_BRIGHT_RED "\x1B[4;41m"
#define RTT_CTRL_BG_BRIGHT_GREEN "\x1B[4;42m"
#define RTT_CTRL_BG_BRIGHT_YELLOW "\x1B[4;43m"
#define RTT_CTRL_BG_BRIGHT_BLUE "\x1B[4;44m"
#define RTT_CTRL_BG_BRIGHT_MAGENTA "\x1B[4;45m"
#define RTT_CTRL_BG_BRIGHT_CYAN "\x1B[4;46m"
#define RTT_CTRL_BG_BRIGHT_WHITE "\x1B[4;47m"
#endif
/*************************** End of file ****************************/

View File

@@ -1,384 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT_Conf.h
Purpose : Implementation of SEGGER real-time transfer (RTT) which
allows real-time communication on targets which support
debugger memory accesses while the CPU is running.
Revision: $Rev: 18601 $
*/
#ifndef SEGGER_RTT_CONF_H
#define SEGGER_RTT_CONF_H
#ifdef __IAR_SYSTEMS_ICC__
#include <intrinsics.h>
#endif
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS
#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3) // Max. number of up-buffers (T->H) available on this target (Default: 3)
#endif
#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS
#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3) // Max. number of down-buffers (H->T) available on this target (Default: 3)
#endif
#ifndef BUFFER_SIZE_UP
#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k)
#endif
#ifndef BUFFER_SIZE_DOWN
#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16)
#endif
#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64)
#endif
#ifndef SEGGER_RTT_MODE_DEFAULT
#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0)
#endif
/*********************************************************************
*
* RTT memcpy configuration
*
* memcpy() is good for large amounts of data,
* but the overhead is big for small amounts, which are usually stored via RTT.
* With SEGGER_RTT_MEMCPY_USE_BYTELOOP a simple byte loop can be used instead.
*
* SEGGER_RTT_MEMCPY() can be used to replace standard memcpy() in RTT functions.
* This is may be required with memory access restrictions,
* such as on Cortex-A devices with MMU.
*/
#ifndef SEGGER_RTT_MEMCPY_USE_BYTELOOP
#define SEGGER_RTT_MEMCPY_USE_BYTELOOP 0 // 0: Use memcpy/SEGGER_RTT_MEMCPY, 1: Use a simple byte-loop
#endif
//
// Example definition of SEGGER_RTT_MEMCPY to external memcpy with GCC toolchains and Cortex-A targets
//
//#if ((defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__)) && (defined (__ARM_ARCH_7A__))
// #define SEGGER_RTT_MEMCPY(pDest, pSrc, NumBytes) SEGGER_memcpy((pDest), (pSrc), (NumBytes))
//#endif
//
// Target is not allowed to perform other RTT operations while string still has not been stored completely.
// Otherwise we would probably end up with a mixed string in the buffer.
// If using RTT from within interrupts, multiple tasks or multi processors, define the SEGGER_RTT_LOCK() and SEGGER_RTT_UNLOCK() function here.
//
// SEGGER_RTT_MAX_INTERRUPT_PRIORITY can be used in the sample lock routines on Cortex-M3/4.
// Make sure to mask all interrupts which can send RTT data, i.e. generate SystemView events, or cause task switches.
// When high-priority interrupts must not be masked while sending RTT data, SEGGER_RTT_MAX_INTERRUPT_PRIORITY needs to be adjusted accordingly.
// (Higher priority = lower priority number)
// Default value for embOS: 128u
// Default configuration in FreeRTOS: configMAX_SYSCALL_INTERRUPT_PRIORITY: ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
// In case of doubt mask all interrupts: 1 << (8 - BASEPRI_PRIO_BITS) i.e. 1 << 5 when 3 bits are implemented in NVIC
// or define SEGGER_RTT_LOCK() to completely disable interrupts.
//
#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4 (Default: 0x20)
#endif
/*********************************************************************
*
* RTT lock configuration for SEGGER Embedded Studio,
* Rowley CrossStudio and GCC
*/
#if ((defined(__SES_ARM) || defined(__SES_RISCV) || defined(__CROSSWORKS_ARM) || defined(__GNUC__) || defined(__clang__)) && !defined (__CC_ARM) && !defined(WIN32))
#if (defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_8M_BASE__))
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
__asm volatile ("mrs %0, primask \n\t" \
"movs r1, $1 \n\t" \
"msr primask, r1 \n\t" \
: "=r" (LockState) \
: \
: "r1" \
);
#define SEGGER_RTT_UNLOCK() __asm volatile ("msr primask, %0 \n\t" \
: \
: "r" (LockState) \
: \
); \
}
#elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__))
#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20)
#endif
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
__asm volatile ("mrs %0, basepri \n\t" \
"mov r1, %1 \n\t" \
"msr basepri, r1 \n\t" \
: "=r" (LockState) \
: "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) \
: "r1" \
);
#define SEGGER_RTT_UNLOCK() __asm volatile ("msr basepri, %0 \n\t" \
: \
: "r" (LockState) \
: \
); \
}
#elif defined(__ARM_ARCH_7A__)
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
__asm volatile ("mrs r1, CPSR \n\t" \
"mov %0, r1 \n\t" \
"orr r1, r1, #0xC0 \n\t" \
"msr CPSR_c, r1 \n\t" \
: "=r" (LockState) \
: \
: "r1" \
);
#define SEGGER_RTT_UNLOCK() __asm volatile ("mov r0, %0 \n\t" \
"mrs r1, CPSR \n\t" \
"bic r1, r1, #0xC0 \n\t" \
"and r0, r0, #0xC0 \n\t" \
"orr r1, r1, r0 \n\t" \
"msr CPSR_c, r1 \n\t" \
: \
: "r" (LockState) \
: "r0", "r1" \
); \
}
#elif defined(__riscv) || defined(__riscv_xlen)
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
__asm volatile ("csrr %0, mstatus \n\t" \
"csrci mstatus, 8 \n\t" \
"andi %0, %0, 8 \n\t" \
: "=r" (LockState) \
: \
: \
);
#define SEGGER_RTT_UNLOCK() __asm volatile ("csrr a1, mstatus \n\t" \
"or %0, %0, a1 \n\t" \
"csrs mstatus, %0 \n\t" \
: \
: "r" (LockState) \
: "a1" \
); \
}
#else
#define SEGGER_RTT_LOCK()
#define SEGGER_RTT_UNLOCK()
#endif
#endif
/*********************************************************************
*
* RTT lock configuration for IAR EWARM
*/
#ifdef __ICCARM__
#if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) || \
(defined (__ARM8M_BASELINE__) && (__CORE__ == __ARM8M_BASELINE__))
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
LockState = __get_PRIMASK(); \
__set_PRIMASK(1);
#define SEGGER_RTT_UNLOCK() __set_PRIMASK(LockState); \
}
#elif (defined (__ARM7EM__) && (__CORE__ == __ARM7EM__)) || \
(defined (__ARM7M__) && (__CORE__ == __ARM7M__)) || \
(defined (__ARM8M_MAINLINE__) && (__CORE__ == __ARM8M_MAINLINE__)) || \
(defined (__ARM8M_MAINLINE__) && (__CORE__ == __ARM8M_MAINLINE__))
#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20)
#endif
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
LockState = __get_BASEPRI(); \
__set_BASEPRI(SEGGER_RTT_MAX_INTERRUPT_PRIORITY);
#define SEGGER_RTT_UNLOCK() __set_BASEPRI(LockState); \
}
#endif
#endif
/*********************************************************************
*
* RTT lock configuration for IAR RX
*/
#ifdef __ICCRX__
#define SEGGER_RTT_LOCK() { \
unsigned long LockState; \
LockState = __get_interrupt_state(); \
__disable_interrupt();
#define SEGGER_RTT_UNLOCK() __set_interrupt_state(LockState); \
}
#endif
/*********************************************************************
*
* RTT lock configuration for IAR RL78
*/
#ifdef __ICCRL78__
#define SEGGER_RTT_LOCK() { \
__istate_t LockState; \
LockState = __get_interrupt_state(); \
__disable_interrupt();
#define SEGGER_RTT_UNLOCK() __set_interrupt_state(LockState); \
}
#endif
/*********************************************************************
*
* RTT lock configuration for KEIL ARM
*/
#ifdef __CC_ARM
#if (defined __TARGET_ARCH_6S_M)
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
register unsigned char PRIMASK __asm( "primask"); \
LockState = PRIMASK; \
PRIMASK = 1u; \
__schedule_barrier();
#define SEGGER_RTT_UNLOCK() PRIMASK = LockState; \
__schedule_barrier(); \
}
#elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M))
#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20)
#endif
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
register unsigned char BASEPRI __asm( "basepri"); \
LockState = BASEPRI; \
BASEPRI = SEGGER_RTT_MAX_INTERRUPT_PRIORITY; \
__schedule_barrier();
#define SEGGER_RTT_UNLOCK() BASEPRI = LockState; \
__schedule_barrier(); \
}
#endif
#endif
/*********************************************************************
*
* RTT lock configuration for TI ARM
*/
#ifdef __TI_ARM__
#if defined (__TI_ARM_V6M0__)
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
LockState = __get_PRIMASK(); \
__set_PRIMASK(1);
#define SEGGER_RTT_UNLOCK() __set_PRIMASK(LockState); \
}
#elif (defined (__TI_ARM_V7M3__) || defined (__TI_ARM_V7M4__))
#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY
#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20)
#endif
#define SEGGER_RTT_LOCK() { \
unsigned int LockState; \
LockState = _set_interrupt_priority(SEGGER_RTT_MAX_INTERRUPT_PRIORITY);
#define SEGGER_RTT_UNLOCK() _set_interrupt_priority(LockState); \
}
#endif
#endif
/*********************************************************************
*
* RTT lock configuration for CCRX
*/
#ifdef __RX
#define SEGGER_RTT_LOCK() { \
unsigned long LockState; \
LockState = get_psw() & 0x010000; \
clrpsw_i();
#define SEGGER_RTT_UNLOCK() set_psw(get_psw() | LockState); \
}
#endif
/*********************************************************************
*
* RTT lock configuration for embOS Simulation on Windows
* (Can also be used for generic RTT locking with embOS)
*/
#if defined(WIN32) || defined(SEGGER_RTT_LOCK_EMBOS)
void OS_SIM_EnterCriticalSection(void);
void OS_SIM_LeaveCriticalSection(void);
#define SEGGER_RTT_LOCK() { \
OS_SIM_EnterCriticalSection();
#define SEGGER_RTT_UNLOCK() OS_SIM_LeaveCriticalSection(); \
}
#endif
/*********************************************************************
*
* RTT lock configuration fallback
*/
#ifndef SEGGER_RTT_LOCK
#define SEGGER_RTT_LOCK() // Lock RTT (nestable) (i.e. disable interrupts)
#endif
#ifndef SEGGER_RTT_UNLOCK
#define SEGGER_RTT_UNLOCK() // Unlock RTT (nestable) (i.e. enable previous interrupt lock state)
#endif
#endif
/*************************** End of file ****************************/

File diff suppressed because it is too large Load Diff

View File

@@ -1,235 +0,0 @@
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : SEGGER_RTT_ASM_ARMv7M.S
Purpose : Assembler implementation of RTT functions for ARMv7M
Additional information:
This module is written to be assembler-independent and works with
GCC and clang (Embedded Studio) and IAR.
*/
#define SEGGER_RTT_ASM // Used to control processed input from header file
#include "SEGGER_RTT.h"
/*********************************************************************
*
* Defines, fixed
*
**********************************************************************
*/
#define _CCIAR 0
#define _CCCLANG 1
#if (defined __SES_ARM) || (defined __GNUC__) || (defined __clang__)
#define _CC_TYPE _CCCLANG
#define _PUB_SYM .global
#define _EXT_SYM .extern
#define _END .end
#define _WEAK .weak
#define _THUMB_FUNC .thumb_func
#define _THUMB_CODE .code 16
#define _WORD .word
#define _SECTION(Sect, Type, AlignExp) .section Sect ##, "ax"
#define _ALIGN(Exp) .align Exp
#define _PLACE_LITS .ltorg
#define _DATA_SECT_START
#define _C_STARTUP _start
#define _STACK_END __stack_end__
#define _RAMFUNC
//
// .text => Link to flash
// .fast => Link to RAM
// OtherSect => Usually link to RAM
// Alignment is 2^x
//
#elif defined (__IASMARM__)
#define _CC_TYPE _CCIAR
#define _PUB_SYM PUBLIC
#define _EXT_SYM EXTERN
#define _END END
#define _WEAK _WEAK
#define _THUMB_FUNC
#define _THUMB_CODE THUMB
#define _WORD DCD
#define _SECTION(Sect, Type, AlignExp) SECTION Sect ## : ## Type ## :REORDER:NOROOT ## (AlignExp)
#define _ALIGN(Exp) alignrom Exp
#define _PLACE_LITS
#define _DATA_SECT_START DATA
#define _C_STARTUP __iar_program_start
#define _STACK_END sfe(CSTACK)
#define _RAMFUNC SECTION_TYPE SHT_PROGBITS, SHF_WRITE | SHF_EXECINSTR
//
// .text => Link to flash
// .textrw => Link to RAM
// OtherSect => Usually link to RAM
// NOROOT => Allows linker to throw away the function, if not referenced
// Alignment is 2^x
//
#endif
#if (_CC_TYPE == _CCIAR)
NAME SEGGER_RTT_ASM_ARMv7M
#else
.syntax unified
#endif
#if defined (RTT_USE_ASM) && (RTT_USE_ASM == 1)
#define SHT_PROGBITS 0x1
/*********************************************************************
*
* Public / external symbols
*
**********************************************************************
*/
_EXT_SYM __aeabi_memcpy
_EXT_SYM __aeabi_memcpy4
_EXT_SYM _SEGGER_RTT
_PUB_SYM SEGGER_RTT_ASM_WriteSkipNoLock
/*********************************************************************
*
* SEGGER_RTT_WriteSkipNoLock
*
* Function description
* Stores a specified number of characters in SEGGER RTT
* control block which is then read by the host.
* SEGGER_RTT_WriteSkipNoLock does not lock the application and
* skips all data, if the data does not fit into the buffer.
*
* Parameters
* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal").
* pBuffer Pointer to character array. Does not need to point to a \0 terminated string.
* NumBytes Number of bytes to be stored in the SEGGER RTT control block.
* MUST be > 0!!!
* This is done for performance reasons, so no initial check has do be done.
*
* Return value
* 1: Data has been copied
* 0: No space, data has not been copied
*
* Notes
* (1) If there is not enough space in the "Up"-buffer, all data is dropped.
* (2) For performance reasons this function does not call Init()
* and may only be called after RTT has been initialized.
* Either by calling SEGGER_RTT_Init() or calling another RTT API function first.
*/
_SECTION(.text, CODE, 2)
_ALIGN(2)
_THUMB_FUNC
SEGGER_RTT_ASM_WriteSkipNoLock: // unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pData, unsigned NumBytes) {
//
// Cases:
// 1) RdOff <= WrOff => Space until wrap-around is sufficient
// 2) RdOff <= WrOff => Space after wrap-around needed (copy in 2 chunks)
// 3) RdOff < WrOff => No space in buf
// 4) RdOff > WrOff => Space is sufficient
// 5) RdOff > WrOff => No space in buf
//
// 1) is the most common case for large buffers and assuming that J-Link reads the data fast enough
//
// Register usage:
// R0 Temporary needed as RdOff, <Tmp> register later on
// R1 pData
// R2 <NumBytes>
// R3 <Tmp> register. Hold free for subroutine calls
// R4 <Rem>
// R5 pRing->pBuffer
// R6 pRing (Points to active struct SEGGER_RTT_BUFFER_DOWN)
// R7 WrOff
//
PUSH {R4-R7}
ADD R3,R0,R0, LSL #+1
LDR.W R0,=_SEGGER_RTT // pRing = &_SEGGER_RTT.aUp[BufferIndex];
ADD R0,R0,R3, LSL #+3
ADD R6,R0,#+24
LDR R0,[R6, #+16] // RdOff = pRing->RdOff;
LDR R7,[R6, #+12] // WrOff = pRing->WrOff;
LDR R5,[R6, #+4] // pRing->pBuffer
CMP R7,R0
BCC.N _CheckCase4 // if (RdOff <= WrOff) { => Case 1), 2) or 3)
//
// Handling for case 1, later on identical to case 4
//
LDR R3,[R6, #+8] // Avail = pRing->SizeOfBuffer - WrOff - 1u; => Space until wrap-around (assume 1 byte not usable for case that RdOff == 0)
SUBS R4,R3,R7 // <Rem> (Used in case we jump into case 2 afterwards)
SUBS R3,R4,#+1 // <Avail>
CMP R3,R2
BCC.N _CheckCase2 // if (Avail >= NumBytes) { => Case 1)?
_Case4:
ADDS R5,R7,R5 // pBuffer += WrOff
ADDS R0,R2,R7 // v = WrOff + NumBytes
//
// 2x unrolling for the copy loop that is used most of the time
// This is a special optimization for small SystemView packets and makes them even faster
//
_ALIGN(2)
_LoopCopyStraight: // memcpy(pRing->pBuffer + WrOff, pData, NumBytes);
LDRB R3,[R1], #+1
STRB R3,[R5], #+1 // *pDest++ = *pSrc++
SUBS R2,R2,#+1
BEQ _CSDone
LDRB R3,[R1], #+1
STRB R3,[R5], #+1 // *pDest++ = *pSrc++
SUBS R2,R2,#+1
BNE _LoopCopyStraight
_CSDone:
STR R0,[R6, #+12] // pRing->WrOff = WrOff + NumBytes;
MOVS R0,#+1
POP {R4-R7}
BX LR // Return 1
_CheckCase2:
ADDS R0,R0,R3 // Avail += RdOff; => Space incl. wrap-around
CMP R0,R2
BCC.N _Case3 // if (Avail >= NumBytes) { => Case 2? => If not, we have case 3) (does not fit)
//
// Handling for case 2
//
ADDS R0,R7,R5 // v = pRing->pBuffer + WrOff => Do not change pRing->pBuffer here because 2nd chunk needs org. value
SUBS R2,R2,R4 // NumBytes -= Rem; (Rem = pRing->SizeOfBuffer - WrOff; => Space until end of buffer)
_LoopCopyBeforeWrapAround: // memcpy(pRing->pBuffer + WrOff, pData, Rem); => Copy 1st chunk
LDRB R3,[R1], #+1
STRB R3,[R0], #+1 // *pDest++ = *pSrc++
SUBS R4,R4,#+1
BNE _LoopCopyBeforeWrapAround
//
// Special case: First check that assumed RdOff == 0 calculated that last element before wrap-around could not be used
// But 2nd check (considering space until wrap-around and until RdOff) revealed that RdOff is not 0, so we can use the last element
// In this case, we may use a copy straight until buffer end anyway without needing to copy 2 chunks
// Therefore, check if 2nd memcpy is necessary at all
//
ADDS R4,R2,#+0 // Save <NumBytes> (needed as counter in loop but must be written to <WrOff> after the loop). Also use this inst to update the flags to skip 2nd loop if possible
BEQ.N _No2ChunkNeeded // if (NumBytes) {
_LoopCopyAfterWrapAround: // memcpy(pRing->pBuffer, pData + Rem, NumBytes);
LDRB R3,[R1], #+1 // pData already points to the next src byte due to copy loop increment before this loop
STRB R3,[R5], #+1 // *pDest++ = *pSrc++
SUBS R2,R2,#+1
BNE _LoopCopyAfterWrapAround
_No2ChunkNeeded:
STR R4,[R6, #+12] // pRing->WrOff = NumBytes; => Must be written after copying data because J-Link may read control block asynchronously while writing into buffer
MOVS R0,#+1
POP {R4-R7}
BX LR // Return 1
_CheckCase4:
SUBS R0,R0,R7
SUBS R0,R0,#+1 // Avail = RdOff - WrOff - 1u;
CMP R0,R2
BCS.N _Case4 // if (Avail >= NumBytes) { => Case 4) == 1) ? => If not, we have case 5) == 3) (does not fit)
_Case3:
MOVS R0,#+0
POP {R4-R7}
BX LR // Return 0
_PLACE_LITS
#endif // defined (RTT_USE_ASM) && (RTT_USE_ASM == 1)
_END
/*************************** End of file ****************************/

View File

@@ -1,500 +0,0 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the RTT protocol and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
File : SEGGER_RTT_printf.c
Purpose : Replacement for printf to write formatted data via RTT
Revision: $Rev: 17697 $
----------------------------------------------------------------------
*/
#include "SEGGER_RTT.h"
#include "SEGGER_RTT_Conf.h"
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
#endif
#include <stdlib.h>
#include <stdarg.h>
#define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0)
#define FORMAT_FLAG_PAD_ZERO (1u << 1)
#define FORMAT_FLAG_PRINT_SIGN (1u << 2)
#define FORMAT_FLAG_ALTERNATE (1u << 3)
/*********************************************************************
*
* Types
*
**********************************************************************
*/
typedef struct {
char* pBuffer;
unsigned BufferSize;
unsigned Cnt;
int ReturnValue;
unsigned RTTBufferIndex;
} SEGGER_RTT_PRINTF_DESC;
/*********************************************************************
*
* Function prototypes
*
**********************************************************************
*/
/*********************************************************************
*
* Static code
*
**********************************************************************
*/
/*********************************************************************
*
* _StoreChar
*/
static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
unsigned Cnt;
Cnt = p->Cnt;
if ((Cnt + 1u) <= p->BufferSize) {
*(p->pBuffer + Cnt) = c;
p->Cnt = Cnt + 1u;
p->ReturnValue++;
}
//
// Write part of string, when the buffer is full
//
if (p->Cnt == p->BufferSize) {
if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
p->ReturnValue = -1;
} else {
p->Cnt = 0u;
}
}
}
/*********************************************************************
*
* _PrintUnsigned
*/
static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
unsigned Div;
unsigned Digit;
unsigned Number;
unsigned Width;
char c;
Number = v;
Digit = 1u;
//
// Get actual field width
//
Width = 1u;
while (Number >= Base) {
Number = (Number / Base);
Width++;
}
if (NumDigits > Width) {
Width = NumDigits;
}
//
// Print leading chars if necessary
//
if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
if (FieldWidth != 0u) {
if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
c = '0';
} else {
c = ' ';
}
while ((FieldWidth != 0u) && (Width < FieldWidth)) {
FieldWidth--;
_StoreChar(pBufferDesc, c);
if (pBufferDesc->ReturnValue < 0) {
break;
}
}
}
}
if (pBufferDesc->ReturnValue >= 0) {
//
// Compute Digit.
// Loop until Digit has the value of the highest digit required.
// Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
//
while (1) {
if (NumDigits > 1u) { // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned)
NumDigits--;
} else {
Div = v / Digit;
if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done
break;
}
}
Digit *= Base;
}
//
// Output digits
//
do {
Div = v / Digit;
v -= Div * Digit;
_StoreChar(pBufferDesc, _aV2C[Div]);
if (pBufferDesc->ReturnValue < 0) {
break;
}
Digit /= Base;
} while (Digit);
//
// Print trailing spaces if necessary
//
if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
if (FieldWidth != 0u) {
while ((FieldWidth != 0u) && (Width < FieldWidth)) {
FieldWidth--;
_StoreChar(pBufferDesc, ' ');
if (pBufferDesc->ReturnValue < 0) {
break;
}
}
}
}
}
}
/*********************************************************************
*
* _PrintInt
*/
static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
unsigned Width;
int Number;
Number = (v < 0) ? -v : v;
//
// Get actual field width
//
Width = 1u;
while (Number >= (int)Base) {
Number = (Number / (int)Base);
Width++;
}
if (NumDigits > Width) {
Width = NumDigits;
}
if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
FieldWidth--;
}
//
// Print leading spaces if necessary
//
if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
if (FieldWidth != 0u) {
while ((FieldWidth != 0u) && (Width < FieldWidth)) {
FieldWidth--;
_StoreChar(pBufferDesc, ' ');
if (pBufferDesc->ReturnValue < 0) {
break;
}
}
}
}
//
// Print sign if necessary
//
if (pBufferDesc->ReturnValue >= 0) {
if (v < 0) {
v = -v;
_StoreChar(pBufferDesc, '-');
} else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
_StoreChar(pBufferDesc, '+');
} else {
}
if (pBufferDesc->ReturnValue >= 0) {
//
// Print leading zeros if necessary
//
if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
if (FieldWidth != 0u) {
while ((FieldWidth != 0u) && (Width < FieldWidth)) {
FieldWidth--;
_StoreChar(pBufferDesc, '0');
if (pBufferDesc->ReturnValue < 0) {
break;
}
}
}
}
if (pBufferDesc->ReturnValue >= 0) {
//
// Print number without sign
//
_PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
}
}
}
}
/*********************************************************************
*
* Public code
*
**********************************************************************
*/
/*********************************************************************
*
* SEGGER_RTT_vprintf
*
* Function description
* Stores a formatted string in SEGGER RTT control block.
* This data is read by the host.
*
* Parameters
* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
* sFormat Pointer to format string
* pParamList Pointer to the list of arguments for the format string
*
* Return values
* >= 0: Number of bytes which have been stored in the "Up"-buffer.
* < 0: Error
*/
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
char c;
SEGGER_RTT_PRINTF_DESC BufferDesc;
int v;
unsigned NumDigits;
unsigned FormatFlags;
unsigned FieldWidth;
char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
BufferDesc.pBuffer = acBuffer;
BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE;
BufferDesc.Cnt = 0u;
BufferDesc.RTTBufferIndex = BufferIndex;
BufferDesc.ReturnValue = 0;
do {
c = *sFormat;
sFormat++;
if (c == 0u) {
break;
}
if (c == '%') {
//
// Filter out flags
//
FormatFlags = 0u;
v = 1;
do {
c = *sFormat;
switch (c) {
case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break;
case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break;
case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break;
default: v = 0; break;
}
} while (v);
//
// filter out field with
//
FieldWidth = 0u;
do {
c = *sFormat;
if ((c < '0') || (c > '9')) {
break;
}
sFormat++;
FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
} while (1);
//
// Filter out precision (number of digits to display)
//
NumDigits = 0u;
c = *sFormat;
if (c == '.') {
sFormat++;
do {
c = *sFormat;
if ((c < '0') || (c > '9')) {
break;
}
sFormat++;
NumDigits = NumDigits * 10u + ((unsigned)c - '0');
} while (1);
}
//
// Filter out length modifier
//
c = *sFormat;
do {
if ((c == 'l') || (c == 'h')) {
sFormat++;
c = *sFormat;
} else {
break;
}
} while (1);
//
// Handle specifiers
//
switch (c) {
case 'c': {
char c0;
v = va_arg(*pParamList, int);
c0 = (char)v;
_StoreChar(&BufferDesc, c0);
break;
}
case 'd':
v = va_arg(*pParamList, int);
_PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'u':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'x':
case 'X':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
break;
case 's':
{
const char * s = va_arg(*pParamList, const char *);
do {
c = *s;
s++;
if (c == '\0') {
break;
}
_StoreChar(&BufferDesc, c);
} while (BufferDesc.ReturnValue >= 0);
}
break;
case 'p':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
break;
case '%':
_StoreChar(&BufferDesc, '%');
break;
default:
break;
}
sFormat++;
} else {
_StoreChar(&BufferDesc, c);
}
} while (BufferDesc.ReturnValue >= 0);
if (BufferDesc.ReturnValue > 0) {
//
// Write remaining data, if any
//
if (BufferDesc.Cnt != 0u) {
SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
}
BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
}
return BufferDesc.ReturnValue;
}
/*********************************************************************
*
* SEGGER_RTT_printf
*
* Function description
* Stores a formatted string in SEGGER RTT control block.
* This data is read by the host.
*
* Parameters
* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
* sFormat Pointer to format string, followed by the arguments for conversion
*
* Return values
* >= 0: Number of bytes which have been stored in the "Up"-buffer.
* < 0: Error
*
* Notes
* (1) Conversion specifications have following syntax:
* %[flags][FieldWidth][.Precision]ConversionSpecifier
* (2) Supported flags:
* -: Left justify within the field width
* +: Always print sign extension for signed conversions
* 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
* Supported conversion specifiers:
* c: Print the argument as one char
* d: Print the argument as a signed integer
* u: Print the argument as an unsigned integer
* x: Print the argument as an hexadecimal integer
* s: Print the string pointed to by the argument
* p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
*/
int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
int r;
va_list ParamList;
va_start(ParamList, sFormat);
r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
va_end(ParamList);
return r;
}
/*************************** End of file ****************************/

View File

@@ -22,8 +22,6 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
; HW_VERSION (default emptystring)
[env]
platform = espressif32
framework = arduino
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
@@ -31,7 +29,7 @@ board_build.partitions = partition-table.csv
; note: we add src to our include search path so that lmic_project_config can override
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/rf95 -Isrc/mesh -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map
-DAXP_DEBUG_PORT=Serial
-DHW_VERSION_${sysenv.COUNTRY}
-DAPP_VERSION=${sysenv.APP_VERSION}
@@ -51,6 +49,7 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/rf95 -Isrc/mesh -Ilib/
; the default is esptool
; upload_protocol = esp-prog
; monitor_speed = 115200
monitor_speed = 921600
# debug_tool = esp-prog
@@ -73,18 +72,22 @@ lib_deps =
Wire ; explicitly needed here because the AXP202 library forgets to add it
https://github.com/meshtastic/arduino-fsm.git
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
https://github.com/meshtastic/SX126x-Arduino.git
Ticker ; Needed for SX126x-Arduino on ESP32
https://github.com/meshtastic/RadioLib.git
https://github.com/meshtastic/TinyGPSPlus.git
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
platform = espressif32
framework = arduino
src_filter =
${env.src_filter} -<nrf52/>
upload_speed = 921600
debug_init_break = tbreak setup
build_flags =
${env.build_flags} -Wall -Wextra
${env.build_flags} -Wall -Wextra -Isrc/esp32
lib_ignore = segger_rtt
platform_packages =
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git
; The 1.0 release of the TBEAM board
[env:tbeam]
@@ -92,7 +95,7 @@ extends = esp32_base
board = ttgo-t-beam
lib_deps =
${env.lib_deps}
AXP202X_Library
https://github.com/meshtastic/AXP202X_Library.git
build_flags =
${esp32_base.build_flags} -D TBEAM_V10
@@ -122,26 +125,48 @@ board = ttgo-lora32-v1
build_flags =
${esp32_base.build_flags} -D TTGO_LORA_V2
; The NRF52840-dk development board
[env:nrf52dk]
; Common settings for NRF52 based targets
[nrf52_base]
platform = nordicnrf52
board = ppr
framework = arduino
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
${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/>
lib_ignore =
BluetoothOTA
lib_deps =
${env.lib_deps}
UC1701
https://github.com/meshtastic/BQ25703A.git
monitor_port = /dev/ttyACM1
debug_extra_cmds =
source gdbinit
; after programming the flash, reset the initial PC
; debug_load_cmds = load
; Set initial breakpoint (defaults to main)
debug_init_break =
;debug_init_break = tbreak loop
;debug_init_break = tbreak Reset_Handler
; The NRF52840-dk development board
[env:nrf52dk]
extends = nrf52_base
board = nrf52840_dk_modified
lib_deps =
UC1701 ; for temp testing
; The PPR board
[env:ppr]
extends = nrf52_base
board = ppr
lib_deps =
${env.lib_deps}
UC1701
https://github.com/meshtastic/BQ25703A.git

2
proto

Submodule proto updated: 8427b23016...e7f181ef6f

1
sdk-nrfxlib Submodule

Submodule sdk-nrfxlib added at 17e8453553

View File

@@ -1,218 +0,0 @@
#include "GPS.h"
#include "configuration.h"
#include "time.h"
#include <assert.h>
#include <sys/time.h>
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps(GPS_SERIAL_NUM);
#else
// Assume NRF52
HardwareSerial &_serial_gps = Serial1;
#endif
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
GPS gps;
// stuff that really should be in in the instance instead...
static uint32_t
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
static bool wantNewLocation = true;
GPS::GPS() : PeriodicTask() {}
void GPS::setup()
{
PeriodicTask::setup();
readFromRTC(); // read the main CPU RTC at first
#ifdef GPS_RX_PIN
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps.begin(GPS_BAUDRATE);
#endif
// _serial_gps.setRxBufferSize(1024); // the default is 256
// ublox.enableDebugging(Serial);
// note: the lib's implementation has the wrong docs for what the return val is
// it is not a bool, it returns zero for success
isConnected = ublox.begin(_serial_gps);
// try a second time, the ublox lib serial parsing is buggy?
if (!isConnected)
isConnected = ublox.begin(_serial_gps);
if (isConnected) {
DEBUG_MSG("Connected to GPS successfully\n");
bool factoryReset = false;
bool ok;
if (factoryReset) {
// It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have
// GPS_TX connected)
ublox.factoryReset();
delay(2000);
isConnected = ublox.begin(_serial_gps);
DEBUG_MSG("Factory reset success=%d\n", isConnected);
if (isConnected) {
ublox.assumeAutoPVT(true, true); // Just parse NEMA for now
}
} else {
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
assert(ok);
ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low
assert(ok);
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
// assert(ok);
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
// assert(ok);
ok = ublox.powerSaveMode(); // use power save mode
assert(ok);
}
ok = ublox.saveConfiguration(2000);
assert(ok);
} else {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NEMA at 9600 baud.
DEBUG_MSG("ERROR: No bidirectional GPS found, hoping that it still might work\n");
// tell lib, we are expecting the module to send PVT messages by itself to our Rx pin
// you can set second parameter to "false" if you want to control the parsing and eviction of the data (need to call
// checkUblox cyclically)
ublox.assumeAutoPVT(true, true);
}
}
void GPS::readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpfull here too*/
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
}
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void GPS::perhapsSetRTC(const struct timeval *tv)
{
if (!timeSetFromGPS) {
timeSetFromGPS = true;
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
#ifndef NO_ESP32
settimeofday(tv, NULL);
#else
assert(0);
#endif
readFromRTC();
}
}
#include <time.h>
uint32_t GPS::getTime()
{
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t GPS::getValidTime()
{
return timeSetFromGPS ? getTime() : 0;
}
/// Returns true if we think the board can enter deep or light sleep now (we might be trying to get a GPS lock)
bool GPS::canSleep()
{
return true; // we leave GPS on during sleep now, so sleep is okay !wantNewLocation;
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
void GPS::prepareSleep()
{
if (isConnected)
ublox.powerOff();
}
void GPS::doTask()
{
uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix
if (isConnected) {
// Consume all characters that have arrived
// getPVT automatically calls checkUblox
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
// If we don't have a fix (a quick check), don't try waiting for a solution)
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
// turn off for now
// fixtype = ublox.getFixType();
DEBUG_MSG("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 (!timeSetFromGPS && ublox.getT()) {
struct timeval tv;
/* 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_isdst = false;
time_t res = mktime(&t);
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300)
DEBUG_MSG("Ignoring invalid GPS time\n");
else
perhapsSetRTC(&tv);
}
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only
{
// we only notify if position has changed
latitude = ublox.getLatitude() * 1e-7;
longitude = ublox.getLongitude() * 1e-7;
altitude = ublox.getAltitude() / 1000; // in mm convert to meters
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude, longitude, altitude);
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
if (hasValidLocation) {
wantNewLocation = false;
notifyObservers(NULL);
// ublox.powerOff();
}
} else // we didn't get a location update, go back to sleep and hope the characters show up
wantNewLocation = true;
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
// the serial
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
}
void GPS::startLock()
{
DEBUG_MSG("Looking for GPS lock\n");
wantNewLocation = true;
setPeriod(1);
}

View File

@@ -1,56 +0,0 @@
#pragma once
#include "Observer.h"
#include "PeriodicTask.h"
#include "SparkFun_Ublox_Arduino_Library.h"
#include "sys/time.h"
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class GPS : public PeriodicTask, public Observable<void *>
{
SFE_UBLOX_GPS ublox;
public:
double latitude, longitude;
uint32_t altitude;
bool isConnected; // Do we have a GPS we are talking to
GPS();
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
uint32_t getTime();
/// Return time since 1970 in secs. If we don't have a GPS lock return zero
uint32_t getValidTime();
void setup();
virtual void doTask();
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void perhapsSetRTC(const struct timeval *tv);
/// Returns true if we think the board can enter deep or light sleep now (we might be trying to get a GPS lock)
bool canSleep();
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
void prepareSleep();
/// Restart our lock attempt - try to get and broadcast a GPS reading ASAP
void startLock();
/// Returns ture if we have acquired GPS lock.
bool hasLock() const { return hasValidLocation; }
private:
void readFromRTC();
bool hasValidLocation = false; // default to false, until we complete our first read
};
extern GPS gps;

49
src/OSTimer.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "OSTimer.h"
#include "configuration.h"
/**
* Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR)
*
* NOTE! xTimerPend... seems to ignore the time passed in on ESP32 and on NRF52
* The reason this didn't work is bcause xTimerPednFunctCall really isn't a timer function at all - it just means run the callback
* from the timer thread the next time you have spare cycles.
*
* @return true if successful, false if the timer fifo is too full.
bool scheduleOSCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
{
return xTimerPendFunctionCall(callback, param1, param2, pdMS_TO_TICKS(delayMsec));
} */
#ifndef NO_ESP32
// Super skanky quick hack to use hardware timers of the ESP32
static hw_timer_t *timer;
static PendableFunction tCallback;
static void *tParam1;
static uint32_t tParam2;
static void IRAM_ATTR onTimer()
{
(*tCallback)(tParam1, tParam2);
}
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
{
if (!timer) {
timer = timerBegin(0, 80, true); // one usec per tick (main clock is 80MhZ on ESP32)
assert(timer);
timerAttachInterrupt(timer, &onTimer, true);
}
tCallback = callback;
tParam1 = param1;
tParam2 = param2;
timerAlarmWrite(timer, delayMsec * 1000L, false); // Do not reload, we want it to be a single shot timer
timerRestart(timer);
timerAlarmEnable(timer);
return true;
}
#endif

8
src/OSTimer.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <Arduino.h>
typedef void (*PendableFunction)(void *pvParameter1, uint32_t ulParameter2);
/// Uses a hardware timer, but calls the handler in _interrupt_ context
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec);

View File

@@ -1,6 +1,7 @@
#pragma once
#include "lock.h"
#include <Arduino.h>
#include <cstdint>
#include <unordered_set>
@@ -66,7 +67,13 @@ class PeriodicTask
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
* While zero this task is disabled and will not run
*/
void setPeriod(uint32_t p) { period = p; }
void setPeriod(uint32_t p)
{
lastMsec = millis(); // reset starting from now
period = p;
}
uint32_t getPeriod() const { return period; }
/**
* Syntatic sugar for suspending tasks

View File

@@ -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
}
@@ -87,7 +100,7 @@ static void lsIdle()
static void lsExit()
{
// setGPSPower(true); // restore GPS power
gps.startLock();
gps->startLock();
}
static void nbEnter()
@@ -104,6 +117,12 @@ static void darkEnter()
screen.setOn(false);
}
static void serialEnter()
{
setBluetoothEnable(false);
screen.setOn(true);
}
static void onEnter()
{
screen.setOn(true);
@@ -113,8 +132,9 @@ static void onEnter()
uint32_t now = millis();
if (now - lastPingMs > 60 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
service.sendNetworkPing(NODENUM_BROADCAST, true);
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
if (displayedNodeNum)
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
lastPingMs = now;
}
}
@@ -132,6 +152,7 @@ State stateSDS(sdsEnter, NULL, NULL, "SDS");
State stateLS(lsEnter, lsIdle, lsExit, "LS");
State stateNB(nbEnter, NULL, NULL, "NB");
State stateDARK(darkEnter, NULL, NULL, "DARK");
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
State stateON(onEnter, NULL, NULL, "ON");
Fsm powerFSM(&stateBOOT);
@@ -147,12 +168,20 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake");
// Handle press events
// Handle press events - note: we ignore button presses when in API mode
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers
// Handle critically low power battery by forcing deep sleep
powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateLS, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateNB, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateDARK, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateON, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateSERIAL, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
@@ -165,6 +194,13 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer
powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");

View File

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

View File

@@ -1,4 +1,5 @@
#include "SerialConsole.h"
#include "PowerFSM.h"
#include "configuration.h"
#include <Arduino.h>
@@ -17,20 +18,28 @@ void SerialConsole::init()
{
Port.begin(SERIAL_BAUD);
StreamAPI::init();
emitRebooted();
}
/**
* we override this to notice when we've received a protobuf over the serial stream. Then we shunt off
* debug serial output.
* we override this to notice when we've received a protobuf over the serial
* stream. Then we shunt off debug serial output.
*/
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
// Note: for the time being we could _allow_ debug printing to keep going out the console
// I _think_ this is okay because we currently only print debug msgs from loop() and we are only
// dispatching serial protobuf msgs from loop() as well. When things are more threaded in the future this
// will need to change.
// setDestination(&noopPrint);
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
setDestination(&noopPrint);
canWrite = true;
StreamAPI::handleToRadio(buf, len);
}
/// Hookable to find out when connection changes
void SerialConsole::onConnectionChanged(bool connected)
{
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
} else {
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
}
}

View File

@@ -19,6 +19,17 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
* debug serial output.
*/
virtual void handleToRadio(const uint8_t *buf, size_t len);
virtual size_t write(uint8_t c)
{
if (c == '\n') // prefix any newlines with carriage return
RedirectablePrint::write('\r');
return RedirectablePrint::write(c);
}
protected:
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected);
};
extern SerialConsole console;

45
src/WorkerThread.cpp Normal file
View File

@@ -0,0 +1,45 @@
#include "WorkerThread.h"
#include "debug.h"
#include <assert.h>
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
{
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
assert(r == pdPASS);
}
void Thread::callRun(void *_this)
{
((Thread *)_this)->doRun();
}
void WorkerThread::doRun()
{
while (!wantExit) {
block();
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
meshtastic::printThreadInfo("net");
}
#endif
loop();
}
}
/**
* Notify this thread so it can run
*/
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
{
xTaskNotify(taskHandle, v, action);
}
void NotifiedWorkerThread::block()
{
xTaskNotifyWait(0, // don't clear notification on entry
clearOnRead, &notification, portMAX_DELAY); // Wait forever
}

91
src/WorkerThread.h Normal file
View File

@@ -0,0 +1,91 @@
#include <Arduino.h>
class Thread
{
protected:
TaskHandle_t taskHandle = NULL;
/**
* set this to true to ask thread to cleanly exit asap
*/
volatile bool wantExit = false;
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
virtual ~Thread() { vTaskDelete(taskHandle); }
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
private:
static void callRun(void *_this);
};
/**
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
*
* Use as a mixin base class for the classes you want to convert.
*
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
*/
class WorkerThread : public Thread
{
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
virtual void loop() = 0;
/**
* The method that will be called when start is called.
*/
virtual void doRun();
};
/**
* A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public WorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
{
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
}
protected:
/**
* The notification that was most recently used to wake the thread. Read from loop()
*/
uint32_t notification = 0;
/**
* What notification bits should be cleared just after we read and return them in notification?
*
* Defaults to clear all of them.
*/
uint32_t clearOnRead = UINT32_MAX;
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};

View File

@@ -48,30 +48,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
/// Convert a preprocessor name into a quoted string
#define xstr(s) str(s)
#define str(s) #s
// -----------------------------------------------------------------------------
// OLED
// -----------------------------------------------------------------------------
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
#define SSD1306_ADDRESS 0x3C
#ifdef NRF52840_XXAA // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
// 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
//
// Standard definitions for NRF52 targets
//
// DEBUG LED
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
// We bind to the GPS using variant.h instead for this platform (Serial1)
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
// FIXME, not yet ready for NRF52
#define RTC_DATA_ATTR
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
#define BUTTON_PIN PIN_BUTTON1
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
#else
//
// Standard definitions for ESP32 targets
//
#define GPS_SERIAL_NUM 1
#define GPS_BAUDRATE 9600
#define GPS_RX_PIN 34
#ifdef USE_JTAG
#define GPS_TX_PIN -1
@@ -88,6 +96,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MOSI_GPIO 27
#define NSS_GPIO 18
#endif
// -----------------------------------------------------------------------------
// OLED
// -----------------------------------------------------------------------------
#define SSD1306_ADDRESS 0x3C
// 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
// DEBUG LED
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#define GPS_BAUDRATE 9600
#if defined(TBEAM_V10)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tbeam"
@@ -188,39 +217,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(NRF52840_XXAA) // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#endif
// FIXME, use variant.h defs for all of this!!!
#ifdef ARDUINO_NRF52840_PCA10056
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "nrf52"
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
// We bind to the GPS using variant.h instead for this platform (Serial1)
#undef GPS_RX_PIN
#undef GPS_TX_PIN
// FIXME, not yet ready for NRF52
#define RTC_DATA_ATTR
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
#define BUTTON_PIN PIN_BUTTON1
#define HW_VENDOR "nrf52dk"
// This board uses 0 to be mean LED on
#undef LED_INVERTED
#define LED_INVERTED 1
// Temporarily testing if we can build the RF95 driver for NRF52
// Uncomment to confirm if we can build the RF95 driver for NRF52
#if 0
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#endif
#elif defined(ARDUINO_NRF52840_PPR)
#define HW_VENDOR "ppr"
#endif
@@ -230,17 +252,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define SERIAL_BAUD 921600 // Serial debug baud rate
#ifdef NO_ESP32
#define USE_SEGGER
#endif
#ifdef USE_SEGGER
#include "SEGGER_RTT.h"
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#include "SerialConsole.h"
#define DEBUG_PORT console // Serial debug port
#ifdef NO_ESP32
#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__)
#else
#ifdef DEBUG_PORT
#define DEBUG_MSG(...) DEBUG_PORT.printf(__VA_ARGS__)
#else

View File

@@ -1,65 +1,81 @@
#include "BluetoothUtil.h"
#include "BluetoothSoftwareUpdate.h"
#include "configuration.h"
#include <esp_gatt_defs.h>
#include <BLE2902.h>
#include <Arduino.h>
#include <Update.h>
#include <CRC32.h>
#include "BluetoothUtil.h"
#include "CallbackCharacteristic.h"
#include "RadioLibInterface.h"
#include "configuration.h"
#include "lock.h"
#include <Arduino.h>
#include <BLE2902.h>
#include <CRC32.h>
#include <Update.h>
#include <esp_gatt_defs.h>
using namespace meshtastic;
CRC32 crc;
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
uint32_t updateExpectedSize, updateActualSize;
Lock *updateLock;
class TotalSizeCharacteristic : public CallbackCharacteristic
{
public:
public:
TotalSizeCharacteristic()
: CallbackCharacteristic("e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
: CallbackCharacteristic("e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
{
}
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
LockGuard g(updateLock);
// Check if there is enough to OTA Update
uint32_t len = getValue32(c, 0);
updateExpectedSize = len;
updateActualSize = 0;
crc.reset();
bool canBegin = Update.begin(len);
DEBUG_MSG("Setting update size %u, result %d\n", len, canBegin);
if (!canBegin)
if (!canBegin) {
// Indicate failure by forcing the size to 0
c->setValue(0UL);
else {
uint32_t zero = 0;
c->setValue(zero);
} else {
// This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only
// talk to one service during the sw update.
//DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update.");
//void stopMeshBluetoothService();
//stopMeshBluetoothService();
// DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update.");
// void stopMeshBluetoothService();
// stopMeshBluetoothService();
if (RadioLibInterface::instance)
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
// writing flash - shut the radio off during updates
}
}
};
#define MAX_BLOCKSIZE 512
class DataCharacteristic : public CallbackCharacteristic
{
public:
DataCharacteristic()
: CallbackCharacteristic(
"e272ebac-d463-4b98-bc84-5cc1a39ee517", BLECharacteristic::PROPERTY_WRITE)
{
}
public:
DataCharacteristic() : CallbackCharacteristic("e272ebac-d463-4b98-bc84-5cc1a39ee517", BLECharacteristic::PROPERTY_WRITE) {}
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
LockGuard g(updateLock);
std::string value = c->getValue();
uint32_t len = value.length();
uint8_t *data = c->getData();
assert(len <= MAX_BLOCKSIZE);
static uint8_t
data[MAX_BLOCKSIZE]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
memcpy(data, c->getData(), len);
// DEBUG_MSG("Writing %u\n", len);
crc.update(data, len);
Update.write(data, len);
updateActualSize += len;
}
};
@@ -67,49 +83,44 @@ static BLECharacteristic *resultC;
class CRC32Characteristic : public CallbackCharacteristic
{
public:
CRC32Characteristic()
: CallbackCharacteristic(
"4826129c-c22a-43a3-b066-ce8f0d5bacc6", BLECharacteristic::PROPERTY_WRITE)
{
}
public:
CRC32Characteristic() : CallbackCharacteristic("4826129c-c22a-43a3-b066-ce8f0d5bacc6", BLECharacteristic::PROPERTY_WRITE) {}
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
LockGuard g(updateLock);
uint32_t expectedCRC = getValue32(c, 0);
uint32_t actualCRC = crc.finalize();
DEBUG_MSG("expected CRC %u\n", expectedCRC);
uint8_t result = 0xff;
// Check the CRC before asking the update to happen.
if (crc.finalize() != expectedCRC)
if (updateActualSize != updateExpectedSize) {
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
result = 0xe1; // FIXME, use real error codes
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
{
DEBUG_MSG("Invalid CRC!\n");
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
result = 0xe0; // FIXME, use real error codes
}
else
{
if (Update.end())
{
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
}
else
{
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
result = Update.getError();
}
if (RadioLibInterface::instance)
RadioLibInterface::instance->startReceive(); // Resume radio
assert(resultC);
resultC->setValue(&result, 1);
resultC->notify();
}
};
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec)
@@ -122,11 +133,15 @@ See bluetooth-api.md
*/
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
{
if (!updateLock)
updateLock = new Lock();
// Create the BLE Service
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
assert(!resultC);
resultC = new BLECharacteristic("5e134862-7411-4424-ac4a-210937432c77", BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
resultC = new BLECharacteristic("5e134862-7411-4424-ac4a-210937432c77",
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
addWithDesc(service, new TotalSizeCharacteristic, "total image size");
addWithDesc(service, new DataCharacteristic, "data");
@@ -135,7 +150,8 @@ BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::st
resultC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
BLECharacteristic *swC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
BLECharacteristic *swC =
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
swC->setValue(swVersion);
service->addCharacteristic(addBLECharacteristic(swC));
@@ -143,7 +159,8 @@ BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::st
mfC->setValue(hwVendor);
service->addCharacteristic(addBLECharacteristic(mfC));
BLECharacteristic *hwvC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
BLECharacteristic *hwvC =
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
hwvC->setValue(hwVersion);
service->addCharacteristic(addBLECharacteristic(hwvC));

View File

@@ -1,8 +1,11 @@
#pragma once
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
BLEService *createUpdateService(BLEServer* server, std::string hwVendor, std::string swVersion, std::string hwVersion);
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion);
void destroyUpdateService();
void bluetoothRebootCheck();

View File

@@ -8,53 +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;
@@ -106,6 +59,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;
/**
@@ -184,7 +185,7 @@ class MySecurity : public BLESecurityCallbacks
void onPassKeyNotify(uint32_t pass_key)
{
Serial.printf("onPassKeyNotify %u\n", pass_key);
Serial.printf("onPassKeyNotify %06u\n", pass_key);
startCb(pass_key);
}
@@ -223,11 +224,15 @@ void deinitBLE()
pServer->getAdvertising()->stop();
destroyUpdateService();
if (pUpdate != NULL) {
destroyUpdateService();
pUpdate->stop(); // we delete them below
pUpdate->executeDelete();
}
pUpdate->stop();
pDevInfo->stop();
pUpdate->stop(); // we delete them below
pDevInfo->executeDelete();
// First shutdown bluetooth
BLEDevice::deinit(false);
@@ -235,14 +240,16 @@ 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 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++)
@@ -276,15 +283,19 @@ BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoo
// We now let users create the battery service only if they really want (not all devices have a battery)
// BLEService *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

View File

@@ -0,0 +1,12 @@
#pragma once
#include "BLECharacteristic.h"
#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this
/**
* A characterstic with a set of overridable callbacks
*/
class CallbackCharacteristic : public BLECharacteristic, public BLECharacteristicCallbacks
{
public:
CallbackCharacteristic(const char *uuid, uint32_t btprops) : BLECharacteristic(uuid, btprops) { setCallbacks(this); }
};

View File

@@ -0,0 +1,83 @@
#include "CryptoEngine.h"
#include "configuration.h"
#include "crypto/includes.h"
#include "crypto/common.h"
// #include "esp_system.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "mbedtls/aes.h"
class ESP32CryptoEngine : public CryptoEngine
{
mbedtls_aes_context aes;
/// How many bytes in our key
uint8_t keySize = 0;
public:
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); }
/**
* Set the key used for encrypt, decrypt.
*
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
*
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(size_t numBytes, uint8_t *bytes)
{
keySize = numBytes;
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8);
if (numBytes != 0) {
auto res = mbedtls_aes_setkey_enc(&aes, bytes, numBytes * 8);
assert(!res);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
{
if (keySize != 0) {
uint8_t stream_block[16];
static uint8_t scratch[MAX_BLOCKSIZE];
size_t nc_off = 0;
// DEBUG_MSG("ESP32 encrypt!\n");
initNonce(fromNode, packetNum);
assert(numBytes <= MAX_BLOCKSIZE);
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes);
assert(!res);
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
{
// DEBUG_MSG("ESP32 decrypt!\n");
// For CTR, the implementation is the same
encrypt(fromNode, packetNum, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new ESP32CryptoEngine();

View File

@@ -23,9 +23,6 @@ static CallbackCharacteristic *meshFromNumCharacteristic;
BLEService *meshService;
// If defined we will also support the old API
#define SUPPORT_OLD_BLE_API
class BluetoothPhoneAPI : public PhoneAPI
{
/**
@@ -44,150 +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)
{
BLEKeepAliveCallbacks::onRead(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)
{
BLEKeepAliveCallbacks::onWrite(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);
}
};
#ifdef SUPPORT_OLD_BLE_API
class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks
{
public:
NodeInfoCharacteristic()
: BLECharacteristic("d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
{
setCallbacks(this);
}
void onRead(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onRead(c);
const NodeInfo *info = nodeDB.readNextInfo();
if (info) {
DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id,
info->user.long_name);
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), NodeInfo_fields, info);
c->setValue(trBytes, numbytes);
} else {
c->setValue(trBytes, 0); // Send an empty response
DEBUG_MSG("Done sending nodeinfos\n");
}
}
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
DEBUG_MSG("Reset nodeinfo read pointer\n");
nodeDB.resetReadPointer();
}
};
// wrap our protobuf version with something that forces the service to reload the config
class RadioCharacteristic : public ProtobufCharacteristic
{
public:
RadioCharacteristic()
: ProtobufCharacteristic("b56786c8-839a-44a1-b98e-a1724c4a0262",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, RadioConfig_fields,
&radioConfig)
{
}
void onRead(BLECharacteristic *c)
{
DEBUG_MSG("Reading radio config, sdsecs %u\n", radioConfig.preferences.sds_secs);
ProtobufCharacteristic::onRead(c);
}
void onWrite(BLECharacteristic *c)
{
DEBUG_MSG("Writing radio config\n");
ProtobufCharacteristic::onWrite(c);
bluetoothPhoneAPI->handleSetRadio(radioConfig);
}
};
// wrap our protobuf version with something that forces the service to reload the owner
class OwnerCharacteristic : public ProtobufCharacteristic
{
public:
OwnerCharacteristic()
: ProtobufCharacteristic("6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ, User_fields, &owner)
{
}
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(
c); // NOTE: We do not call the standard ProtobufCharacteristic superclass, because we want custom write behavior
static User o; // if the phone doesn't set ID we are careful to keep ours, we also always keep our macaddr
if (writeToDest(c, &o)) {
bluetoothPhoneAPI->handleSetOwner(o);
}
}
};
class MyNodeInfoCharacteristic : public ProtobufCharacteristic
{
public:
MyNodeInfoCharacteristic()
: ProtobufCharacteristic("ea9f3f82-8dc4-4733-9452-1f6da28892a2", BLECharacteristic::PROPERTY_READ, MyNodeInfo_fields,
&myNodeInfo)
{
}
void onRead(BLECharacteristic *c)
{
// update gps connection state
myNodeInfo.has_gps = gps.isConnected;
ProtobufCharacteristic::onRead(c);
myNodeInfo.error_code = 0; // The phone just read us, so throw it away
myNodeInfo.error_address = 0;
}
};
#endif
class ToRadioCharacteristic : public CallbackCharacteristic
{
@@ -196,9 +49,6 @@ class ToRadioCharacteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
DEBUG_MSG("Got on write\n");
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
}
};
@@ -212,7 +62,6 @@ class FromRadioCharacteristic : public CallbackCharacteristic
void onRead(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onRead(c);
size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes);
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
@@ -236,11 +85,7 @@ class FromNumCharacteristic : public CallbackCharacteristic
// observe(&service.fromNumChanged);
}
void onRead(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onRead(c);
DEBUG_MSG("FIXME implement fromnum read\n");
}
void onRead(BLECharacteristic *c) { DEBUG_MSG("FIXME implement fromnum read\n"); }
};
/*
@@ -263,12 +108,6 @@ BLEService *createMeshBluetoothService(BLEServer *server)
addWithDesc(service, meshFromNumCharacteristic, "fromRadio");
addWithDesc(service, new ToRadioCharacteristic, "toRadio");
addWithDesc(service, new FromRadioCharacteristic, "fromNum");
#ifdef SUPPORT_OLD_BLE_API
addWithDesc(service, new MyNodeInfoCharacteristic, "myNode");
addWithDesc(service, new RadioCharacteristic, "radio");
addWithDesc(service, new OwnerCharacteristic, "owner");
addWithDesc(service, new NodeInfoCharacteristic, "nodeinfo");
#endif
meshFromNumCharacteristic->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
@@ -292,6 +131,7 @@ void stopMeshBluetoothService()
{
assert(meshService);
meshService->stop();
meshService->executeDelete();
}
void destroyMeshBluetoothService()

View File

@@ -22,8 +22,8 @@ void reinitBluetooth()
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
screen.startBluetoothPinScreen(pin);
},
[]() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, xstr(APP_VERSION),
xstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
[]() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, optstr(APP_VERSION),
optstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
createMeshBluetoothService(serve);
// Start advertising - this must be done _after_ creating all services
@@ -119,16 +119,8 @@ void axp192Init()
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
#if 0
// cribbing from https://github.com/m5stack/M5StickC/blob/master/src/AXP192.cpp to fix charger to be more like 300ms.
// I finally found an english datasheet. Will look at this later - but suffice it to say the default code from TTGO has 'issues'
axp.adc1Enable(0xff, 1); // turn on all adcs
uint8_t val = 0xc2;
axp._writeByte(0x33, 1, &val); // Bat charge voltage to 4.2, Current 280mA
val = 0b11110010;
// Set ADC sample rate to 200hz
// axp._writeByte(0x84, 1, &val);
// Not connected
//val = 0xfc;
@@ -164,6 +156,15 @@ void axp192Init()
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());
#ifdef AXP192_SLAVE_ADDRESS
axp192Init();
#endif
@@ -189,6 +190,16 @@ 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()
{
@@ -229,5 +240,10 @@ void esp32Loop()
readPowerStatus();
axp.clearIRQ();
}
if (powerStatus.haveBattery && !powerStatus.usb &&
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) // If we have a battery at all and it is less than 10% full, force deep sleep
powerFSM.trigger(EVENT_LOW_BATTERY);
#endif // T_BEAM_V10
}

81
src/gps/GPS.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include "GPS.h"
#include "configuration.h"
#include "time.h"
#include <assert.h>
#include <sys/time.h>
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
HardwareSerial &GPS::_serial_gps = _serial_gps_real;
#else
// Assume NRF52
HardwareSerial &GPS::_serial_gps = Serial1;
#endif
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
GPS *gps;
// stuff that really should be in in the instance instead...
static uint32_t
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
void readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpfull here too*/
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
}
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void perhapsSetRTC(const struct timeval *tv)
{
if (!timeSetFromGPS) {
timeSetFromGPS = true;
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
#ifndef NO_ESP32
settimeofday(tv, NULL);
#else
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
#endif
readFromRTC();
}
}
void perhapsSetRTC(struct tm &t)
{
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
time_t res = mktime(&t);
struct timeval tv;
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300)
DEBUG_MSG("Ignoring invalid GPS time\n");
else
perhapsSetRTC(&tv);
}
#include <time.h>
uint32_t getTime()
{
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t getValidTime()
{
return timeSetFromGPS ? getTime() : 0;
}

55
src/gps/GPS.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include "Observer.h"
#include "PeriodicTask.h"
#include "sys/time.h"
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void perhapsSetRTC(const struct timeval *tv);
void perhapsSetRTC(struct tm &t);
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
uint32_t getTime();
/// Return time since 1970 in secs. If we don't have a GPS lock return zero
uint32_t getValidTime();
void readFromRTC();
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class GPS : public Observable<void *>
{
protected:
bool hasValidLocation = false; // default to false, until we complete our first read
static HardwareSerial &_serial_gps;
public:
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
int32_t altitude = 0;
bool isConnected = false; // Do we have a GPS we are talking to
virtual ~GPS() {}
/**
* Returns true if we succeeded
*/
virtual bool setup() { return true; }
/// A loop callback for subclasses that need it. FIXME, instead just block on serial reads
virtual void loop() {}
/// Returns ture if we have acquired GPS lock.
bool hasLock() const { return hasValidLocation; }
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock() {}
};
extern GPS *gps;

65
src/gps/NEMAGPS.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "NEMAGPS.h"
#include "configuration.h"
static int32_t toDegInt(RawDegrees d)
{
int32_t degMult = 10000000; // 1e7
int32_t r = d.deg * degMult + d.billionths / 100;
if (d.negative)
r *= -1;
return r;
}
void NEMAGPS::loop()
{
while (_serial_gps.available() > 0) {
int c = _serial_gps.read();
// Serial.write(c);
reader.encode(c);
}
uint32_t now = millis();
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
// serial chars at whatever rate)
lastUpdateMsec = now;
auto ti = reader.time;
auto d = reader.date;
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
struct tm t;
t.tm_sec = ti.second();
t.tm_min = ti.minute();
t.tm_hour = ti.hour();
t.tm_mday = d.day();
t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
perhapsSetRTC(t);
isConnected = true; // we seem to have a real GPS (but not necessarily a lock)
}
if (reader.location.isUpdated()) {
if (reader.altitude.isValid())
altitude = reader.altitude.meters();
if (reader.location.isValid()) {
auto loc = reader.location.value();
latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng);
}
// 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);
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
if (hasValidLocation)
notifyObservers(NULL);
}
}
}

21
src/gps/NEMAGPS.h Normal file
View File

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

139
src/gps/UBloxGPS.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include "UBloxGPS.h"
#include "sleep.h"
#include <assert.h>
UBloxGPS::UBloxGPS() : PeriodicTask()
{
notifySleepObserver.observe(&notifySleep);
}
bool UBloxGPS::setup()
{
#ifdef GPS_RX_PIN
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps.begin(GPS_BAUDRATE);
#endif
// _serial_gps.setRxBufferSize(1024); // the default is 256
// ublox.enableDebugging(Serial);
// note: the lib's implementation has the wrong docs for what the return val is
// it is not a bool, it returns zero for success
isConnected = ublox.begin(_serial_gps);
// try a second time, the ublox lib serial parsing is buggy?
if (!isConnected)
isConnected = ublox.begin(_serial_gps);
if (isConnected) {
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
bool factoryReset = false;
bool ok;
if (factoryReset) {
// It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have
// GPS_TX connected)
ublox.factoryReset();
delay(3000);
isConnected = ublox.begin(_serial_gps);
DEBUG_MSG("Factory reset success=%d\n", isConnected);
ok = ublox.saveConfiguration(3000);
assert(ok);
return false;
} else {
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
assert(ok);
ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low
assert(ok);
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
// assert(ok);
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
// assert(ok);
ok = ublox.powerSaveMode(true, 2000); // use power save mode, the default timeout (1100ms seems a bit too tight)
assert(ok);
}
ok = ublox.saveConfiguration(3000);
assert(ok);
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
return true;
} else {
return false;
}
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int UBloxGPS::prepareSleep(void *unused)
{
if (isConnected)
ublox.powerOff();
return 0;
}
void UBloxGPS::doTask()
{
uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix
assert(isConnected);
// Consume all characters that have arrived
// getPVT automatically calls checkUblox
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
// If we don't have a fix (a quick check), don't try waiting for a solution)
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
// turn off for now
// fixtype = ublox.getFixType();
// DEBUG_MSG("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()) {
/* 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_isdst = false;
perhapsSetRTC(t);
}
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // 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);
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
if (hasValidLocation) {
wantNewLocation = false;
notifyObservers(NULL);
// ublox.powerOff();
}
} else // we didn't get a location update, go back to sleep and hope the characters show up
wantNewLocation = true;
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
// the serial
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
}
void UBloxGPS::startLock()
{
DEBUG_MSG("Looking for GPS lock\n");
wantNewLocation = true;
setPeriod(1);
}

41
src/gps/UBloxGPS.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include "GPS.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "SparkFun_Ublox_Arduino_Library.h"
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class UBloxGPS : public GPS, public PeriodicTask
{
SFE_UBLOX_GPS ublox;
bool wantNewLocation = true;
CallbackObserver<UBloxGPS, void *> notifySleepObserver = CallbackObserver<UBloxGPS, void *>(this, &UBloxGPS::prepareSleep);
public:
UBloxGPS();
/**
* Returns true if we succeeded
*/
virtual bool setup();
virtual void doTask();
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock();
private:
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused);
};

View File

@@ -21,18 +21,19 @@
*/
#include "GPS.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NEMAGPS.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "PowerFSM.h"
#include "Router.h"
#include "UBloxGPS.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
// #include "rom/rtc.h"
#include "FloodingRouter.h"
#include "DSRRouter.h"
#include "debug.h"
#include "main.h"
#include "screen.h"
#include "sleep.h"
@@ -52,7 +53,7 @@ meshtastic::PowerStatus powerStatus;
bool ssd1306_found;
bool axp192_found;
FloodingRouter realRouter;
DSRRouter realRouter;
Router &router = realRouter; // Users of router don't care what sort of subclass implements that API
// -----------------------------------------------------------------------------
@@ -103,8 +104,6 @@ const char *getDeviceName()
return name;
}
static MeshRadio *radio = NULL;
static uint32_t ledBlinker()
{
static bool ledOn;
@@ -118,6 +117,13 @@ static uint32_t ledBlinker()
Periodic ledPeriodic(ledBlinker);
#include "RF95Interface.h"
#include "SX1262Interface.h"
#ifdef NO_ESP32
#include "variant.h"
#endif
void setup()
{
#ifdef USE_SEGGER
@@ -161,7 +167,7 @@ void setup()
ledPeriodic.setup();
// Hello
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", xstr(APP_VERSION), xstr(HW_VERSION));
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
#ifndef NO_ESP32
// Don't init display if we don't have one or we are waking headless due to a timer event
@@ -181,19 +187,57 @@ void setup()
screen.print("Started...\n");
// Init GPS
gps.setup();
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
// If we know we have a L80 GPS, don't try UBLOX
#ifndef L80_RESET
// Init GPS - first try ublox
gps = new UBloxGPS();
if (!gps->setup()) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NEMA at 9600 baud.
DEBUG_MSG("ERROR: No UBLOX GPS found, hoping that NEMA might work\n");
delete gps;
gps = new NEMAGPS();
gps->setup();
}
#else
gps = new NEMAGPS();
gps->setup();
#endif
service.init();
realRouter.setup(); // required for our periodic task (kinda skanky FIXME)
#ifdef SX1262_ANT_SW
// make analog PA vs not PA switch on SX1262 eval board work properly
pinMode(SX1262_ANT_SW, OUTPUT);
digitalWrite(SX1262_ANT_SW, 1);
#endif
// Init our SPI controller
#ifdef NRF52_SERIES
SPI.begin();
#else
// ESP32
SPI.begin(SCK_GPIO, MISO_GPIO, MOSI_GPIO, NSS_GPIO);
SPI.setFrequency(4000000);
#endif
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
radio = new MeshRadio();
router.addInterface(&radio->radioIf);
RadioInterface *rIf =
#if defined(RF95_IRQ_GPIO)
// new CustomRF95(); old Radiohead based driver
new RF95Interface(NSS_GPIO, RF95_IRQ_GPIO, RESET_GPIO, SPI);
#elif defined(SX1262_CS)
new SX1262Interface(SX1262_CS, SX1262_DIO1, SX1262_RESET, SX1262_BUSY, SPI);
#else
new SimRadio();
#endif
if (radio && !radio->init())
if (!rIf->init())
recordCriticalError(ErrNoRadio);
else
router.addInterface(rIf);
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
@@ -227,6 +271,7 @@ void loop()
{
uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop?
gps->loop(); // FIXME, remove from main, instead block on read
router.loop();
powerFSM.run_machine();
service.loop();
@@ -238,6 +283,8 @@ 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
@@ -270,12 +317,20 @@ 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() ? "ok" : ":(");
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.

32
src/mesh/CryptoEngine.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "CryptoEngine.h"
#include "configuration.h"
void CryptoEngine::setKey(size_t numBytes, uint8_t *bytes)
{
DEBUG_MSG("WARNING: Using stub crypto - all crypto is sent in plaintext!\n");
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
{
DEBUG_MSG("WARNING: noop encryption!\n");
}
void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
{
DEBUG_MSG("WARNING: noop decryption!\n");
}
/**
* Init our 128 bit nonce for a new packet
*/
void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetNum)
{
memset(nonce, 0, sizeof(nonce));
*((uint64_t *)&nonce[0]) = packetNum;
*((uint32_t *)&nonce[8]) = fromNode;
}

52
src/mesh/CryptoEngine.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <Arduino.h>
/**
* see docs/software/crypto.md for details.
*
*/
#define MAX_BLOCKSIZE 256
class CryptoEngine
{
protected:
/** Our per packet nonce */
uint8_t nonce[16];
public:
virtual ~CryptoEngine() {}
/**
* Set the key used for encrypt, decrypt.
*
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
*
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(size_t numBytes, uint8_t *bytes);
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes);
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes);
protected:
/**
* Init our 128 bit nonce for a new packet
*
* The NONCE is constructed by concatenating (from MSB to LSB):
* a 64 bit packet number (stored in little endian order)
* a 32 bit sending node number (stored in little endian order)
* a 32 bit block counter (starts at zero)
*/
void initNonce(uint32_t fromNode, uint64_t packetNum);
};
extern CryptoEngine *crypto;

245
src/mesh/DSRRouter.cpp Normal file
View File

@@ -0,0 +1,245 @@
#include "DSRRouter.h"
#include "configuration.h"
/* when we receive any packet
- sniff and update tables (especially useful to find adjacent nodes). Update user, network and position info.
- if we need to route() that packet, resend it to the next_hop based on our nodedb.
- if it is broadcast or destined for our node, deliver locally
- handle routereply/routeerror/routediscovery messages as described below
- then free it
routeDiscovery
- if we've already passed through us (or is from us), then it ignore it
- use the nodes already mentioned in the request to update our routing table
- if they were looking for us, send back a routereply
- if max_hops is zero and they weren't looking for us, drop (FIXME, send back error - I think not though?)
- if we receive a discovery packet, we use it to populate next_hop (if needed) towards the requester (after decrementing max_hops)
- if we receive a discovery packet, and we have a next_hop in our nodedb for that destination we send a (reliable) we send a route
reply towards the requester
when sending any reliable packet
- if timeout doing retries, send a routeError (nak) message back towards the original requester. all nodes eavesdrop on that
packet and update their route caches.
when we receive a routereply packet
- update next_hop on the node, if the new reply needs fewer hops than the existing one (we prefer shorter paths). fixme, someday
use a better heuristic
when we receive a routeError packet
- delete the route for that failed recipient, restartRouteDiscovery()
- if we receive routeerror in response to a discovery,
- fixme, eventually keep caches of possible other routes.
*/
ErrorCode DSRRouter::send(MeshPacket *p)
{
// We only consider multihop routing packets (i.e. those with dest set)
if (p->decoded.dest) {
// add an entry for this pending message
auto pending = startRetransmission(p);
// FIXME - when acks come in for this packet, we should _not_ delete the record unless the ack was from
// the final dest. We need to keep that record around until FIXME
// Also we should not retransmit multihop entries in that table at all
// If we have an entry in our routing tables, just send it, otherwise start a route discovery
NodeNum nextHop = getNextHop(p->decoded.dest);
if (nextHop) {
sendNextHop(nextHop, p); // start a reliable single hop send
} else {
pending->wantRoute = true;
// start discovery, but only if we don't already a discovery in progress for that node number
startDiscovery(p->decoded.dest);
}
return ERRNO_OK;
} else
return ReliableRouter::send(p);
}
void DSRRouter::sniffReceived(const MeshPacket *p)
{
// Learn 0 hop routes by just hearing any adjacent nodes
// But treat broadcasts carefully, because when flood broadcasts go out they keep the same original "from". So we want to
// ignore rebroadcasts.
// this will also add records for any ACKs we receive for our messages
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
}
switch (p->decoded.which_payload) {
case SubPacket_route_request_tag:
// Handle route discovery packets (will be a broadcast message)
// FIXME - always start request with the senders nodenum
if (weAreInRoute(p->decoded.route_request)) {
DEBUG_MSG("Ignoring a route request that contains us\n");
} else {
updateRoutes(p->decoded.route_request,
true); // Update our routing tables based on the route that came in so far on this request
if (p->decoded.dest == getNodeNum()) {
// They were looking for us, send back a route reply (the sender address will be first in the list)
sendRouteReply(p->decoded.route_request);
} else {
// They were looking for someone else, forward it along (as a zero hop broadcast)
NodeNum nextHop = getNextHop(p->decoded.dest);
if (nextHop) {
// in our route cache, reply to the requester (the sender address will be first in the list)
sendRouteReply(p->decoded.route_request, nextHop);
} else {
// Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
resendRouteRequest(p);
}
}
}
break;
case SubPacket_route_reply_tag:
updateRoutes(p->decoded.route_reply, false);
// FIXME, if any of our current pending packets were waiting for this route, send them (and leave them as regular pending
// packets until ack arrives)
// FIXME, if we don't get a route reply at all (or a route error), timeout and generate a routeerror TIMEOUT on our own...
break;
case SubPacket_route_error_tag:
removeRoute(p->decoded.dest);
// FIXME: if any pending packets were waiting on this route, delete them
break;
default:
break;
}
// We simply ignore ACKs - because ReliableRouter will delete the pending packet for us
// Handle regular packets
if (p->to == getNodeNum()) { // Destined for us (at least for this hop)
// We need to route this packet to some other node
if (p->decoded.dest && p->decoded.dest != p->to) {
// if we have a route out, resend the packet to the next hop, otherwise return RouteError no-route available
NodeNum nextHop = getNextHop(p->decoded.dest);
if (nextHop) {
sendNextHop(nextHop, p); // start a reliable single hop send
} else {
// We don't have a route out
assert(p->decoded.source); // I think this is guaranteed by now
// FIXME - what if the current packet _is_ a route error packet?
sendRouteError(p, RouteError_NO_ROUTE);
}
// FIXME, stop local processing of this packet
}
// handle naks - convert them to route error packets
// All naks are generated locally, because we failed resending the packet too many times
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
if (nakId) {
auto pending = findPendingPacket(p->to, nakId);
if (pending && pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore
removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node
sendRouteError(p, RouteError_GOT_NAK);
}
}
}
return ReliableRouter::sniffReceived(p);
}
/**
* Does our node appear in the specified route
*/
bool DSRRouter::weAreInRoute(const RouteDiscovery &route)
{
return true; // FIXME
}
/**
* Given a DSR route, use that route to update our DB of possible routes
*
* Note: routes are always listed in the same order - from sender to receipient (i.e. route_replies also use this some order)
*
* @param isRequest is true if we are looking at a route request, else we are looking at a reply
**/
void DSRRouter::updateRoutes(const RouteDiscovery &route, bool isRequest)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* send back a route reply (the sender address will be first in the list)
*/
void DSRRouter::sendRouteReply(const RouteDiscovery &route, NodeNum toAppend)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* Given a nodenum return the next node we should forward to if we want to reach that node.
*
* @return 0 if no route found
*/
NodeNum DSRRouter::getNextHop(NodeNum dest)
{
DEBUG_MSG("FIXME not implemented");
return 0;
}
/** Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
*
* We will bump down hop_limit in this call.
*/
void DSRRouter::resendRouteRequest(const MeshPacket *p)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* Record that forwarder can reach dest for us, but they will need numHops to get there.
* If our routing tables already have something that can reach that node in fewer hops we will keep the existing route
* instead.
*/
void DSRRouter::addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* Record that we no longer have a route to the dest
*/
void DSRRouter::removeRoute(NodeNum dest)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* Forward the specified packet to the specified node
*/
void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
{
DEBUG_MSG("FIXME not implemented");
}
/**
* Send a route error packet towards whoever originally sent this message
*/
void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err)
{
DEBUG_MSG("FIXME not implemented");
}
/** make a copy of p, start discovery, but only if we don't
* already a discovery in progress for that node number. Caller has already scheduled this message for retransmission
* when the discovery is complete.
*/
void DSRRouter::startDiscovery(NodeNum dest)
{
DEBUG_MSG("FIXME not implemented");
}

80
src/mesh/DSRRouter.h Normal file
View File

@@ -0,0 +1,80 @@
#include "ReliableRouter.h"
class DSRRouter : public ReliableRouter
{
protected:
/**
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
*/
virtual void sniffReceived(const MeshPacket *p);
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
virtual ErrorCode send(MeshPacket *p);
private:
/**
* Does our node appear in the specified route
*/
bool weAreInRoute(const RouteDiscovery &route);
/**
* Given a DSR route, use that route to update our DB of possible routes
*
* Note: routes are always listed in the same order - from sender to receipient (i.e. route_replies also use this some order)
*
* @param isRequest is true if we are looking at a route request, else we are looking at a reply
**/
void updateRoutes(const RouteDiscovery &route, bool isRequest);
/**
* send back a route reply (the sender address will be first in the list)
*/
void sendRouteReply(const RouteDiscovery &route, NodeNum toAppend = 0);
/**
* Given a nodenum return the next node we should forward to if we want to reach that node.
*
* @return 0 if no route found
*/
NodeNum getNextHop(NodeNum dest);
/** Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
*
* We will bump down hop_limit in this call.
*/
void resendRouteRequest(const MeshPacket *p);
/**
* Record that forwarder can reach dest for us, but they will need numHops to get there.
* If our routing tables already have something that can reach that node in fewer hops we will keep the existing route
* instead.
*/
void addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops);
/**
* Record that we no longer have a route to the dest
*/
void removeRoute(NodeNum dest);
/**
* Forward the specified packet to the specified node
*/
void sendNextHop(NodeNum n, const MeshPacket *p);
/**
* Send a route error packet towards whoever originally sent this message
*/
void sendRouteError(const MeshPacket *p, RouteError err);
/** make a copy of p, start discovery, but only if we don't
* already a discovery in progress for that node number. Caller has already scheduled this message for retransmission
* when the discovery is complete.
*/
void startDiscovery(NodeNum dest);
};

View File

@@ -0,0 +1,54 @@
#include "FloodingRouter.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
FloodingRouter::FloodingRouter() {}
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
ErrorCode FloodingRouter::send(MeshPacket *p)
{
// Add any messages _we_ send to the seen message list (so we will ignore all retransmissions we see)
wasSeenRecently(p); // FIXME, move this to a sniffSent method
return Router::send(p);
}
bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
{
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
DEBUG_MSG("Ignoring incoming msg, because we've already seen it: fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to,
p->id, p->hop_limit);
return true;
}
return Router::shouldFilterReceived(p);
}
void FloodingRouter::sniffReceived(const MeshPacket *p)
{
// If a broadcast, possibly _also_ send copies out into the mesh.
// (FIXME, do something smarter than naive flooding here)
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0) {
if (p->id != 0) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
tosend->hop_limit--; // bump down the hop count
DEBUG_MSG("Rebroadcasting received floodmsg to neighbors, fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to,
p->id, tosend->hop_limit);
// Note: we are careful to resend using the original senders node id
// We are careful not to call our hooked version of send() - because we don't want to check this again
Router::send(tosend);
} else {
DEBUG_MSG("Ignoring a simple (0 id) broadcast\n");
}
}
// handle the packet as normal
Router::sniffReceived(p);
}

View File

@@ -1,17 +1,8 @@
#pragma once
#include "PacketHistory.h"
#include "PeriodicTask.h"
#include "Router.h"
#include <vector>
/**
* A record of a recent message broadcast
*/
struct BroadcastRecord {
NodeNum sender;
PacketId id;
uint32_t rxTimeMsec; // Unix time in msecs - the time we received it
};
/**
* This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense)
@@ -36,19 +27,9 @@ struct BroadcastRecord {
Any entries in recentBroadcasts that are older than X seconds (longer than the
max time a flood can take) will be discarded.
*/
class FloodingRouter : public Router, public PeriodicTask
class FloodingRouter : public Router, protected PacketHistory
{
private:
/** FIXME: really should be a std::unordered_set with the key being sender,id.
* This would make checking packets in wasSeenRecently faster.
*/
std::vector<BroadcastRecord> recentBroadcasts;
/**
* Packets we've received that we need to resend after a short delay
*/
PointerQueue<MeshPacket> toResend;
public:
/**
* Constructor
@@ -65,19 +46,15 @@ class FloodingRouter : public Router, public PeriodicTask
protected:
/**
* Called from loop()
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
* Should this incoming filter be dropped?
*
* Note: this method will free the provided packet
* Called immedately on receiption, before any further processing.
* @return true to abandon the packet
*/
virtual void handleReceived(MeshPacket *p);
virtual bool shouldFilterReceived(const MeshPacket *p);
virtual void doTask();
private:
/**
* Update recentBroadcasts and return true if we have already seen this packet
* Look for broadcasts we need to rebroadcast
*/
bool wasSeenRecently(const MeshPacket *p);
virtual void sniffReceived(const MeshPacket *p);
};

View File

@@ -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 &&
@@ -78,4 +115,9 @@ 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
}
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); }
};

View File

@@ -1,125 +0,0 @@
#include "error.h"
#include <SPI.h>
#include <assert.h>
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
#include "sleep.h"
#include <pb_decode.h>
#include <pb_encode.h>
/**
* ## LoRaWAN for North America
LoRaWAN defines 64, 125 kHz channels from 902.3 to 914.9 MHz increments.
The maximum output power for North America is +30 dBM.
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are
separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency.
*/
/// Sometimes while debugging it is useful to set this false, to disable rf95 accesses
bool useHardware = true;
MeshRadio::MeshRadio() // , manager(radioIf)
{
myNodeInfo.num_channels = NUM_CHANNELS;
// Can't print strings this early - serial not setup yet
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
}
bool MeshRadio::init()
{
if (!useHardware)
return true;
DEBUG_MSG("Starting meshradio init...\n");
configChangedObserver.observe(&service.configChanged);
preflightSleepObserver.observe(&preflightSleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
#ifdef RESET_GPIO
pinMode(RESET_GPIO, OUTPUT); // Deassert reset
digitalWrite(RESET_GPIO, HIGH);
// pulse reset
digitalWrite(RESET_GPIO, LOW);
delay(10);
digitalWrite(RESET_GPIO, HIGH);
delay(10);
#endif
radioIf.setThisAddress(
nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor time.
if (!radioIf.init()) {
DEBUG_MSG("LoRa radio init failed\n");
DEBUG_MSG("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info\n");
return false;
}
// not needed - defaults on
// rf95.setPayloadCRC(true);
reloadConfig();
return true;
}
/** hash a string into an integer
*
* djb2 by Dan Bernstein.
* http://www.cse.yorku.ca/~oz/hash.html
*/
unsigned long hash(char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++) != 0)
hash = ((hash << 5) + hash) + (unsigned char)c; /* hash * 33 + c */
return hash;
}
int MeshRadio::reloadConfig(void *unused)
{
radioIf.setModeIdle(); // Need to be idle before doing init
// Set up default configuration
// No Sync Words in LORA mode.
radioIf.setModemConfig(
(RH_RF95::ModemConfigChoice)channelSettings.modem_config); // Radio default
// setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
// rf95.setPreambleLength(8); // Default is 8
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
int channel_num = hash(channelSettings.name) % NUM_CHANNELS;
float center_freq = CH0 + CH_SPACING * channel_num;
if (!radioIf.setFrequency(center_freq)) {
DEBUG_MSG("setFrequency failed\n");
assert(0); // fixme panic
}
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 5 to 23 dBm:
// FIXME - can we do this? It seems to be in the Heltec board.
radioIf.setTxPower(channelSettings.tx_power, false);
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, txpower=%d\n", channelSettings.name, channelSettings.modem_config,
channel_num, channelSettings.tx_power);
// Done with init tell radio to start receiving
radioIf.setModeRx();
return 0;
}

View File

@@ -1,9 +1,7 @@
#pragma once
#include "CustomRF95.h"
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "configuration.h"
#include "mesh.pb.h"
@@ -61,47 +59,3 @@
#define NUM_CHANNELS NUM_CHANNELS_US
#endif
/**
* A raw low level interface to our mesh. Only understands nodenums and bytes (not protobufs or node ids)
*/
class MeshRadio
{
public:
// Kinda ugly way of selecting different radio implementations, but soon this MeshRadio class will be going away
// entirely. At that point we can make things pretty.
#ifdef RF95_IRQ_GPIO
CustomRF95
radioIf; // the raw radio interface - for now I'm leaving public - because this class is shrinking to be almost nothing
#else
SimRadio radioIf;
#endif
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
MeshRadio();
bool init();
private:
CallbackObserver<MeshRadio, void *> configChangedObserver =
CallbackObserver<MeshRadio, void *>(this, &MeshRadio::reloadConfig);
CallbackObserver<MeshRadio, void *> preflightSleepObserver =
CallbackObserver<MeshRadio, void *>(this, &MeshRadio::preflightSleepCb);
CallbackObserver<MeshRadio, void *> notifyDeepSleepObserver =
CallbackObserver<MeshRadio, void *>(this, &MeshRadio::notifyDeepSleepDb);
/// The radioConfig object just changed, call this to force the hw to change to the new settings
int reloadConfig(void *unused = NULL);
/// Return 0 if sleep is okay
int preflightSleepCb(void *unused = NULL) { return radioIf.canSleep() ? 0 : 1; }
int notifyDeepSleepDb(void *unused = NULL)
{
radioIf.sleep();
return 0;
}
};

View File

@@ -46,8 +46,6 @@ MeshService service;
#include "Router.h"
#define NUM_PACKET_ID 255 // 0 is consider invalid
static uint32_t sendOwnerCb()
{
service.sendOurOwner();
@@ -57,16 +55,6 @@ static uint32_t sendOwnerCb()
static Periodic sendOwnerPeriod(sendOwnerCb);
/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId()
{
static uint32_t i;
i++;
return (i % NUM_PACKET_ID) + 1; // return number between 1 and 255
}
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
{
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
@@ -77,17 +65,17 @@ void MeshService::init()
sendOwnerPeriod.setup();
nodeDB.init();
gpsObserver.observe(&gps);
gpsObserver.observe(gps);
packetReceivedObserver.observe(&router.notifyPacketReceived);
}
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocForSending();
MeshPacket *p = router.allocForSending();
p->to = dest;
p->payload.want_response = wantReplies;
p->payload.has_user = true;
User &u = p->payload.user;
p->decoded.want_response = wantReplies;
p->decoded.which_payload = SubPacket_user_tag;
User &u = p->decoded.user;
u = owner;
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
@@ -98,12 +86,14 @@ void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
{
bool wasBroadcast = mp->to == NODENUM_BROADCAST;
bool isCollision = mp->from == myNodeInfo.my_node_num;
// we win if we have a lower macaddr
bool weWin = memcmp(&owner.macaddr, &mp->payload.user.macaddr, sizeof(owner.macaddr)) < 0;
// Disable this collision testing if we use 32 bit nodenums
bool isCollision = (sizeof(NodeNum) == 1) && (mp->from == myNodeInfo.my_node_num);
if (isCollision) {
// we win if we have a lower macaddr
bool weWin = memcmp(&owner.macaddr, &mp->decoded.user.macaddr, sizeof(owner.macaddr)) < 0;
if (weWin) {
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
@@ -127,7 +117,7 @@ const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
sendOurOwner(mp->from);
String lcd = String("Joined: ") + mp->payload.user.long_name + "\n";
String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n";
screen.print(lcd.c_str());
}
@@ -136,17 +126,17 @@ const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
void MeshService::handleIncomingPosition(const MeshPacket *mp)
{
if (mp->has_payload && mp->payload.has_position) {
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.position.time);
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_position_tag) {
DEBUG_MSG("handled incoming position time=%u\n", mp->decoded.position.time);
if (mp->payload.position.time) {
if (mp->decoded.position.time) {
struct timeval tv;
uint32_t secs = mp->payload.position.time;
uint32_t secs = mp->decoded.position.time;
tv.tv_sec = secs;
tv.tv_usec = 0;
gps.perhapsSetRTC(&tv);
perhapsSetRTC(&tv);
}
} else {
DEBUG_MSG("Ignoring incoming packet - not a position\n");
@@ -158,13 +148,13 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
// If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work)
if (!gps.isConnected)
if (!gps->isConnected)
handleIncomingPosition(mp);
else {
DEBUG_MSG("Ignoring incoming time, because we have a GPS\n");
}
if (mp->has_payload && mp->payload.has_user) {
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
mp = handleFromRadioUser(mp);
}
@@ -185,7 +175,7 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
MeshPacket *copied = packetPool.allocCopy(*mp);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
if (mp->payload.want_response)
if (mp->decoded.want_response)
sendNetworkPing(mp->from);
} else {
DEBUG_MSG("Not delivering vetoed User message\n");
@@ -227,8 +217,8 @@ void MeshService::handleToRadio(MeshPacket &p)
if (p.id == 0)
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone
// (so we update our nodedb for the local node)
p.rx_time = getValidTime(); // Record the time the packet arrived from the phone
// (so we update our nodedb for the local node)
// Send the packet into the mesh
@@ -250,37 +240,16 @@ void MeshService::sendToMesh(MeshPacket *p)
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: for now, we allow a device with a local GPS to include the time, so that gpsless
// devices can get time.
if (p->has_payload && p->payload.has_position) {
if (!gps.isConnected) {
DEBUG_MSG("Stripping time %u from position send\n", p->payload.position.time);
p->payload.position.time = 0;
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) {
if (!gps->isConnected) {
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
p->decoded.position.time = 0;
} else
DEBUG_MSG("Providing time to mesh %u\n", p->payload.position.time);
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
}
// If the phone sent a packet just to us, don't send it out into the network
if (p->to == nodeDB.getNodeNum())
DEBUG_MSG("Dropping locally processed message\n");
else {
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
if (router.send(p) != ERRNO_OK) {
DEBUG_MSG("No radio was able to send packet, discarding...\n");
releaseToPool(p);
}
}
}
MeshPacket *MeshService::allocForSending()
{
MeshPacket *p = packetPool.allocZeroed();
p->has_payload = true;
p->from = nodeDB.getNodeNum();
p->to = NODENUM_BROADCAST;
p->id = generatePacketId();
p->rx_time = gps.getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp
return p;
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
router.sendLocal(p);
}
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
@@ -302,31 +271,31 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
assert(node->has_position);
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = allocForSending();
MeshPacket *p = router.allocForSending();
p->to = dest;
p->payload.has_position = true;
p->payload.position = node->position;
p->payload.want_response = wantReplies;
p->payload.position.time = gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
p->decoded.which_payload = SubPacket_position_tag;
p->decoded.position = node->position;
p->decoded.want_response = wantReplies;
p->decoded.position.time = getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
sendToMesh(p);
}
int MeshService::onGPSChanged(void *unused)
{
DEBUG_MSG("got gps notify\n");
// DEBUG_MSG("got gps notify\n");
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = allocForSending();
p->payload.has_position = true;
MeshPacket *p = router.allocForSending();
p->decoded.which_payload = SubPacket_position_tag;
Position &pos = p->payload.position;
Position &pos = p->decoded.position;
// !zero or !zero lat/long means valid
if (gps.latitude != 0 || gps.longitude != 0) {
if (gps.altitude != 0)
pos.altitude = gps.altitude;
pos.latitude = gps.latitude;
pos.longitude = gps.longitude;
pos.time = gps.getValidTime();
if (gps->latitude != 0 || gps->longitude != 0) {
if (gps->altitude != 0)
pos.altitude = gps->altitude;
pos.latitude_i = gps->latitude;
pos.longitude_i = gps->longitude;
pos.time = getValidTime();
}
// We limit our GPS broadcasts to a max rate

View File

@@ -67,9 +67,6 @@ class MeshService
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void reloadOwner() { sendOurOwner(); }
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
MeshPacket *allocForSending();
/// Called when the user wakes up our GUI, normally sends our latest location to the mesh (if we have it), otherwise at least
/// sends our owner
void sendNetworkPing(NodeNum dest, bool wantReplies = false);

View File

@@ -6,15 +6,27 @@
#include "mesh.pb.h"
#include <Arduino.h>
typedef uint8_t NodeNum;
typedef uint8_t PacketId; // A packet sequence number
typedef uint32_t NodeNum;
typedef uint32_t PacketId; // A packet sequence number
#define NODENUM_BROADCAST 255
#define NODENUM_BROADCAST (sizeof(NodeNum) == 4 ? UINT32_MAX : UINT8_MAX)
#define ERRNO_OK 0
#define ERRNO_NO_INTERFACES 33
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
/**
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.
*
* We reserve 3 bits in the header so this could be up to 7, but given the high range of lora and typical usecases, keeping
* maxhops to 3 should be fine for a while. This also serves to prevent routing/flooding attempts to be attempted for
* too long.
**/
#define HOP_MAX 7
/// We normally just use max 3 hops for sending reliable messages
#define HOP_RELIABLE 3
typedef int ErrorCode;
/// Alloc and free packets to our global, ISR safe pool
extern MemoryPool<MeshPacket> packetPool;
extern Allocator<MeshPacket> &packetPool;

View File

@@ -5,9 +5,12 @@
#include "FS.h"
#include "SPIFFS.h"
#include "CryptoEngine.h"
#include "GPS.h"
#include "NodeDB.h"
#include "PacketHistory.h"
#include "PowerFSM.h"
#include "Router.h"
#include "configuration.h"
#include "error.h"
#include "mesh-pb-constants.h"
@@ -27,11 +30,19 @@ DeviceState versions used to be defined in the .proto file but really only this
#define here.
*/
#define DEVICESTATE_CUR_VER 7
#define DEVICESTATE_CUR_VER 9
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
#ifndef NO_ESP32
#define FS SPIFFS
#define FSBegin() FS.begin(true)
#define FILE_O_WRITE "w"
#define FILE_O_READ "r"
#else
#include "InternalFileSystem.h"
#define FS InternalFS
#define FSBegin() FS.begin()
using namespace Adafruit_LittleFS_Namespace;
#endif
// FIXME - move this somewhere else
@@ -47,11 +58,17 @@ User &owner = devicestate.owner;
static uint8_t ourMacAddr[6];
/**
* The node number the user is currently looking at
* 0 if none
*/
NodeNum displayedNodeNum;
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
void NodeDB::resetRadioConfig()
{
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
@@ -75,15 +92,21 @@ void NodeDB::resetRadioConfig()
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.tx_power = 23;
memcpy(&channelSettings.psk, &defaultpsk, sizeof(channelSettings.psk));
memcpy(&channelSettings.psk.bytes, &defaultpsk, sizeof(channelSettings.psk));
channelSettings.psk.size = sizeof(defaultpsk);
strcpy(channelSettings.name, "Default");
}
// Tell our crypto engine about the psk
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;
radioConfig.preferences.position_broadcast_secs = 6 * 60;
radioConfig.preferences.ls_secs = 60;
*/
}
@@ -93,7 +116,6 @@ void NodeDB::init()
devicestate.has_my_node = true;
devicestate.has_radio = true;
devicestate.has_owner = true;
devicestate.has_radio = false;
devicestate.radio.has_channel_settings = true;
devicestate.radio.has_preferences = true;
devicestate.node_db_count = 0;
@@ -103,10 +125,9 @@ void NodeDB::init()
// default to no GPS, until one has been found by probing
myNodeInfo.has_gps = false;
strncpy(myNodeInfo.region, xstr(HW_VERSION), sizeof(myNodeInfo.region));
strncpy(myNodeInfo.firmware_version, xstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
myNodeInfo.min_app_version = 172;
generatePacketId(); // FIXME - ugly way to init current_packet_id;
// Init our blank owner info to reasonable defaults
getMacAddr(ourMacAddr);
@@ -114,13 +135,29 @@ void NodeDB::init()
ourMacAddr[5]);
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
// make each node start with ad different random seed (but okay that the sequence is the same each boot)
randomSeed((ourMacAddr[2] << 24L) | (ourMacAddr[3] << 16L) | (ourMacAddr[4] << 8L) | ourMacAddr[5]);
// Set default owner name
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", ourMacAddr[5]);
sprintf(owner.short_name, "?%02X", myNodeInfo.my_node_num & 0xff);
// Crummy guess at our nodenum
if (!FSBegin()) // FIXME - do this in main?
{
DEBUG_MSG("ERROR filesystem mount Failed\n");
// FIXME - report failure to phone
}
// saveToDisk();
loadFromDisk();
// saveToDisk();
// We set node_num and packet_id _after_ loading from disk, because we always want to use the values this
// rom was compiled for, not what happens to be in the save file.
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
pickNewNodeNum();
// Include our owner in the node db under our nodenum
@@ -128,8 +165,12 @@ void NodeDB::init()
info->user = owner;
info->has_user = true;
// saveToDisk();
loadFromDisk();
// We set these _after_ loading from disk - because they come from the build and are more trusted than
// what is stored in flash
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
resetRadioConfig(); // If bogus settings got saved, then fix them
DEBUG_MSG("NODENUM=0x%x, dbsize=%d\n", myNodeInfo.my_node_num, *numNodes);
@@ -143,9 +184,14 @@ void NodeDB::init()
*/
void NodeDB::pickNewNodeNum()
{
// FIXME not the right way to guess node numes
uint8_t r = ourMacAddr[5];
if (r == 0xff || r < NUM_RESERVED)
NodeNum r = myNodeInfo.my_node_num;
// If we don't have a nodenum at app - pick an initial nodenum based on the macaddr
if (r == 0)
r = sizeof(NodeNum) == 1 ? ourMacAddr[5]
: ((ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]);
if (r == NODENUM_BROADCAST || r < NUM_RESERVED)
r = NUM_RESERVED; // don't pick a reserved node number
NodeInfo *found;
@@ -166,13 +212,7 @@ void NodeDB::loadFromDisk()
#ifdef FS
static DeviceState scratch;
if (!FS.begin(true)) // FIXME - do this in main?
{
DEBUG_MSG("ERROR SPIFFS Mount Failed\n");
// FIXME - report failure to phone
}
File f = FS.open(preffile);
auto f = FS.open(preffile);
if (f) {
DEBUG_MSG("Loading saved preferences\n");
pb_istream_t stream = {&readcb, &f, DeviceState_size};
@@ -206,7 +246,7 @@ void NodeDB::loadFromDisk()
void NodeDB::saveToDisk()
{
#ifdef FS
File f = FS.open(preftmp, "w");
auto f = FS.open(preftmp, FILE_O_WRITE);
if (f) {
DEBUG_MSG("Writing preferences\n");
@@ -218,15 +258,18 @@ void NodeDB::saveToDisk()
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();
} 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");
}
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 {
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
}
@@ -246,7 +289,7 @@ const NodeInfo *NodeDB::readNextInfo()
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
uint32_t sinceLastSeen(const NodeInfo *n)
{
uint32_t now = gps.getTime();
uint32_t now = getTime();
uint32_t last_seen = n->position.time;
int delta = (int)(now - last_seen);
@@ -274,8 +317,8 @@ size_t NodeDB::getNumOnlineNodes()
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp)
{
if (mp.has_payload) {
const SubPacket &p = mp.payload;
if (mp.which_payload == MeshPacket_decoded_tag) {
const SubPacket &p = mp.decoded;
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
int oldNumNodes = *numNodes;
@@ -289,19 +332,24 @@ void NodeDB::updateFrom(const MeshPacket &mp)
info->position.time = mp.rx_time;
}
if (p.has_position) {
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
switch (p.which_payload) {
case SubPacket_position_tag: {
// we carefully preserve the old time, because we always trust our local timestamps more
uint32_t oldtime = info->position.time;
info->position = p.position;
info->position.time = oldtime;
info->has_position = true;
updateGUIforNode = info;
break;
}
if (p.has_data) {
case SubPacket_data_tag: {
// Keep a copy of the most recent text message.
if (p.data.typ == Data_Type_CLEAR_TEXT) {
DEBUG_MSG("Received text msg from=0%0x, msg=%.*s\n", mp.from, p.data.payload.size, p.data.payload.bytes);
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.data.payload.size,
p.data.payload.bytes);
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
// We only store/display messages destined for us.
devicestate.rx_text_message = mp;
@@ -310,9 +358,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
}
}
break;
}
if (p.has_user) {
case SubPacket_user_tag: {
DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name);
bool changed = memcmp(&info->user, &p.user,
@@ -330,6 +379,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
// We just changed something important about the user, store our DB
// saveToDisk();
}
break;
}
}
}
}

View File

@@ -95,4 +95,10 @@ class NodeDB
void loadFromDisk();
};
/**
* The node number the user is currently looking at
* 0 if none
*/
extern NodeNum displayedNodeNum;
extern NodeDB nodeDB;

View File

@@ -0,0 +1,55 @@
#include "PacketHistory.h"
#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
// setup our periodic task
}
/**
* Update recentBroadcasts and return true if we have already seen this packet
*/
bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
{
if (p->id == 0) {
DEBUG_MSG("Ignoring message with zero id\n");
return false; // Not a floodable message ID, so we don't care
}
uint32_t now = millis();
for (size_t i = 0; i < recentPackets.size();) {
PacketRecord &r = recentPackets[i];
if ((now - r.rxTimeMsec) >= FLOOD_EXPIRE_TIME) {
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
recentPackets.erase(recentPackets.begin() + i); // delete old record
} else {
if (r.id == p->id && r.sender == p->from) {
DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
// Update the time on this record to now
if (withUpdate)
r.rxTimeMsec = now;
return true;
}
i++;
}
}
// Didn't find an existing record, make one
if (withUpdate) {
PacketRecord r;
r.id = p->id;
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);
}
return false;
}

71
src/mesh/PacketHistory.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#include "Router.h"
#include <queue>
#include <unordered_set>
using namespace std;
/// We clear our old flood record five minute after we see the last of it
#define FLOOD_EXPIRE_TIME (5 * 60 * 1000L)
/**
* A record of a recent message broadcast
*/
struct PacketRecord {
NodeNum sender;
PacketId id;
uint32_t rxTimeMsec; // Unix time in msecs - the time we received it
bool operator==(const PacketRecord &p) const { return sender == p.sender && id == p.id; }
};
class PacketRecordHashFunction
{
public:
size_t operator()(const PacketRecord &p) const { return (hash<NodeNum>()(p.sender)) ^ (hash<PacketId>()(p.id)); }
};
/// Order packet records by arrival time, we want the oldest packets to be in the front of our heap
class PacketRecordOrderFunction
{
public:
size_t operator()(const PacketRecord &p1, const PacketRecord &p2) const
{
// 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
t1 = t2;
t2 = p1.rxTimeMsec;
}
return t1 > t2;
}
};
/**
* This is a mixin that adds a record of past packets we have seen
*/
class PacketHistory
{
private:
/** FIXME: really should be a std::unordered_set with the key being sender,id.
* This would make checking packets in wasSeenRecently faster.
*/
vector<PacketRecord> recentPackets;
// priority_queue<PacketRecord, vector<PacketRecord>, PacketRecordOrderFunction> arrivalTimes;
// unordered_set<PacketRecord, PacketRecordHashFunction> recentPackets;
public:
PacketHistory();
/**
* Update recentBroadcasts and return true if we have already seen this packet
*
* @param withUpdate if true and not found we add an entry to recentPackets
*/
bool wasSeenRecently(const MeshPacket *p, bool withUpdate = true);
};

View File

@@ -1,6 +1,7 @@
#include "PhoneAPI.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include <assert.h>
PhoneAPI::PhoneAPI()
@@ -14,16 +15,36 @@ void PhoneAPI::init()
observe(&service.fromNumChanged);
}
void PhoneAPI::checkConnectionTimeout()
{
if (isConnected) {
bool newConnected = (millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
if (!newConnected) {
isConnected = false;
onConnectionChanged(isConnected);
}
}
}
/**
* Handle a ToRadio protobuf
*/
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);
}
// return (lastContactMsec != 0) &&
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;
DEBUG_MSG("PACKET FROM PHONE: id=%d, to=%x, want_ack=%d, which1=%d, which2=%d, typ=%d, buflen=%d\n", p.id, p.to, p.want_ack, p.which_payload,
p.decoded.which_payload, p.decoded.data.typ, bufLength);
service.handleToRadio(p);
break;
}
@@ -71,8 +92,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));
@@ -227,6 +252,9 @@ void PhoneAPI::handleToRadioPacket(MeshPacket *p) {}
/// If the mesh service tells us fromNum has changed, tell the phone
int PhoneAPI::onNotify(uint32_t newValue)
{
checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this
// from idle)
if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) {
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
onNowHasData(newValue);

View File

@@ -45,14 +45,16 @@ class PhoneAPI
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
const NodeInfo *nodeInfoForPhone = NULL;
/// Our fromradio packet while it is being assembled
FromRadio fromRadioScratch;
ToRadio toRadioScratch; // this is a static scratch object, any data must be copied elsewhere before returning
/// Use to ensure that clients don't get confused about old messages from the radio
uint32_t config_nonce = 0;
/** the last msec we heard from the client on the other side of this link */
uint32_t lastContactMsec = 0;
bool isConnected = false;
public:
PhoneAPI();
@@ -85,10 +87,19 @@ class PhoneAPI
void handleSetRadio(const RadioConfig &r);
protected:
/// Our fromradio packet while it is being assembled
FromRadio fromRadioScratch;
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected) {}
/// If we haven't heard from the other side in a while then say not connected
void checkConnectionTimeout();
/**
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
*/
void onNowHasData(uint32_t fromRadioNum) {}
virtual void onNowHasData(uint32_t fromRadioNum) {}
private:
/**

126
src/mesh/RF95Interface.cpp Normal file
View File

@@ -0,0 +1,126 @@
#include "RF95Interface.h"
#include "MeshRadio.h" // kinda yucky, but we need to know which region we are in
#include "RadioLibRF95.h"
#include <configuration.h>
#define MAX_POWER 17
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, SPIClass &spi)
: RadioLibInterface(cs, irq, rst, 0, spi)
{
// FIXME - we assume devices never get destroyed
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
bool RF95Interface::init()
{
RadioLibInterface::init();
applyModemConfig();
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
iface = lora = new RadioLibRF95(&module);
int res = lora->begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength);
DEBUG_MSG("LORA init result %d\n", res);
if (res == ERR_NONE)
res = lora->setCRC(SX126X_LORA_CRC_ON);
if (res == ERR_NONE)
startReceive(); // start receiving
return res == ERR_NONE;
}
void INTERRUPT_ATTR RF95Interface::disableInterrupt()
{
lora->clearDio0Action();
}
bool RF95Interface::reconfigure()
{
applyModemConfig();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora->setSpreadingFactor(sf);
assert(err == ERR_NONE);
err = lora->setBandwidth(bw);
assert(err == ERR_NONE);
err = lora->setCodingRate(cr);
assert(err == ERR_NONE);
err = lora->setSyncWord(syncWord);
assert(err == ERR_NONE);
err = lora->setCurrentLimit(currentLimit);
assert(err == ERR_NONE);
err = lora->setPreambleLength(preambleLength);
assert(err == ERR_NONE);
err = lora->setFrequency(freq);
assert(err == ERR_NONE);
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
err = lora->setOutputPower(power);
assert(err == ERR_NONE);
startReceive(); // restart receiving
return ERR_NONE;
}
/**
* Add SNR data to received messages
*/
void RF95Interface::addReceiveMetadata(MeshPacket *mp)
{
mp->rx_snr = lora->getSNR();
}
void RF95Interface::setStandby()
{
int err = lora->standby();
assert(err == ERR_NONE);
isReceiving = false; // If we were receiving, not any more
disableInterrupt();
completeSending(); // If we were sending, not anymore
}
void RF95Interface::startReceive()
{
setStandby();
int err = lora->startReceive();
assert(err == ERR_NONE);
isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
bool RF95Interface::isActivelyReceiving()
{
return lora->isReceiving();
}
bool RF95Interface::sleep()
{
// put chipset into sleep mode
disableInterrupt();
lora->sleep();
return true;
}

55
src/mesh/RF95Interface.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include "MeshRadio.h" // kinda yucky, but we need to know which region we are in
#include "RadioLibInterface.h"
#include "RadioLibRF95.h"
/**
* Our new not radiohead adapter for RF95 style radios
*/
class RF95Interface : public RadioLibInterface
{
RadioLibRF95 *lora; // Either a RFM95 or RFM96 depending on what was stuffed on this board
public:
RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, SPIClass &spi);
/// 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();
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure();
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep();
protected:
/**
* Glue functions called from ISR land
*/
virtual void disableInterrupt();
/**
* Enable a particular ISR callback glue function
*/
virtual void enableInterrupt(void (*callback)()) { lora->setDio0Action(callback); }
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving();
/**
* Start waiting to receive a message
*/
virtual void startReceive();
/**
* Add SNR data to received messages
*/
virtual void addReceiveMetadata(MeshPacket *mp);
virtual void setStandby();
};

129
src/mesh/RadioInterface.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include "RadioInterface.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
/**
* ## LoRaWAN for North America
LoRaWAN defines 64, 125 kHz channels from 902.3 to 914.9 MHz increments.
The maximum output power for North America is +30 dBM.
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are
separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency.
*/
// 1kb was too small
#define RADIO_STACK_SIZE 4096
RadioInterface::RadioInterface()
{
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
myNodeInfo.num_channels = NUM_CHANNELS;
// Can't print strings this early - serial not setup yet
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
}
bool RadioInterface::init()
{
DEBUG_MSG("Starting meshradio init...\n");
configChangedObserver.observe(&service.configChanged);
preflightSleepObserver.observe(&preflightSleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
// we now expect interfaces to operate in promiscous mode
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
// time.
// we want this thread to run at very high priority, because it is effectively running as a user space ISR
start("radio", RADIO_STACK_SIZE, configMAX_PRIORITIES - 1); // Start our worker thread
return true;
}
/** hash a string into an integer
*
* djb2 by Dan Bernstein.
* http://www.cse.yorku.ca/~oz/hash.html
*/
unsigned long hash(char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++) != 0)
hash = ((hash << 5) + hash) + (unsigned char)c; /* hash * 33 + c */
return hash;
}
/**
* Pull our channel settings etc... from protobufs to the dumb interface settings
*/
void RadioInterface::applyModemConfig()
{
// Set up default configuration
// No Sync Words in LORA mode.
modemConfig = (ModemConfigChoice)channelSettings.modem_config;
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
int channel_num = hash(channelSettings.name) % NUM_CHANNELS;
freq = CH0 + CH_SPACING * channel_num;
power = channelSettings.tx_power;
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
channelSettings.tx_power);
}
ErrorCode SimRadio::send(MeshPacket *p)
{
DEBUG_MSG("SimRadio.send\n");
packetPool.release(p);
return ERRNO_OK;
}
void RadioInterface::deliverToReceiver(MeshPacket *p)
{
assert(rxDest);
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
}
/***
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of payload bytes to send
*/
size_t RadioInterface::beginSending(MeshPacket *p)
{
assert(!sendingPacket);
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
lastTxStart = millis();
PacketHeader *h = (PacketHeader *)radiobuf;
h->from = p->from;
h->to = p->to;
h->id = p->id;
assert(p->hop_limit <= HOP_MAX);
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
// if the sender nodenum is zero, that means uninitialized
assert(h->from);
memcpy(radiobuf + sizeof(PacketHeader), p->encrypted.bytes, p->encrypted.size);
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
}

164
src/mesh/RadioInterface.h Normal file
View File

@@ -0,0 +1,164 @@
#pragma once
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "WorkerThread.h"
#include "mesh.pb.h"
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
#define MAX_RHPACKETLEN 256
#define PACKET_FLAGS_HOP_MASK 0x07
#define PACKET_FLAGS_WANT_ACK_MASK 0x08
/**
* This structure has to exactly match the wire layout when sent over the radio link. Used to keep compatibility
* wtih the old radiohead implementation.
*/
typedef struct {
NodeNum to, from; // can be 1 byte or four bytes
PacketId id; // can be 1 byte or 4 bytes
/**
* Usage of flags:
*
* The bottom three bits of flags are use to store hop_limit when sent over the wire.
**/
uint8_t flags;
} PacketHeader;
typedef enum {
Bw125Cr45Sf128 = 0, ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
Bw500Cr45Sf128, ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
Bw31_25Cr48Sf512, ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
Bw125Cr48Sf4096, ///< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range
} ModemConfigChoice;
/**
* Basic operations all radio chipsets must implement.
*
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
*/
class RadioInterface : protected NotifiedWorkerThread
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;
CallbackObserver<RadioInterface, void *> configChangedObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::reloadConfig);
CallbackObserver<RadioInterface, void *> preflightSleepObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::preflightSleepCb);
CallbackObserver<RadioInterface, void *> notifyDeepSleepObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::notifyDeepSleepDb);
protected:
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
uint32_t lastTxStart = 0L;
/**
* A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
* */
uint8_t radiobuf[MAX_RHPACKETLEN];
/**
* Enqueue a received packet for the registered receiver
*/
void deliverToReceiver(MeshPacket *p);
public:
float freq = 915.0; // FIXME, init all these params from user setings
int8_t power = 17;
ModemConfigChoice modemConfig;
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
RadioInterface();
/**
* Set where to deliver received packets. This method should only be used by the Router class
*/
void setReceiver(PointerQueue<MeshPacket> *_rxDest) { rxDest = _rxDest; }
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
* This method must be used before putting the CPU into deep or light sleep.
*/
virtual bool canSleep() { return true; }
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() { return true; }
/**
* Send a packet (possibly by enquing in a private fifo). This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
virtual ErrorCode send(MeshPacket *p) = 0;
// methods from radiohead
/// 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();
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() = 0;
protected:
/***
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the
* PacketHeader & payload).
*
* Used as the first step of
*/
size_t beginSending(MeshPacket *p);
virtual void loop() {} // Idle processing
/**
* Convert our modemConfig enum into wf, sf, etc...
*
* These paramaters will be pull from the channelSettings global
*/
virtual void applyModemConfig();
private:
/// Return 0 if sleep is okay
int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; }
int notifyDeepSleepDb(void *unused = NULL)
{
sleep();
return 0;
}
int reloadConfig(void *unused)
{
reconfigure();
return 0;
}
};
class SimRadio : public RadioInterface
{
public:
virtual ErrorCode send(MeshPacket *p);
// methods from radiohead
/// 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() { return true; }
};

View File

@@ -0,0 +1,314 @@
#include "RadioLibInterface.h"
#include "MeshTypes.h"
#include "mesh-pb-constants.h"
#include <configuration.h>
#include <pb_decode.h>
#include <pb_encode.h>
// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that
static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi, PhysicalLayer *_iface)
: PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
{
assert(!instance); // We assume only one for now
instance = this;
}
bool RadioLibInterface::init()
{
setup(); // init our timer
return RadioInterface::init();
}
#ifndef NO_ESP32
// ESP32 doesn't use that flag
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
#else
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x)
#endif
void INTERRUPT_ATTR RadioLibInterface::isrLevel0Common(PendingISR cause)
{
instance->disableInterrupt();
instance->pending = cause;
BaseType_t xHigherPriorityTaskWoken;
instance->notifyFromISR(&xHigherPriorityTaskWoken, cause, eSetValueWithOverwrite);
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
The macro used to do this is dependent on the port and may be called
portEND_SWITCHING_ISR. */
YIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void INTERRUPT_ATTR RadioLibInterface::isrRxLevel0()
{
isrLevel0Common(ISR_RX);
}
void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
{
isrLevel0Common(ISR_TX);
}
/** Our ISR code currently needs this to find our active instance
*/
RadioLibInterface *RadioLibInterface::instance;
/**
* Convert our modemConfig enum into wf, sf, etc...
*/
void RadioLibInterface::applyModemConfig()
{
RadioInterface::applyModemConfig();
switch (modemConfig) {
case Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
bw = 125;
cr = 5;
sf = 7;
break;
case Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
bw = 500;
cr = 5;
sf = 7;
break;
case Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
bw = 31.25;
cr = 8;
sf = 9;
break;
case Bw125Cr48Sf4096:
bw = 125;
cr = 8;
sf = 12;
break;
default:
assert(0); // Unknown enum
}
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
bool RadioLibInterface::canSendImmediately()
{
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
// we almost certainly guarantee no one outside will like the packet we are sending.
bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving();
if (busyTx || busyRx) {
if (busyTx)
DEBUG_MSG("Can not send yet, busyTx\n");
if (busyRx)
DEBUG_MSG("Can not send yet, busyRx\n");
return false;
} else
return true;
}
/// Send a packet (possibly by enquing in a private fifo). This routine will
/// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// 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);
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
packetPool.release(p);
return res;
}
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
startTransmitTimer(true);
return res;
}
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", res);
return res;
}
/** At the low end we want to pick a delay large enough that anyone who just completed sending (some other node)
* has had enough time to switch their radio back into receive mode.
*/
#define MIN_TX_WAIT_MSEC 100
/**
* At the high end, this value is used to spread node attempts across time so when they are replying to a packet
* they don't both check that the airwaves are clear at the same moment. As long as they are off by some amount
* one of the two will be first to start transmitting and the other will see that. I bet 500ms is more than enough
* to guarantee this.
*/
#define MAX_TX_WAIT_MSEC 2000 // stress test would still fail occasionally with 1000
/** radio helper thread callback.
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
wait a random delay of 50 to 200 ms to make sure we are not stomping on someone else. The 50ms delay at the beginning ensures all
possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 200ms
random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first
and then they will wait until that packet finishes.
NOTE: the large flood rebroadcast delay might still be needed even with this approach. Because we might not be able to hear other
transmitters that we are potentially stomping on. Requires further thought.
FIXME, the MIN_TX_WAIT_MSEC and MAX_TX_WAIT_MSEC values should be tuned via logic analyzer later.
*/
void RadioLibInterface::loop()
{
pending = ISR_NONE;
switch (notification) {
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:
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
if (!txQueue.isEmpty()) {
if (!canSendImmediately()) {
startTransmitTimer(); // try again in a little while
} else {
// Send any outgoing packets we have ready
MeshPacket *txp = txQueue.dequeuePtr(0);
assert(txp);
startSend(txp);
}
} else {
// DEBUG_MSG("done with txqueue\n");
}
break;
default:
assert(0); // We expected to receive a valid notification from the ISR
}
}
void RadioLibInterface::doTask()
{
disable(); // Don't call this callback again
// We use without overwrite, so that if there is already an interrupt pending to be handled, that gets handle properly (the
// ISR handler will restart our timer)
notify(TRANSMIT_DELAY_COMPLETED, eSetValueWithoutOverwrite);
}
void RadioLibInterface::startTransmitTimer(bool withDelay)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (getPeriod() == 0 && !txQueue.isEmpty()) {
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);
}
}
void RadioLibInterface::handleTransmitInterrupt()
{
// DEBUG_MSG("handling lora TX interrupt\n");
assert(sendingPacket); // Were we sending? - FIXME, this was null coming out of light sleep due to RF95 ISR!
completeSending();
}
void RadioLibInterface::completeSending()
{
if (sendingPacket) {
txGood++;
DEBUG_MSG("Completed sending to=0x%x, id=%u\n", sendingPacket->to, sendingPacket->id);
// We are done sending that packet, release it
packetPool.release(sendingPacket);
sendingPacket = NULL;
// DEBUG_MSG("Done with send\n");
}
}
void RadioLibInterface::handleReceiveInterrupt()
{
assert(isReceiving);
isReceiving = false;
// read the number of actually received bytes
size_t length = iface->getPacketLength();
int state = iface->readData(radiobuf, length);
if (state != ERR_NONE) {
DEBUG_MSG("ignoring received packet due to error=%d\n", state);
rxBad++;
} else {
// Skip the 4 headers that are at the beginning of the rxBuf
int32_t payloadLen = length - sizeof(PacketHeader);
const uint8_t *payload = radiobuf + sizeof(PacketHeader);
// check for short packets
if (payloadLen < 0) {
DEBUG_MSG("ignoring received packet too short\n");
rxBad++;
} else {
const PacketHeader *h = (PacketHeader *)radiobuf;
rxGood++;
// Note: we deliver _all_ packets to our router (i.e. our interface is intentionally promiscuous).
// This allows the router and other apps on our node to sniff packets (usually routing) between other
// nodes.
MeshPacket *mp = packetPool.allocZeroed();
mp->from = h->from;
mp->to = h->to;
mp->id = h->id;
assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code
mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK;
mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK);
addReceiveMetadata(mp);
mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
assert(payloadLen <= sizeof(mp->encrypted.bytes));
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);
deliverToReceiver(mp);
}
}
}
/** 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);
setStandby(); // Cancel any already in process receives
size_t numbytes = beginSending(txp);
int res = iface->startTransmit(radiobuf, numbytes);
assert(res == ERR_NONE);
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrTxLevel0);
}

View File

@@ -0,0 +1,147 @@
#pragma once
#include "PeriodicTask.h"
#include "RadioInterface.h"
#include <RadioLib.h>
// ESP32 has special rules about ISR code
#ifdef ARDUINO_ARCH_ESP32
#define INTERRUPT_ATTR IRAM_ATTR
#else
#define INTERRUPT_ATTR
#endif
class RadioLibInterface : public RadioInterface, private PeriodicTask
{
/// Used as our notification from the ISR
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
volatile PendingISR pending = ISR_NONE;
/**
* Raw ISR handler that just calls our polymorphic method
*/
static void isrTxLevel0(), isrLevel0Common(PendingISR code);
/**
* Debugging counts
*/
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE);
protected:
float bw = 125;
uint8_t sf = 9;
uint8_t cr = 7;
/**
* FIXME, use a meshtastic sync word, but hashed with the Channel name. Currently picking the same default
* the RF95 used (0x14). Note: do not use 0x34 - that is reserved for lorawan
*/
uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE;
float currentLimit = 100; // FIXME
uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving
Module module; // The HW interface to the radio
/**
* provides lowest common denominator RadioLib API
*/
PhysicalLayer *iface;
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving;
public:
/** Our ISR code currently needs this to find our active instance
*/
static RadioLibInterface *instance;
/**
* Glue functions called from ISR land
*/
virtual void disableInterrupt() = 0;
/**
* Enable a particular ISR callback glue function
*/
virtual void enableInterrupt(void (*)()) = 0;
public:
RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi,
PhysicalLayer *iface = NULL);
virtual ErrorCode send(MeshPacket *p);
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
* This method must be used before putting the CPU into deep or light sleep.
*/
virtual bool canSleep();
/**
* Start waiting to receive a message
*
* External functions can call this method to wake the device from sleep.
*/
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
*
* If the timer was already running, we just wait for that one to occur.
* */
void startTransmitTimer(bool withDelay = true);
void handleTransmitInterrupt();
void handleReceiveInterrupt();
static void timerCallback(void *p1, uint32_t p2);
virtual void doTask();
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();
/**
* Convert our modemConfig enum into wf, sf, etc...
*
* These paramaters will be pull from the channelSettings global
*/
virtual void applyModemConfig();
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately();
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() = 0;
/**
* Raw ISR handler that just calls our polymorphic method
*/
static void isrRxLevel0();
/**
* If a send was in progress finish it and return the buffer to the pool */
void completeSending();
/**
* Add SNR data to received messages
*/
virtual void addReceiveMetadata(MeshPacket *mp) = 0;
virtual void loop(); // Idle processing
virtual void setStandby() = 0;
};

68
src/mesh/RadioLibRF95.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include "RadioLibRF95.h"
#define RF95_CHIP_VERSION 0x12
#define RF95_ALT_VERSION 0x11 // Supposedly some versions of the chip have id 0x11
RadioLibRF95::RadioLibRF95(Module *mod) : SX1278(mod) {}
int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit,
uint16_t preambleLength, uint8_t gain)
{
// execute common part
int16_t state = SX127x::begin(RF95_CHIP_VERSION, syncWord, currentLimit, preambleLength);
if (state != ERR_NONE)
state = SX127x::begin(RF95_ALT_VERSION, syncWord, currentLimit, preambleLength);
RADIOLIB_ASSERT(state);
// configure settings not accessible by API
state = config();
RADIOLIB_ASSERT(state);
// configure publicly accessible settings
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
state = setBandwidth(bw);
RADIOLIB_ASSERT(state);
state = setSpreadingFactor(sf);
RADIOLIB_ASSERT(state);
state = setCodingRate(cr);
RADIOLIB_ASSERT(state);
state = setOutputPower(power);
RADIOLIB_ASSERT(state);
state = setGain(gain);
return (state);
}
int16_t RadioLibRF95::setFrequency(float freq)
{
// RADIOLIB_CHECK_RANGE(freq, 862.0, 1020.0, ERR_INVALID_FREQUENCY);
// set frequency
return (SX127x::setFrequencyRaw(freq));
}
#define RH_RF95_MODEM_STATUS_CLEAR 0x10
#define RH_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08
#define RH_RF95_MODEM_STATUS_RX_ONGOING 0x04
#define RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED 0x02
#define RH_RF95_MODEM_STATUS_SIGNAL_DETECTED 0x01
bool RadioLibRF95::isReceiving()
{
// 0x0b == Look for header info valid, signal synchronized or signal detected
uint8_t reg = readReg(SX127X_REG_MODEM_STAT);
// Serial.printf("reg %x\n", reg);
return (reg & (RH_RF95_MODEM_STATUS_SIGNAL_DETECTED | RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED |
RH_RF95_MODEM_STATUS_HEADER_INFO_VALID)) != 0;
}
uint8_t RadioLibRF95::readReg(uint8_t addr)
{
return _mod->SPIreadRegister(addr);
}

73
src/mesh/RadioLibRF95.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <RadioLib.h>
/*!
\class RFM95
\brief Derived class for %RFM95 modules. Overrides some methods from SX1278 due to different parameter ranges.
*/
class RadioLibRF95: public SX1278 {
public:
// constructor
/*!
\brief Default constructor. Called from Arduino sketch when creating new LoRa instance.
\param mod Instance of Module that will be used to communicate with the %LoRa chip.
*/
RadioLibRF95(Module* mod);
// basic methods
/*!
\brief %LoRa modem initialization method. Must be called at least once from Arduino sketch to initialize the module.
\param freq Carrier frequency in MHz. Allowed values range from 868.0 MHz to 915.0 MHz.
\param bw %LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz.
\param sf %LoRa link spreading factor. Allowed values range from 6 to 12.
\param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8.
\param syncWord %LoRa sync word. Can be used to distinguish different networks. Note that value 0x34 is reserved for LoRaWAN networks.
\param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm.
\param currentLimit Trim value for OCP (over current protection) in mA. Can be set to multiplies of 5 in range 45 to 120 mA and to multiples of 10 in range 120 to 240 mA.
Set to 0 to disable OCP (not recommended).
\param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols longer than the set number.
Allowed values range from 6 to 65535.
\param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
Set to 0 to enable automatic gain control (recommended).
\returns \ref status_codes
*/
int16_t begin(float freq = 915.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = SX127X_SYNC_WORD, int8_t power = 17, uint8_t currentLimit = 100, uint16_t preambleLength = 8, uint8_t gain = 0);
// configuration methods
/*!
\brief Sets carrier frequency. Allowed values range from 868.0 MHz to 915.0 MHz.
\param freq Carrier frequency to be set in MHz.
\returns \ref status_codes
*/
int16_t setFrequency(float freq);
// Return true if we are actively receiving a message currently
bool isReceiving();
/// For debugging
uint8_t readReg(uint8_t addr);
#ifndef RADIOLIB_GODMODE
private:
#endif
};

196
src/mesh/ReliableRouter.cpp Normal file
View File

@@ -0,0 +1,196 @@
#include "ReliableRouter.h"
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
// ReliableRouter::ReliableRouter() {}
/**
* If the message is want_ack, then add it to a list of packets to retransmit.
* If we run out of retransmissions, send a nak packet towards the original client to indicate failure.
*/
ErrorCode ReliableRouter::send(MeshPacket *p)
{
if (p->want_ack) {
// If someone asks for acks on broadcast, we need the hop limit to be at least one, so that first node that receives our
// message will rebroadcast
if (p->to == NODENUM_BROADCAST && p->hop_limit == 0)
p->hop_limit = 1;
auto copy = packetPool.allocCopy(*p);
startRetransmission(copy);
}
return FloodingRouter::send(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);
// 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
// the original sending process.
if (stopRetransmission(p->from, p->id)) {
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
sendAckNak(true, p->from, p->id);
}
}
return FloodingRouter::shouldFilterReceived(p);
}
/**
* If we receive a want_ack packet (do not check for wasSeenRecently), send back an ack (this might generate multiple ack sends in
* case the our first ack gets lost)
*
* If we receive an ack packet (do check wasSeenRecently), clear out any retransmissions and
* forward the ack to the application layer.
*
* If we receive a nak packet (do check wasSeenRecently), clear out any retransmissions
* and forward the nak to the application layer.
*
* Otherwise, let superclass handle it.
*/
void ReliableRouter::sniffReceived(const MeshPacket *p)
{
NodeNum ourNode = getNodeNum();
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
// - not DSR routing)
if (p->want_ack) {
sendAckNak(true, p->from, p->id);
}
// If the payload is valid, look for ack/nak
PacketId ackId = p->decoded.which_ack == SubPacket_success_id_tag ? p->decoded.ack.success_id : 0;
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) {
if (ackId) {
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
}
}
// handle the packet as normal
FloodingRouter::sniffReceived(p);
}
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom)
{
auto p = allocForSending();
p->hop_limit = 0; // Assume just immediate neighbors for now
p->to = to;
DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=%d,id=%d\n", isAck, to, idFrom, p->id);
if (isAck) {
p->decoded.ack.success_id = idFrom;
p->decoded.which_ack = SubPacket_success_id_tag;
} else {
p->decoded.ack.fail_id = idFrom;
p->decoded.which_ack = SubPacket_fail_id_tag;
}
sendLocal(p); // we sometimes send directly to the local node
}
#define NUM_RETRANSMISSIONS 3
PendingPacket::PendingPacket(MeshPacket *p)
{
packet = p;
numRetransmissions = NUM_RETRANSMISSIONS - 1; // We subtract one, because we assume the user just did the first send
setNextTx();
}
PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key)
{
auto old = pending.find(key); // If we have an old record, someone messed up because id got reused
if (old != pending.end()) {
return &old->second;
} else
return NULL;
}
/**
* Stop any retransmissions we are doing of the specified node/packet ID pair
*/
bool ReliableRouter::stopRetransmission(NodeNum from, PacketId id)
{
auto key = GlobalPacketId(from, id);
return stopRetransmission(key);
}
bool ReliableRouter::stopRetransmission(GlobalPacketId key)
{
auto old = findPendingPacket(key);
if (old) {
auto numErased = pending.erase(key);
assert(numErased == 1);
packetPool.release(old->packet);
return true;
} else
return false;
}
/**
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.
*/
PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
{
auto id = GlobalPacketId(p);
auto rec = PendingPacket(p);
stopRetransmission(p->from, p->id);
pending[id] = rec;
return &pending[id];
}
/**
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
*/
void ReliableRouter::doRetransmissions()
{
uint32_t now = millis();
// FIXME, we should use a better datastructure rather than walking through this map.
// for(auto el: pending) {
for (auto it = pending.begin(), nextIt = it; it != pending.end(); it = nextIt) {
++nextIt; // we use this odd pattern because we might be deleting it...
auto &p = it->second;
// FIXME, handle 51 day rolloever here!!!
if (p.nextTxMsec <= now) {
if (p.numRetransmissions == 0) {
DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
p.packet->id);
sendAckNak(false, p.packet->from, p.packet->id);
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
// allows the DSR version to still be able to look at the PendingPacket
stopRetransmission(it->first);
} else {
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
p.packet->id, p.numRetransmissions);
// Note: we call the superclass version because we don't want to have our version of send() add a new
// retransmission record
FloodingRouter::send(packetPool.allocCopy(*p.packet));
// Queue again
--p.numRetransmissions;
p.setNextTx();
}
}
}
}

129
src/mesh/ReliableRouter.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#include "FloodingRouter.h"
#include "PeriodicTask.h"
#include <unordered_map>
/**
* An identifier for a globalally unique message - a pair of the sending nodenum and the packet id assigned
* to that message
*/
struct GlobalPacketId {
NodeNum node;
PacketId id;
bool operator==(const GlobalPacketId &p) const { return node == p.node && id == p.id; }
GlobalPacketId(const MeshPacket *p)
{
node = p->from;
id = p->id;
}
GlobalPacketId(NodeNum _from, PacketId _id)
{
node = _from;
id = _id;
}
};
/**
* A packet queued for retransmission
*/
struct PendingPacket {
MeshPacket *packet;
/** The next time we should try to retransmit this packet */
uint32_t nextTxMsec;
/** Starts at NUM_RETRANSMISSIONS -1(normally 3) and counts down. Once zero it will be removed from the list */
uint8_t numRetransmissions;
/** True if we have started trying to find a route - for DSR usage
* While trying to find a route we don't actually send the data packet. We just leave it here pending until
* we have a route or we've failed to find one.
*/
bool wantRoute = false;
PendingPacket() {}
PendingPacket(MeshPacket *p);
void setNextTx() { nextTxMsec = millis() + random(20 * 1000, 22 * 1000); }
};
class GlobalPacketIdHashFunction
{
public:
size_t operator()(const GlobalPacketId &p) const { return (hash<NodeNum>()(p.node)) ^ (hash<PacketId>()(p.id)); }
};
/**
* This is a mixin that extends Router with the ability to do (one hop only) reliable message sends.
*/
class ReliableRouter : public FloodingRouter
{
private:
unordered_map<GlobalPacketId, PendingPacket, GlobalPacketIdHashFunction> pending;
public:
/**
* Constructor
*
*/
// ReliableRouter();
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
virtual ErrorCode send(MeshPacket *p);
/** Do our retransmission handling */
virtual void loop()
{
doRetransmissions();
FloodingRouter::loop();
}
protected:
/**
* Look for acks/naks or someone retransmitting us
*/
virtual void sniffReceived(const MeshPacket *p);
/**
* Try to find the pending packet record for this ID (or NULL if not found)
*/
PendingPacket *findPendingPacket(NodeNum from, PacketId id) { return findPendingPacket(GlobalPacketId(from, id)); }
PendingPacket *findPendingPacket(GlobalPacketId p);
/**
* We hook this method so we can see packets before FloodingRouter says they should be discarded
*/
virtual bool shouldFilterReceived(const MeshPacket *p);
/**
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.
*/
PendingPacket *startRetransmission(MeshPacket *p);
private:
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void sendAckNak(bool isAck, NodeNum to, PacketId idFrom);
/**
* Stop any retransmissions we are doing of the specified node/packet ID pair
*
* @return true if we found and removed a transmission with this ID
*/
bool stopRetransmission(NodeNum from, PacketId id);
bool stopRetransmission(GlobalPacketId p);
/**
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
*/
void doRetransmissions();
};

228
src/mesh/Router.cpp Normal file
View File

@@ -0,0 +1,228 @@
#include "Router.h"
#include "CryptoEngine.h"
#include "GPS.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include <NodeDB.h>
/**
* Router todo
*
* DONE: Implement basic interface and use it elsewhere in app
* Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s
* Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet)
* Add DSR mixin
*
**/
#define MAX_RX_FROMRADIO \
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 + 2 * MAX_TX_QUEUE + \
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
// static MemoryDynamic<MeshPacket> staticPool;
Allocator<MeshPacket> &packetPool = staticPool;
/**
* Constructor
*
* Currently we only allow one interface, that may change in the future
*/
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO) {}
/**
* do idle processing
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
*/
void Router::loop()
{
MeshPacket *mp;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
perhapsHandleReceived(mp);
}
}
/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId()
{
static uint32_t i; // Note: trying to keep this in noinit didn't help for working across reboots
static bool didInit = false;
assert(sizeof(PacketId) == 4 || sizeof(PacketId) == 1); // only supported values
uint32_t numPacketId = sizeof(PacketId) == 1 ? UINT8_MAX : UINT32_MAX; // 0 is consider invalid
if (!didInit) {
didInit = true;
// pick a random initial sequence number at boot (to prevent repeated reboots always starting at 0)
// Note: we mask the high order bit to ensure that we never pass a 'negative' number to random
i = random(numPacketId & 0x7fffffff);
DEBUG_MSG("Initial packet id %u, numPacketId %u\n", i, numPacketId);
}
i++;
PacketId id = (i % numPacketId) + 1; // return number between 1 and numPacketId (ie - never zero)
myNodeInfo.current_packet_id = id; // Kinda crufty - we keep updating this so the phone can see a current value
return id;
}
MeshPacket *Router::allocForSending()
{
MeshPacket *p = packetPool.allocZeroed();
p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start.
p->from = nodeDB.getNodeNum();
p->to = NODENUM_BROADCAST;
p->hop_limit = HOP_RELIABLE;
p->id = generatePacketId();
p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp
return p;
}
ErrorCode Router::sendLocal(MeshPacket *p)
{
if (p->to == nodeDB.getNodeNum()) {
DEBUG_MSG("Enqueuing internal message for the receive queue\n");
fromRadioQueue.enqueue(p);
return ERRNO_OK;
} else
return send(p);
}
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error.
*/
ErrorCode Router::send(MeshPacket *p)
{
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
assert(
!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert
// Never set the want_ack flag on broadcast packets sent over the air.
if (p->to == NODENUM_BROADCAST)
p->want_ack = false;
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
assert(p->which_payload == MeshPacket_encrypted_tag ||
p->which_payload == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
// First convert from protobufs to raw bytes
if (p->which_payload == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
assert(numbytes <= MAX_RHPACKETLEN);
crypto->encrypt(p->from, p->id, numbytes, bytes);
// Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes);
p->encrypted.size = numbytes;
p->which_payload = MeshPacket_encrypted_tag;
}
if (iface) {
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
return iface->send(p);
} else {
DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
packetPool.release(p);
return ERRNO_NO_INTERFACES;
}
}
/**
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
*/
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
}
bool Router::perhapsDecode(MeshPacket *p)
{
if (p->which_payload == MeshPacket_decoded_tag)
return true; // If packet was already decoded just return
assert(p->which_payload == MeshPacket_encrypted_tag);
// FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without
// being able to decrypt their data.
// Try to decrypt the packet if we can
static uint8_t bytes[MAX_RHPACKETLEN];
memcpy(bytes, p->encrypted.bytes,
p->encrypted.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
// Take those raw bytes and convert them back into a well structured protobuf we can understand
if (!pb_decode_from_bytes(bytes, p->encrypted.size, SubPacket_fields, &p->decoded)) {
DEBUG_MSG("Invalid protobufs in received mesh packet!\n");
return false;
} else {
// parsing was successful
p->which_payload = MeshPacket_decoded_tag;
return true;
}
}
NodeNum Router::getNodeNum()
{
return nodeDB.getNodeNum();
}
/**
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
*/
void Router::handleReceived(MeshPacket *p)
{
// FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function
// Also, we should set the time from the ISR and it should have msec level resolution
p->rx_time = getValidTime(); // store the arrival timestamp for the phone
// Take those raw bytes and convert them back into a well structured protobuf we can understand
if (perhapsDecode(p)) {
// parsing was successful, queue for our recipient
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);
notifyPacketReceived.notifyObservers(p);
}
}
}
void Router::perhapsHandleReceived(MeshPacket *p)
{
assert(radioConfig.has_preferences);
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from);
if (ignore)
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
else if (ignore |= shouldFilterReceived(p)) {
// DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from);
}
// Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might
// cache/learn of the existence of nodes (i.e. FloodRouter) that they should not
if (!ignore)
handleReceived(p);
packetPool.release(p);
}

120
src/mesh/Router.h Normal file
View File

@@ -0,0 +1,120 @@
#pragma once
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "mesh.pb.h"
/**
* A mesh aware router that supports multiple interfaces.
*/
class Router
{
private:
RadioInterface *iface;
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone.
PointerQueue<MeshPacket> fromRadioQueue;
public:
/// Local services that want to see _every_ packet this node receives can observe this.
/// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting
/// freed)
Observable<const MeshPacket *> notifyPacketReceived;
/**
* Constructor
*
*/
Router();
/**
* Currently we only allow one interface, that may change in the future
*/
void addInterface(RadioInterface *_iface)
{
iface = _iface;
iface->setReceiver(&fromRadioQueue);
}
/**
* do idle processing
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
*/
virtual void loop();
/**
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue
*
* NOTE: This method will free the provided packet (even if we return an error code)
*/
ErrorCode sendLocal(MeshPacket *p);
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
MeshPacket *allocForSending();
/**
* @return our local nodenum */
NodeNum getNodeNum();
protected:
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*
* NOTE: This method will free the provided packet (even if we return an error code)
*/
virtual ErrorCode send(MeshPacket *p);
/**
* Should this incoming filter be dropped?
*
* Called immedately on receiption, before any further processing.
* @return true to abandon the packet
*/
virtual bool shouldFilterReceived(const MeshPacket *p) { return false; }
/**
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
*/
virtual void sniffReceived(const MeshPacket *p);
/**
* Remove any encryption and decode the protobufs inside this packet (if necessary).
*
* @return true for success, false for corrupt packet.
*/
bool perhapsDecode(MeshPacket *p);
private:
/**
* Called from loop()
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
*
* Note: this packet will never be called for messages sent/generated by this node.
* Note: this method will free the provided packet.
*/
void perhapsHandleReceived(MeshPacket *p);
/**
* Called from perhapsHandleReceived() - allows subclass message delivery behavior.
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
*
* Note: this packet will never be called for messages sent/generated by this node.
* Note: this method will free the provided packet.
*/
void handleReceived(MeshPacket *p);
};
extern Router &router;
/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId();

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