mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-21 18:22:32 +00:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a54bbcf2a | ||
|
|
02dfe7564f | ||
|
|
b704911603 | ||
|
|
0193c281ef | ||
|
|
59086fd477 | ||
|
|
88a704c4d3 | ||
|
|
9f49f90acd | ||
|
|
93f69d5a94 | ||
|
|
cceecf5f8e | ||
|
|
e5d2d24e2c | ||
|
|
eb40013ddc | ||
|
|
dda946d933 | ||
|
|
178e800969 | ||
|
|
dec4870649 | ||
|
|
97f5a7c5fe | ||
|
|
7ff42b89a6 | ||
|
|
44de3bd179 | ||
|
|
15cb599cd1 | ||
|
|
038b7c9c91 | ||
|
|
724e39bc92 | ||
|
|
def86131f0 | ||
|
|
8f1c1a9049 | ||
|
|
3f3a1a11df | ||
|
|
64f6c0f5c0 | ||
|
|
7cd60d859e | ||
|
|
db11d9280c | ||
|
|
ca03110932 | ||
|
|
b8b503cb0a | ||
|
|
bb885a5110 | ||
|
|
bebaa838c4 | ||
|
|
7fa9d09d9f | ||
|
|
7bc299573f | ||
|
|
e0a1855429 | ||
|
|
5e75beff3f | ||
|
|
4f7e85c1a4 | ||
|
|
5ad30caf67 | ||
|
|
8f26ae240a | ||
|
|
d445cbe083 | ||
|
|
12599849db | ||
|
|
3c9c01189d | ||
|
|
4f3a9d8646 | ||
|
|
0c7c3f17e5 | ||
|
|
5b0451f25c | ||
|
|
8f3b33c84c | ||
|
|
2fdb75efdf | ||
|
|
fbd12e1929 | ||
|
|
ffe95f62ab | ||
|
|
3e4ccef992 | ||
|
|
309e7be00c | ||
|
|
f0f6c4950b | ||
|
|
16998ebd8d | ||
|
|
b77c068881 | ||
|
|
e94227cddd | ||
|
|
fe3cbeed3a | ||
|
|
2061706c11 | ||
|
|
a0b6d57591 | ||
|
|
3673f95fe5 | ||
|
|
c67b53b969 | ||
|
|
562b227c73 | ||
|
|
169d85d0fa | ||
|
|
bd77d47215 | ||
|
|
e40524baf0 | ||
|
|
31f735ae1f | ||
|
|
9232dfcccf | ||
|
|
fab616a15e | ||
|
|
2419ebb04e | ||
|
|
88c576798b | ||
|
|
db766f18ed | ||
|
|
f9a805e3d4 | ||
|
|
e5f9a752d8 | ||
|
|
20b41836e2 | ||
|
|
c55df4d1cc | ||
|
|
908b1020c0 | ||
|
|
78470ed3f5 | ||
|
|
4ce7df295e | ||
|
|
598abb0d23 | ||
|
|
184eac6281 | ||
|
|
176532f55f | ||
|
|
8eb3045451 | ||
|
|
2fe145aed9 | ||
|
|
ab61e64056 | ||
|
|
5b17417e0c | ||
|
|
04a83fd6b7 | ||
|
|
7730bd762a | ||
|
|
62286fff52 | ||
|
|
25cca0628d | ||
|
|
65406eaa08 | ||
|
|
ea24394110 | ||
|
|
6afeb3e456 | ||
|
|
f108c576a7 | ||
|
|
6eb74415ab | ||
|
|
0d14b69a24 | ||
|
|
d4eb47e837 | ||
|
|
86716c4397 | ||
|
|
7bbcf101c1 | ||
|
|
8e495e417c | ||
|
|
a6e0d6d751 | ||
|
|
cff255a397 | ||
|
|
3336e821ad | ||
|
|
d75d603218 | ||
|
|
bf5be49186 | ||
|
|
39ff26aa45 | ||
|
|
02b5efed96 | ||
|
|
0a07c5692c | ||
|
|
2464784f00 | ||
|
|
a8f5ff1fbd | ||
|
|
0a6af936ed | ||
|
|
0b62083e35 | ||
|
|
5ca149fac9 | ||
|
|
80c69c28cd | ||
|
|
5904d66111 | ||
|
|
fd17193d5e | ||
|
|
5c379c4a98 | ||
|
|
9c5d626e7d | ||
|
|
4757b6807e | ||
|
|
ac7f3cd603 | ||
|
|
04ad8bb533 | ||
|
|
5924690ce3 | ||
|
|
3ac3baf1c1 | ||
|
|
aa88e2bb1f | ||
|
|
640cb3bf7f | ||
|
|
6ad451eb5f | ||
|
|
93a06906cb | ||
|
|
3754950a0e | ||
|
|
06a17885eb | ||
|
|
33624b7753 | ||
|
|
2d6b9320a5 | ||
|
|
6b35454570 | ||
|
|
753a57230d | ||
|
|
9c3e63e6b5 | ||
|
|
4d4d7b38cc | ||
|
|
23f148f8ce | ||
|
|
d150549754 | ||
|
|
441c4c7b3c | ||
|
|
c22df18e28 | ||
|
|
e32b41938a | ||
|
|
ef395a1596 | ||
|
|
af35f3006c | ||
|
|
13ac686c96 | ||
|
|
05a0266fc4 | ||
|
|
3acbf31198 | ||
|
|
2e82bf3ca1 | ||
|
|
63e1a3f47e | ||
|
|
07a8972aea | ||
|
|
d1cb45aa5d | ||
|
|
1b3610d0fa | ||
|
|
2fcdc2c09f | ||
|
|
da74803ffb | ||
|
|
890511615f | ||
|
|
17de6f9532 | ||
|
|
2a15195c32 | ||
|
|
1b050a6c3d | ||
|
|
1da62e5ba1 | ||
|
|
feb6f2e59e | ||
|
|
7dabad1b70 | ||
|
|
a73f466473 | ||
|
|
1fd92cf8eb | ||
|
|
dd671ceca6 | ||
|
|
8a76931b37 | ||
|
|
b071eecce1 | ||
|
|
81734f75c8 | ||
|
|
11d57e721a | ||
|
|
9b0e329bb9 | ||
|
|
450fb7bc35 | ||
|
|
c1f8c8cca4 | ||
|
|
ea250d9cd3 | ||
|
|
be468a2183 | ||
|
|
a47d6c4d68 | ||
|
|
5386a5b224 | ||
|
|
a350b3795b | ||
|
|
cf2aa37635 | ||
|
|
d1387be015 | ||
|
|
f8857ad45b | ||
|
|
d831beab3d | ||
|
|
5c4ae6c042 | ||
|
|
a0c97825e8 | ||
|
|
cc3bac7ea0 | ||
|
|
ce21859ada | ||
|
|
54cd082bfe |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,5 @@ main/credentials.h
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -12,7 +12,7 @@
|
||||
"type": "platformio-debug",
|
||||
"request": "launch",
|
||||
"name": "PIO Debug",
|
||||
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/esp32/firmware.elf",
|
||||
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/tbeam/firmware.elf",
|
||||
"toolchainBinDir": "/home/kevinh/.platformio/packages/toolchain-xtensa32/bin",
|
||||
"preLaunchTask": {
|
||||
"type": "PlatformIO",
|
||||
@@ -24,7 +24,7 @@
|
||||
"type": "platformio-debug",
|
||||
"request": "launch",
|
||||
"name": "PIO Debug (skip Pre-Debug)",
|
||||
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/esp32/firmware.elf",
|
||||
"executable": "/home/kevinh/development/meshtastic/meshtastic-esp32/.pio/build/tbeam/firmware.elf",
|
||||
"toolchainBinDir": "/home/kevinh/.platformio/packages/toolchain-xtensa32/bin",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
|
||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -42,7 +42,12 @@
|
||||
"typeinfo": "cpp",
|
||||
"string": "cpp",
|
||||
"*.xbm": "cpp",
|
||||
"list": "cpp"
|
||||
"list": "cpp",
|
||||
"atomic": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"optional": "cpp",
|
||||
"string_view": "cpp",
|
||||
"cassert": "cpp"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"Meshtastic",
|
||||
|
||||
84
README.md
84
README.md
@@ -1,39 +1,56 @@
|
||||
# Meshtastic-esp32
|
||||
This is the device side code for the [meshtastic.org](https://www.meshtastic.org) project.
|
||||
|
||||
This is the device side code for the [meshtastic.org](https://www.meshtastic.org) project.
|
||||
|
||||

|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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 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.
|
||||
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 a 18650 battery (total cost less than $35). Make
|
||||
sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen
|
||||
is optional, but highly recommended.
|
||||
|
||||
We currently support 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.
|
||||
|
||||
See (meshtastic.org) for 3D printable cases.
|
||||
|
||||
## Installing the firmware
|
||||
Prebuilt binaries for the supported radios is available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
||||
## Firmware Installation
|
||||
|
||||
The instructions currently require a few commmand lines, but it should be pretty straightforward. Please post comments on our group chat if you have problems or successes. Steps to install:
|
||||
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
||||
|
||||
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
||||
|
||||
### Installing from a GUI - Windows and Mac
|
||||
|
||||
1. Download and unzip the latest Meshtastic firmware [release](https://github.com/meshtastic/Meshtastic-esp32/releases).
|
||||
2. Download [ESPHome Flasher](https://github.com/esphome/esphome-flasher/releases) (either x86-32bit Windows or x64-64 bit Windows).
|
||||
3. Connect your radio to your USB port and open ESPHome Flasher.
|
||||
4. If your board is not showing under Serial Port then you likely need to install the drivers for the CP210X serial chip. In Windows you can check by searching “Device Manager” and ensuring the device is shown under “Ports”.
|
||||
5. If there is an error, download the drivers [here](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers), then unzip and run the Installer application.
|
||||
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 don’t have a screen).
|
||||
|
||||
### Installing from a commandline
|
||||
|
||||
These instructions currently require a few commmand lines, but it should be pretty straightforward.
|
||||
|
||||
1. Install "pip". Pip is the python package manager we use to get the esptool installer app. Instructions [here](https://www.makeuseof.com/tag/install-pip-for-python/). If you are using OS-X, see these [special instructions](docs/software/install-OSX.md).
|
||||
2. Run "pip install --upgrade esptool" to get esptool installed on your machine.
|
||||
3. Connect your radio to your USB port.
|
||||
4. Confirm that your device is talking to your PC by running "esptool.py chip_id". The Heltec build also works on the TTGO LORA32 radio. You should see something like:
|
||||
|
||||
1. Purchase a radio (see above) with the correct frequencies for your country (915MHz for US or JP, 470MHz for CN, 433MHz and 870MHz for EU).
|
||||
2. Install "pip". Pip is the python package manager we use to get the esptool installer app. Instructions [here](https://www.makeuseof.com/tag/install-pip-for-python/).
|
||||
3. Run "pip install --upgrade esptool" to get esptool installed on your machine
|
||||
4. Connect your radio to your USB port
|
||||
5. Confirm that your device is talking to your PC by running "esptool.py chip_id". The Heltec build also works on the TTGO LORA32 radio. You should see something like:
|
||||
```
|
||||
mydir$ esptool.py chip_id
|
||||
esptool.py v2.6
|
||||
@@ -51,14 +68,16 @@ Warning: ESP32 has no Chip ID. Reading MAC instead.
|
||||
MAC: 24:6f:28:b5:36:71
|
||||
Hard resetting via RTS pin...
|
||||
```
|
||||
6. cd into the directory where the release zip file was expanded.
|
||||
7. 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".
|
||||
|
||||
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.
|
||||
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".
|
||||
|
||||
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.
|
||||
|
||||
You should see something like this:
|
||||
|
||||
```
|
||||
kevinh@kevin-server:~/development/meshtastic/meshtastic-esp32/release/latest$ ./device-install.sh firmware-TBEAM-US-0.1.8.bin
|
||||
kevinh@kevin-server:~/development/meshtastic/meshtastic-esp32/release/latest$ ./device-install.sh firmware-TBEAM-US-0.1.8.bin
|
||||
Trying to flash firmware-TBEAM-US-0.1.8.bin, but first erasing and writing system information
|
||||
esptool.py v2.6
|
||||
Found 2 serial ports
|
||||
@@ -120,29 +139,34 @@ Hash of data verified.
|
||||
Leaving...
|
||||
Hard resetting via RTS pin...
|
||||
```
|
||||
8. The board will boot and show the Meshtastic logo.
|
||||
9. 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.
|
||||
|
||||
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
|
||||
The source code for the (optional) Meshtastic Android app is [here](https://github.com/meshtastic/Meshtastic-Android).
|
||||
|
||||
Alpha test builds are current available by opting into our alpha test group. See (www.meshtastic.org) for instructions.
|
||||
The source code for the (optional) Meshtastic Android app is [here](https://github.com/meshtastic/Meshtastic-Android).
|
||||
|
||||
Alpha test builds are current available by opting into our alpha test group. See (www.meshtastic.org) for instructions.
|
||||
|
||||
After our rate of change slows a bit, we will make beta builds available here (without needing to join the alphatest group):
|
||||
[](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)
|
||||
|
||||
# Development
|
||||
|
||||
We'd love to have you join us on this merry little project. Please see our [development documents](./docs/software/sw-design.md) and [join us in our discussion forum](https://meshtastic.discourse.group/).
|
||||
We'd love to have you join us on this merry little project. Please see our [development documents](./docs/software/sw-design.md) and [join us in our discussion forum](https://meshtastic.discourse.group/).
|
||||
|
||||
# Credits
|
||||
|
||||
This project is run by volunteers. Past contributors include:
|
||||
This project is run by volunteers. Past contributors include:
|
||||
|
||||
* @astro-arphid: Added support for 433MHz radios in europe.
|
||||
* @claesg: Various documentation fixes and 3D print enclosures
|
||||
* @girtsf: So far our CI system, but soon lots of device improvements
|
||||
- @astro-arphid: Added support for 433MHz radios in europe.
|
||||
- @claesg: Various documentation fixes and 3D print enclosures
|
||||
- @girtsf: Lots of improvements
|
||||
- @spattinson: Fixed interrupt handling for the AXP192 part
|
||||
|
||||
# IMPORTANT DISCLAIMERS AND FAQ
|
||||
|
||||
For a listing of currently missing features and a FAQ click [here](docs/faq.md).
|
||||
For a listing of currently missing features and a FAQ click [here](docs/faq.md).
|
||||
|
||||
Copyright 2019 Geeksville Industries, LLC. GPL V3 Licensed.
|
||||
|
||||
@@ -6,10 +6,11 @@ source bin/version.sh
|
||||
|
||||
COUNTRIES="US EU433 EU865 CN JP"
|
||||
#COUNTRIES=US
|
||||
#COUNTRIES=CN
|
||||
|
||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
|
||||
#BOARDS=tbeam
|
||||
|
||||
SRCMAP=.pio/build/esp32/output.map
|
||||
SRCBIN=.pio/build/esp32/firmware.bin
|
||||
SRCELF=.pio/build/esp32/firmware.elf
|
||||
OUTDIR=release/latest
|
||||
|
||||
# We keep all old builds (and their map files in the archive dir)
|
||||
@@ -17,29 +18,35 @@ ARCHIVEDIR=release/archive
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
|
||||
mkdir -p $OUTDIR/bins $OUTDIR/elfs
|
||||
rm -f $OUTDIR/bins/*
|
||||
|
||||
# build the named environment and copy the bins to the release directory
|
||||
function do_build {
|
||||
ENV_NAME=$1
|
||||
echo "Building for $ENV_NAME with $PLATFORMIO_BUILD_FLAGS"
|
||||
SRCBIN=.pio/build/$ENV_NAME/firmware.bin
|
||||
SRCELF=.pio/build/$ENV_NAME/firmware.elf
|
||||
rm -f $SRCBIN
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export HW_VERSION="1.0-$COUNTRY"
|
||||
export APP_VERSION=$VERSION
|
||||
export COUNTRY
|
||||
|
||||
pio run --jobs 4 --environment $ENV_NAME # -v
|
||||
cp $SRCBIN $OUTDIR/bins/firmware-$ENV_NAME-$COUNTRY-$VERSION.bin
|
||||
cp $SRCELF $OUTDIR/elfs/firmware-$ENV_NAME-$COUNTRY-$VERSION.elf
|
||||
}
|
||||
|
||||
for COUNTRY in $COUNTRIES; do
|
||||
|
||||
HWVERSTR="1.0-$COUNTRY"
|
||||
COMMONOPTS="-DAPP_VERSION=$VERSION -DHW_VERSION_$COUNTRY -DHW_VERSION=$HWVERSTR -Wall -Wextra -Wno-missing-field-initializers -Isrc -Os -Wl,-Map,.pio/build/esp32/output.map -DAXP_DEBUG_PORT=Serial"
|
||||
|
||||
export PLATFORMIO_BUILD_FLAGS="-DT_BEAM_V10 $COMMONOPTS"
|
||||
echo "Building with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f $SRCBIN $SRCMAP
|
||||
pio run # -v
|
||||
cp $SRCBIN $OUTDIR/firmware-TBEAM-$COUNTRY-$VERSION.bin
|
||||
cp $SRCELF $OUTDIR/firmware-TBEAM-$COUNTRY-$VERSION.elf
|
||||
#cp $SRCMAP $ARCHIVEDIR/firmware-TBEAM-$COUNTRY-$VERSION.map
|
||||
|
||||
export PLATFORMIO_BUILD_FLAGS="-DHELTEC_LORA32 $COMMONOPTS"
|
||||
rm -f $SRCBIN $SRCMAP
|
||||
pio run # -v
|
||||
cp $SRCBIN $OUTDIR/firmware-HELTEC-$COUNTRY-$VERSION.bin
|
||||
cp $SRCELF $OUTDIR/firmware-HELTEC-$COUNTRY-$VERSION.elf
|
||||
#cp $SRCMAP $ARCHIVEDIR/firmware-HELTEC-$COUNTRY-$VERSION.map
|
||||
for BOARD in $BOARDS; do
|
||||
do_build $BOARD
|
||||
done
|
||||
done
|
||||
|
||||
# keep the bins in archive also
|
||||
cp $OUTDIR/firmware* $ARCHIVEDIR
|
||||
cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $ARCHIVEDIR
|
||||
|
||||
cat >$OUTDIR/curfirmwareversion.xml <<XML
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -54,6 +61,6 @@ Generated by bin/buildall.sh -->
|
||||
XML
|
||||
|
||||
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
|
||||
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/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
|
||||
|
||||
echo BUILT ALL
|
||||
1
bin/nrf52-console.sh
Executable file
1
bin/nrf52-console.sh
Executable file
@@ -0,0 +1 @@
|
||||
JLinkRTTViewer
|
||||
@@ -3,4 +3,4 @@ set -e
|
||||
|
||||
source bin/version.sh
|
||||
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/firmware-HELTEC-US-$VERSION.bin
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-heltec-US-$VERSION.bin
|
||||
|
||||
@@ -3,4 +3,4 @@ set -e
|
||||
|
||||
source bin/version.sh
|
||||
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/firmware-TBEAM-US-$VERSION.bin
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-tbeam-US-$VERSION.bin
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1"
|
||||
# the nanopb tool seems to require that the .options file be in the current directory!
|
||||
cd proto
|
||||
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src -I=../proto mesh.proto
|
||||
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto mesh.proto
|
||||
|
||||
1
bin/run-1-monitor.sh
Executable file
1
bin/run-1-monitor.sh
Executable file
@@ -0,0 +1 @@
|
||||
pio run --upload-port /dev/ttyUSB1 -t upload -t monitor
|
||||
@@ -1 +1 @@
|
||||
pio device monitor -b 115200
|
||||
pio device monitor -b 921600
|
||||
|
||||
@@ -1 +1 @@
|
||||
pio device monitor -p /dev/ttyUSB1 -b 115200
|
||||
pio device monitor -p /dev/ttyUSB1 -b 921600
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
export VERSION=0.1.10
|
||||
export VERSION=0.6.0
|
||||
46
boards/ppr.json
Normal file
46
boards/ppr.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_PPR -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4403"]],
|
||||
"usb_product": "PPR",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "ppr",
|
||||
"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": "Meshtastic PPR (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": "Othernet"
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.geeksville.mesh",
|
||||
"sha256_cert_fingerprints":
|
||||
["D0:05:E7:8B:D2:1B:FA:94:56:1D:6B:90:EB:53:07:1A:74:4F:D9:C2:6F:13:87:6A:D9:17:4F:C2:59:48:02:9D"]
|
||||
["D0:05:E7:8B:D2:1B:FA:94:56:1D:6B:90:EB:53:07:1A:74:4F:D9:C2:6F:13:87:6A:D9:17:4F:C2:59:48:02:9D", "42:17:52:DC:57:40:38:B5:6B:86:61:1C:2F:47:DB:2B:0F:A2:EA:59:E1:18:9C:AA:90:8D:37:D6:CD:40:0E:BB", "A9:3B:45:65:68:C1:75:DB:08:00:A0:9F:06:77:7F:89:2D:81:24:32:AD:B8:A3:DF:73:BC:3E:7F:06:C8:0C:6D"]
|
||||
}
|
||||
}]
|
||||
}]
|
||||
|
||||
@@ -36,8 +36,9 @@ This software is 100% open source and developed by a group of hobbyist experimen
|
||||
|
||||
# Updates
|
||||
|
||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our chat, github releases or the Android alpha tester emails.
|
||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
||||
|
||||
- 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.
|
||||
- 02/23/2020 - 0.0.4 release. Still very bleeding edge but much closer to the final power management, a charged T-BEAM should run for many days with this load. If you'd like to try it, we'd love your feedback. Click [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md) for instructions.
|
||||
@@ -45,11 +46,11 @@ Note: Updates are happening almost daily, only major updates are listed below. F
|
||||
|
||||
## Meshtastic Android app
|
||||
|
||||
Once out of alpha the companion Android application will be released here:
|
||||
Our Android application is available here:
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dhomepage%26anid%3Dadmob)
|
||||
|
||||
But if you want the bleeding edge app now, we'd love to have your help testing. Three steps to opt-in to the alpha- test:
|
||||
The link above will return older more stable releases. We would prefer if you join our alpha-test group, because the application is rapidly improving. Three steps to opt-in to the alpha- test:
|
||||
|
||||
1. Join [this Google group](https://groups.google.com/forum/#!forum/meshtastic-alpha-testers) with the account you use in Google Play.
|
||||
2. Go to this [URL](https://play.google.com/apps/testing/com.geeksville.mesh) to opt-in to the alpha test.
|
||||
|
||||
@@ -3,3 +3,6 @@ theme: jekyll-theme-cayman
|
||||
title: Meshtastic
|
||||
description: An opensource hiking, pilot, skiing, Signal-App-extending GPS mesh communicator
|
||||
google_analytics: G-DRZ5H5EXHV
|
||||
|
||||
include: [".well-known"]
|
||||
|
||||
|
||||
24
docs/faq.md
24
docs/faq.md
@@ -1,27 +1,25 @@
|
||||
# Disclaimers
|
||||
|
||||
This project is still pretty young but moving at a pretty good pace. Not all features are fully implemented in the current alpha builds.
|
||||
This project is still pretty young but moving at a pretty good pace. Not all features are fully implemented in the current alpha builds.
|
||||
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
|
||||
* 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 current Android GUI is slightly ugly still
|
||||
* The Android API needs to be documented better
|
||||
* The mesh protocol is turned off for now, currently we only send packets one hop distant. The mesh feature will be turned on again [soonish](https://github.com/meshtastic/Meshtastic-esp32/issues/3).
|
||||
* No one has written an iOS app yet. But some good souls [are talking about it](https://github.com/meshtastic/Meshtastic-esp32/issues/14) ;-)
|
||||
- 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
|
||||
- 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) ;-)
|
||||
|
||||
For more details see the [device software TODO](https://github.com/meshtastic/Meshtastic-esp32/blob/master/docs/software/TODO.md) or the [Android app TODO](https://github.com/meshtastic/Meshtastic-Android/blob/master/TODO.md).
|
||||
|
||||
# FAQ
|
||||
|
||||
If you have a question missing from this faq, please [ask in our discussion forum](https://meshtastic.discourse.group/). And if you are feeling extra generous send in a pull-request for this faq.md with whatever we answered ;-).
|
||||
If you have a question missing from this faq, please [ask in our discussion forum](https://meshtastic.discourse.group/). And if you are feeling extra generous send in a pull-request for this faq.md with whatever we answered ;-).
|
||||
|
||||
## Q: Which of the various supported radios should I buy?
|
||||
|
||||
Basically you just need the radio + (optional but recommended) battery. The TBEAM is usually better because it has gps and huge battery socket. The Heltec is basically the same hardware but without the GPS (the phone provides position data to the radio in that case, so the behavior is similar - but it does burn some battery in the phone). Also the battery for the Heltec can be smaller.
|
||||
Basically you just need the radio + (optional but recommended) battery. The TBEAM is usually better because it has gps and huge battery socket. The Heltec is basically the same hardware but without the GPS (the phone provides position data to the radio in that case, so the behavior is similar - but it does burn some battery in the phone). Also the battery for the Heltec can be smaller.
|
||||
|
||||
In addition to Aliexpress, (banggood.com) usually has stock and faster shipping, or Amazon. If buying a TBEAM, make sure to buy a version that includes the OLED screen - this project doesn't absolutely require the screen, but we use it if is installed.
|
||||
In addition to Aliexpress, (banggood.com) usually has stock and faster shipping, or Amazon. If buying a TBEAM, make sure to buy a version that includes the OLED screen - this project doesn't absolutely require the screen, but we use it if is installed.
|
||||
|
||||
@claesg has added links to various 3D printable cases, you can see them at (www.meshtastic.org).
|
||||
|
||||
@@ -29,8 +27,8 @@ In addition to Aliexpress, (banggood.com) usually has stock and faster shipping,
|
||||
|
||||
Nope. though if some other person/group wanted to use this software and a more customized device we think that would be awesome (as long as they obey the GPL license).
|
||||
|
||||
## Q: Does this project use patented algorithms?
|
||||
## Q: Does this project use patented algorithms?
|
||||
|
||||
(Kindly borrowed from the geeks at [ffmpeg](http://ffmpeg.org/legal.html))
|
||||
|
||||
We do not know, we are not lawyers so we are not qualified to answer this. Also we have never read patents to implement any part of this, so even if we were qualified we could not answer it as we do not know what is patented. Furthermore the sheer number of software patents makes it impossible to read them all so no one (lawyer or not) could answer such a question with a definite no. We are merely geeks experimenting on a fun and free project.
|
||||
We do not know, we are not lawyers so we are not qualified to answer this. Also we have never read patents to implement any part of this, so even if we were qualified we could not answer it as we do not know what is patented. Furthermore the sheer number of software patents makes it impossible to read them all so no one (lawyer or not) could answer such a question with a definite no. We are merely geeks experimenting on a fun and free project.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
TODO:
|
||||
* reread the radiohead mesh implementation
|
||||
* read about general mesh flooding solutions
|
||||
* reread the disaster radio protocol docs
|
||||
|
||||
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
|
||||
It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728
|
||||
https://en.wikipedia.org/wiki/Dynamic_Source_Routing
|
||||
|
||||
broadcast solution:
|
||||
Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent)
|
||||
Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set
|
||||
|
||||
compare to db sync algorithm?
|
||||
|
||||
what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies.
|
||||
|
||||
If Y were to die, at least the neighbor nodes of Y would have their last known position of Y.
|
||||
@@ -17,6 +17,10 @@ Items to complete soon (next couple of alpha releases).
|
||||
|
||||
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/
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
# Bluetooth API
|
||||
|
||||
The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
|
||||
The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
|
||||
|
||||
## A note on MTU sizes
|
||||
|
||||
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
|
||||
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
|
||||
|
||||
## MeshBluetoothService
|
||||
## MeshBluetoothService
|
||||
|
||||
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.
|
||||
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
|
||||
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:
|
||||
|
||||
* SetMTU size to 512
|
||||
* 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 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"
|
||||
- SetMTU size to 512
|
||||
- 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 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"
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
UUID for the service: 6ba1b218-15a8-461f-9fa8-5dcae273eafd
|
||||
|
||||
@@ -37,7 +37,7 @@ Description (including human readable name)
|
||||
8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
read
|
||||
fromradio - contains a newly received FromRadio packet destined towards the phone (up to MAXPACKET bytes per packet).
|
||||
After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this
|
||||
After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this
|
||||
mailbox.
|
||||
|
||||
f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
@@ -49,34 +49,22 @@ read,notify,write
|
||||
fromnum - the current packet # in the message waiting inside fromradio, if the phone sees this notify it should read messages
|
||||
until it catches up with this number.
|
||||
|
||||
The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32 callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio.
|
||||
The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32 callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio.
|
||||
When the esp32 advances fromnum, it will delay doing the notify by 100ms, in the hopes that the notify will never actally need to be sent if the phone is already pulling from fromradio.
|
||||
|
||||
Note: that if the phone ever sees this number decrease, it means the esp32 has rebooted.
|
||||
|
||||
ea9f3f82-8dc4-4733-9452-1f6da28892a2
|
||||
read
|
||||
mynode - read this to access a MyNodeInfo protobuf
|
||||
|
||||
d31e02e0-c8ab-4d3f-9cc9-0b8466bdabe8
|
||||
read, write
|
||||
nodeinfo - read this to get a series of NodeInfos (ending with a null empty record), write to this to restart the read statemachine that returns all the node infos
|
||||
|
||||
b56786c8-839a-44a1-b98e-a1724c4a0262
|
||||
read,write
|
||||
radio - read/write this to access a RadioConfig protobuf
|
||||
|
||||
6ff1d8b6-e2de-41e3-8c0b-8fa384f64eb6
|
||||
read,write
|
||||
owner - read/write this to access a User protobuf
|
||||
|
||||
Re: queue management
|
||||
Not all messages are kept in the fromradio queue (filtered based on SubPacket):
|
||||
* only the most recent Position and User messages for a particular node are kept
|
||||
* all Data SubPackets are kept
|
||||
* No WantNodeNum / DenyNodeNum messages are kept
|
||||
A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging)
|
||||
|
||||
- only the most recent Position and User messages for a particular node are kept
|
||||
- all Data SubPackets are kept
|
||||
- No WantNodeNum / DenyNodeNum messages are kept
|
||||
A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging)
|
||||
|
||||
## Protobuf API
|
||||
|
||||
On connect, you should send a want_config_id protobuf to the device. This will cause the device to send its node DB and radio config via the fromradio endpoint. After sending the full DB, the radio will send a want_config_id to indicate it is done sending the configuration.
|
||||
|
||||
## Other bluetooth services
|
||||
|
||||
@@ -85,21 +73,21 @@ provided by the device.
|
||||
|
||||
### BluetoothSoftwareUpdate
|
||||
|
||||
The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt).
|
||||
The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt).
|
||||
|
||||
SoftwareUpdateService UUID cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30
|
||||
|
||||
Characteristics
|
||||
|
||||
| UUID | properties | description|
|
||||
|--------------------------------------|------------------|------------|
|
||||
| e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e | write,read | total image size, 32 bit, write this first, then read read back to see if it was acceptable (0 mean not accepted) |
|
||||
| e272ebac-d463-4b98-bc84-5cc1a39ee517 | write | data, variable sized, recommended 512 bytes, write one for each block of file |
|
||||
| 4826129c-c22a-43a3-b066-ce8f0d5bacc6 | write | crc32, write last - writing this will complete the OTA operation, now you can read result |
|
||||
| 5e134862-7411-4424-ac4a-210937432c77 | read,notify | result code, readable but will notify when the OTA operation completes |
|
||||
| GATT_UUID_SW_VERSION_STR/0x2a28 | read | We also implement these standard GATT entries because SW update probably needs them: |
|
||||
| GATT_UUID_MANU_NAME/0x2a29 | read | |
|
||||
| GATT_UUID_HW_VERSION_STR/0x2a27 | read | |
|
||||
| UUID | properties | description |
|
||||
| ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e | write,read | total image size, 32 bit, write this first, then read read back to see if it was acceptable (0 mean not accepted) |
|
||||
| e272ebac-d463-4b98-bc84-5cc1a39ee517 | write | data, variable sized, recommended 512 bytes, write one for each block of file |
|
||||
| 4826129c-c22a-43a3-b066-ce8f0d5bacc6 | write | crc32, write last - writing this will complete the OTA operation, now you can read result |
|
||||
| 5e134862-7411-4424-ac4a-210937432c77 | read,notify | result code, readable but will notify when the OTA operation completes |
|
||||
| GATT_UUID_SW_VERSION_STR/0x2a28 | read | We also implement these standard GATT entries because SW update probably needs them: |
|
||||
| GATT_UUID_MANU_NAME/0x2a29 | read | |
|
||||
| GATT_UUID_HW_VERSION_STR/0x2a27 | read | |
|
||||
|
||||
### DeviceInformationService
|
||||
|
||||
@@ -107,4 +95,4 @@ Implements the standard BLE contract for this service (has software version, har
|
||||
|
||||
### BatteryLevelService
|
||||
|
||||
Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically)
|
||||
Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically)
|
||||
|
||||
@@ -6,19 +6,15 @@ 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. Edit configuration.h and comment out *one* of the following two lines (depending on which board you are using):
|
||||
```
|
||||
// #define T_BEAM_V10
|
||||
#define HELTEC_LORA32
|
||||
```
|
||||
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 -t upload" (This command will fetch dependencies, build the project and install it on the board via USB)
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
## Decoding stack traces
|
||||
|
||||
If you get a crash, you can decode the addresses from the `Backtrace:` line:
|
||||
|
||||
1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`.
|
||||
2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the
|
||||
last `firmware.elf`, so you must be running the same binary that's still in
|
||||
|
||||
27
docs/software/install-OSX.md
Normal file
27
docs/software/install-OSX.md
Normal file
@@ -0,0 +1,27 @@
|
||||
(Here's some quick tips on installing the device code from OS-X, thanks to @android606)
|
||||
|
||||
First time using LoRa for anything, just checking it out.
|
||||
|
||||
I bought a T-Beam on eBay, followed the instructions to install the firmware here:
|
||||
[https://github.com/meshtastic/Meshtastic-esp32](https://github.com/meshtastic/Meshtastic-esp32)
|
||||
|
||||
I'm using a Mac for this, so that might account for differences in the steps to get it working. I just swapped out my SSD last month, I'm using a pretty fresh install of OS X 10.15.3/Catalina.
|
||||
|
||||
I got it working fairly smoothly, but there were two hang-ups I thought I'd mention:
|
||||
|
||||
1. I am about 0% familiar with Python, so there were some issues getting esptool.py working. Basically, this OS X comes with Python 2.7 and no pip. Pip installed okay, so I used it to install esptool. Esptool appeared to install correctly, but I couldn't get it to work to save my life. Simply typing "esptool.py" doesn't work, and I just don't know enough python to figure out why. For some reason, it installs but isn't in the \$PATH anywhere, and I don't know where it went. Python 2.7 kept giving me warning messages about being old and unsupported, so I figured that might be a hint that I should upgrade.
|
||||
|
||||
I ended up doing this:
|
||||
|
||||
- brew install pyenv (to install pyenv)
|
||||
- pyenv install 3.7.7 (to install and select python 3.7.7)
|
||||
- pyenv global 3.7.7 (to select the new version of python)
|
||||
- brew install pip (to install pip3)
|
||||
- pip3 install --upgrade esptool (note I specifically had to use "pip3", not "pip")
|
||||
|
||||
...then I was able to execute esptool.py
|
||||
|
||||
2. esptool.py didn't work though, because the virtual com port wasn't showing up as a device. I had to install a driver from Silicon Labs, which I got here:
|
||||
[driver for the CP210X USB to UART bridge from Silicon Labs](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers)
|
||||
|
||||
After I installed that, esptool.py was completely happy and the firmware loaded right up.
|
||||
@@ -1,72 +1,95 @@
|
||||
# Mesh broadcast algorithm
|
||||
|
||||
FIXME - instead look for standard solutions. this approach seems really suboptimal, because too many nodes will try to rebroast. If
|
||||
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/
|
||||
|
||||
TODO:
|
||||
* DONE reread the radiohead mesh implementation - hop to hop acknoledgement 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
|
||||
* 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)
|
||||
|
||||
- DONE reread the radiohead mesh implementation - hop to hop acknoledgement 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
|
||||
- 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
|
||||
|
||||
interesting paper on lora mesh: https://portal.research.lu.se/portal/files/45735775/paper.pdf
|
||||
It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728
|
||||
It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728
|
||||
https://en.wikipedia.org/wiki/Dynamic_Source_Routing
|
||||
|
||||
broadcast solution:
|
||||
Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent)
|
||||
Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set
|
||||
Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent)
|
||||
Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set
|
||||
|
||||
compare to db sync algorithm?
|
||||
|
||||
what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies.
|
||||
what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies.
|
||||
|
||||
If Y were to die, at least the neighbor nodes of Y would have their last known position of Y.
|
||||
|
||||
## approach 1
|
||||
|
||||
* send all broadcasts with a TTL
|
||||
* periodically(?) do a survey to find the max TTL that is needed to fully cover the current network.
|
||||
* to do a study first send a broadcast (maybe our current initial user announcement?) with TTL set to one (so therefore no one will rebroadcast our request)
|
||||
* survey replies are sent unicast back to us (and intervening nodes will need to keep the route table that they have built up based on past packets)
|
||||
* count the number of replies to this TTL 1 attempt. That is the number of nodes we can reach without any rebroadcasts
|
||||
* repeat the study with a TTL of 2 and then 3. stop once the # of replies stops going up.
|
||||
* it is important for any node to do listen before talk to prevent stomping on other rebroadcasters...
|
||||
* For these little networks I bet a max TTL would never be higher than 3?
|
||||
- send all broadcasts with a TTL
|
||||
- periodically(?) do a survey to find the max TTL that is needed to fully cover the current network.
|
||||
- to do a study first send a broadcast (maybe our current initial user announcement?) with TTL set to one (so therefore no one will rebroadcast our request)
|
||||
- survey replies are sent unicast back to us (and intervening nodes will need to keep the route table that they have built up based on past packets)
|
||||
- count the number of replies to this TTL 1 attempt. That is the number of nodes we can reach without any rebroadcasts
|
||||
- repeat the study with a TTL of 2 and then 3. stop once the # of replies stops going up.
|
||||
- it is important for any node to do listen before talk to prevent stomping on other rebroadcasters...
|
||||
- For these little networks I bet a max TTL would never be higher than 3?
|
||||
|
||||
## approach 2
|
||||
|
||||
* send a TTL1 broadcast, the replies let us build a list of the nodes (stored as a bitvector?) that we can see (and their rssis)
|
||||
* we then broadcast out that bitvector (also TTL1) asking "can any of ya'll (even indirectly) see anyone else?"
|
||||
* if a node can see someone I missed (and they are the best person to see that node), they reply (unidirectionally) with the missing nodes and their rssis (other nodes might sniff (and update their db) based on this reply but they don't have to)
|
||||
* given that the max number of nodes in this mesh will be like 20 (for normal cases), I bet globally updating this db of "nodenums and who has the best rssi for packets from that node" would be useful
|
||||
* once the global DB is shared, when a node wants to broadcast, it just sends out its broadcast . the first level receivers then make a decision "am I the best to rebroadcast to someone who likely missed this packet?" if so, rebroadcast
|
||||
- send a TTL1 broadcast, the replies let us build a list of the nodes (stored as a bitvector?) that we can see (and their rssis)
|
||||
- we then broadcast out that bitvector (also TTL1) asking "can any of ya'll (even indirectly) see anyone else?"
|
||||
- if a node can see someone I missed (and they are the best person to see that node), they reply (unidirectionally) with the missing nodes and their rssis (other nodes might sniff (and update their db) based on this reply but they don't have to)
|
||||
- given that the max number of nodes in this mesh will be like 20 (for normal cases), I bet globally updating this db of "nodenums and who has the best rssi for packets from that node" would be useful
|
||||
- once the global DB is shared, when a node wants to broadcast, it just sends out its broadcast . the first level receivers then make a decision "am I the best to rebroadcast to someone who likely missed this packet?" if so, rebroadcast
|
||||
|
||||
## approach 3
|
||||
|
||||
* when a node X wants to know other nodes positions, it broadcasts its position with want_replies=true. Then each of the nodes that received that request broadcast their replies (possibly by using special timeslots?)
|
||||
* all nodes constantly update their local db based on replies they witnessed.
|
||||
* after 10s (or whatever) if node Y notices that it didn't hear a reply from node Z (that Y has heard from recently ) to that initial request, that means Z never heard the request from X. Node Y will reply to X on Z's behalf.
|
||||
* could this work for more than one hop? Is more than one hop needed? Could it work for sending messages (i.e. for a msg sent to Z with want-reply set).
|
||||
- when a node X wants to know other nodes positions, it broadcasts its position with want_replies=true. Then each of the nodes that received that request broadcast their replies (possibly by using special timeslots?)
|
||||
- all nodes constantly update their local db based on replies they witnessed.
|
||||
- after 10s (or whatever) if node Y notices that it didn't hear a reply from node Z (that Y has heard from recently ) to that initial request, that means Z never heard the request from X. Node Y will reply to X on Z's behalf.
|
||||
- could this work for more than one hop? Is more than one hop needed? Could it work for sending messages (i.e. for a msg sent to Z with want-reply set).
|
||||
|
||||
## approach 4
|
||||
|
||||
look into the literature for this idea specifically.
|
||||
|
||||
* don't view it as a mesh protocol as much as a "distributed db unification problem". When nodes talk to nearby nodes they work together
|
||||
to update their nodedbs. Each nodedb would have a last change date and any new changes that only one node has would get passed to the
|
||||
other node. This would nicely allow distant nodes to propogate their position to all other nodes (eventually).
|
||||
* handle group messages the same way, there would be a table of messages and time of creation.
|
||||
* when a node has a new position or message to send out, it does a broadcast. All the adjacent nodes update their db instantly (this handles 90% of messages I'll bet).
|
||||
* Occasionally a node might broadcast saying "anyone have anything newer than time X?" If someone does, they send the diffs since that date.
|
||||
* essentially everything in this variant becomes broadcasts of "request db updates for >time X - for _all_ or for a particular nodenum" and nodes sending (either due to request or because they changed state) "here's a set of db updates". Every node is constantly trying to
|
||||
build the most recent version of reality, and if some nodes are too far, then nodes closer in will eventually forward their changes to the distributed db.
|
||||
* construct non ambigious rules for who broadcasts to request db updates. ideally the algorithm should nicely realize node X can see most other nodes, so they should just listen to all those nodes and minimize the # of broadcasts. the distributed picture of nodes rssi could be useful here?
|
||||
* possibly view the BLE protocol to the radio the same way - just a process of reconverging the node/msgdb database.
|
||||
- don't view it as a mesh protocol as much as a "distributed db unification problem". When nodes talk to nearby nodes they work together
|
||||
to update their nodedbs. Each nodedb would have a last change date and any new changes that only one node has would get passed to the
|
||||
other node. This would nicely allow distant nodes to propogate their position to all other nodes (eventually).
|
||||
- handle group messages the same way, there would be a table of messages and time of creation.
|
||||
- when a node has a new position or message to send out, it does a broadcast. All the adjacent nodes update their db instantly (this handles 90% of messages I'll bet).
|
||||
- Occasionally a node might broadcast saying "anyone have anything newer than time X?" If someone does, they send the diffs since that date.
|
||||
- essentially everything in this variant becomes broadcasts of "request db updates for >time X - for _all_ or for a particular nodenum" and nodes sending (either due to request or because they changed state) "here's a set of db updates". Every node is constantly trying to
|
||||
build the most recent version of reality, and if some nodes are too far, then nodes closer in will eventually forward their changes to the distributed db.
|
||||
- construct non ambigious rules for who broadcasts to request db updates. ideally the algorithm should nicely realize node X can see most other nodes, so they should just listen to all those nodes and minimize the # of broadcasts. the distributed picture of nodes rssi could be useful here?
|
||||
- possibly view the BLE protocol to the radio the same way - just a process of reconverging the node/msgdb database.
|
||||
|
||||
# Old notes
|
||||
|
||||
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
|
||||
It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728
|
||||
https://en.wikipedia.org/wiki/Dynamic_Source_Routing
|
||||
|
||||
broadcast solution:
|
||||
Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent)
|
||||
Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set
|
||||
|
||||
compare to db sync algorithm?
|
||||
|
||||
what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies.
|
||||
|
||||
If Y were to die, at least the neighbor nodes of Y would have their last known position of Y.
|
||||
|
||||
141
docs/software/nrf52-TODO.md
Normal file
141
docs/software/nrf52-TODO.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# NRF52 TODO
|
||||
|
||||
## 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
|
||||
- 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
|
||||
- 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.
|
||||
- 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
|
||||
- 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
|
||||
|
||||
## Items to be 'feature complete'
|
||||
|
||||
- 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
|
||||
- backport the common PMU API between AXP192 and PmuBQ25703A
|
||||
- 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)
|
||||
|
||||
## Things to do 'someday'
|
||||
|
||||
Nice ideas worth considering someday...
|
||||
|
||||
- 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.
|
||||
- decrease BLE xmit power "At 0dBm with the DC/DC on, the nRF52832 transmitter draws 5.3mA. Increasing the TX power to +4dBm adds only 2.2mA. Decreasing it to -40 dBm saves only 2.6mA."
|
||||
- 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?
|
||||
|
||||
## Old unorganized notes
|
||||
|
||||
## Notes on PCA10059 Dongle
|
||||
|
||||
- docs: https://infocenter.nordicsemi.com/pdf/nRF52840_Dongle_User_Guide_v1.0.pdf
|
||||
|
||||
- Currently using Nordic PCA10059 Dongle hardware
|
||||
- https://community.platformio.org/t/same-bootloader-same-softdevice-different-board-different-pins/11411/9
|
||||
|
||||
## Done
|
||||
|
||||
- DONE add "DFU trigger library" to application load
|
||||
- DONE: using this: Possibly use this bootloader? https://github.com/adafruit/Adafruit_nRF52_Bootloader
|
||||
|
||||
```
|
||||
|
||||
/*
|
||||
per
|
||||
https://docs.platformio.org/en/latest/tutorials/nordicnrf52/arduino_debugging_unit_testing.html
|
||||
|
||||
ardunino github is here https://github.com/sandeepmistry/arduino-nRF5
|
||||
devboard hw docs here:
|
||||
https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/nrf52840_DK/hw_buttons_leds.html?cp=4_0_4_7_6
|
||||
|
||||
https://docs.platformio.org/en/latest/boards/nordicnrf52/nrf52840_dk_adafruit.html
|
||||
|
||||
must install adafruit bootloader first!
|
||||
https://learn.adafruit.com/circuitpython-on-the-nrf52/nrf52840-bootloader
|
||||
see link above and turn off jlink filesystem if we see unreliable serial comms
|
||||
over USBCDC
|
||||
|
||||
adafruit bootloader install commands (from their readme)
|
||||
kevinh@kevin-server:~/.platformio/packages/framework-arduinoadafruitnrf52/bootloader$
|
||||
nrfjprog -e -f nrf52 Erasing user available code and UICR flash areas. Applying
|
||||
system reset.
|
||||
kevinh@kevin-server:~/.platformio/packages/framework-arduinoadafruitnrf52/bootloader$
|
||||
nrfjprog --program pca10056/pca10056_bootloader-0.3.2_s140_6.1.1.hex -f nrf52
|
||||
Parsing hex file.
|
||||
Reading flash area to program to guarantee it is erased.
|
||||
Checking that the area to write is not protected.
|
||||
Programming device.
|
||||
kevinh@kevin-server:~/.platformio/packages/framework-arduinoadafruitnrf52/bootloader$
|
||||
nrfjprog --reset -f nrf52 Applying system reset. Run.
|
||||
|
||||
install jlink tools from here:
|
||||
https://www.segger.com/downloads/jlink#J-LinkSoftwareAndDocumentationPack
|
||||
|
||||
install nrf tools from here:
|
||||
https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Command-Line-Tools/Download#infotabs
|
||||
|
||||
examples of turning off the loop call to save power:
|
||||
https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/advertising-beacon
|
||||
|
||||
example of a more complex BLE service:
|
||||
https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/custom-hrm
|
||||
*/
|
||||
|
||||
// See g_ADigitalPinMap to see how arduino maps to the real gpio#s - and all in
|
||||
// P0
|
||||
#define LED1 14
|
||||
#define LED2 13
|
||||
|
||||
/*
|
||||
good led ble demo:
|
||||
https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Peripheral/nrf_blinky/nrf_blinky.ino
|
||||
*/
|
||||
```
|
||||
@@ -4,34 +4,35 @@ i.e. sleep behavior
|
||||
|
||||
## Power measurements
|
||||
|
||||
Since one of the main goals of this project is long battery life, it is important to consider that in our software/protocol design. Based on initial measurements it seems that the current code should run about three days between charging, and with a bit more software work (see the [TODO list](TODO.md)) a battery life of eight days should be quite doable. Our current power measurements/model is in [this spreadsheet](https://docs.google.com/spreadsheets/d/1ft1bS3iXqFKU8SApU8ZLTq9r7QQEGESYnVgdtvdT67k/edit?usp=sharing).
|
||||
Since one of the main goals of this project is long battery life, it is important to consider that in our software/protocol design. Based on initial measurements it seems that the current code should run about three days between charging, and with a bit more software work (see the [TODO list](TODO.md)) a battery life of eight days should be quite doable. Our current power measurements/model is in [this spreadsheet](https://docs.google.com/spreadsheets/d/1ft1bS3iXqFKU8SApU8ZLTq9r7QQEGESYnVgdtvdT67k/edit?usp=sharing).
|
||||
|
||||
## States
|
||||
|
||||
From lower to higher power consumption.
|
||||
|
||||
* Super-deep-sleep (SDS) - everything is off, CPU, radio, bluetooth, GPS. Only wakes due to timer or button press. We enter this mode only after no radio comms for a few hours, used to put the device into what is effectively "off" mode.
|
||||
- Super-deep-sleep (SDS) - everything is off, CPU, radio, bluetooth, GPS. Only wakes due to timer or button press. We enter this mode only after no radio comms for a few hours, used to put the device into what is effectively "off" mode.
|
||||
onEntry: setBluetoothOn(false), call doDeepSleep
|
||||
onExit: (standard bootup code, starts in DARK)
|
||||
|
||||
* deep-sleep (DS) - CPU is off, radio is on, bluetooth and GPS is off. Note: This mode is never used currently, because it only saves 1.5mA vs light-sleep
|
||||
- deep-sleep (DS) - CPU is off, radio is on, bluetooth and GPS is off. Note: This mode is never used currently, because it only saves 1.5mA vs light-sleep
|
||||
(Not currently used)
|
||||
|
||||
* light-sleep (LS) - CPU is suspended (RAM stays alive), radio is on, bluetooth is off, GPS is off. Note: currently GPS is not turned
|
||||
off during light sleep, but there is a TODO item to fix this.
|
||||
onEntry: setBluetoothOn(false), setGPSPower(false), doLightSleep()
|
||||
- light-sleep (LS) - CPU is suspended (RAM stays alive), radio is on, bluetooth is off, GPS is off. Note: currently GPS is not turned
|
||||
off during light sleep, but there is a TODO item to fix this.
|
||||
NOTE: On NRF52 platforms (because CPU current draw is so low), light-sleep state is never used.
|
||||
onEntry: setBluetoothOn(false), setGPSPower(false), doLightSleep()
|
||||
onIdle: (if we wake because our led blink timer has expired) blink the led then go back to sleep until we sleep for ls_secs
|
||||
onExit: setGPSPower(true), start trying to get gps lock: gps.startLock(), once lock arrives service.sendPosition(BROADCAST)
|
||||
|
||||
* No bluetooth (NB) - CPU is running, radio is on, GPS is on but bluetooth is off, screen is off.
|
||||
- No bluetooth (NB) - CPU is running, radio is on, GPS is on but bluetooth is off, screen is off.
|
||||
onEntry: setBluetoothOn(false)
|
||||
onExit:
|
||||
onExit:
|
||||
|
||||
* running dark (DARK) - Everything is on except screen
|
||||
- running dark (DARK) - Everything is on except screen
|
||||
onEntry: setBluetoothOn(true)
|
||||
onExit:
|
||||
onExit:
|
||||
|
||||
* full on (ON) - Everything is on
|
||||
- full on (ON) - Everything is on
|
||||
onEntry: setBluetoothOn(true), screen.setOn(true)
|
||||
onExit: screen.setOn(false)
|
||||
|
||||
@@ -39,47 +40,47 @@ off during light sleep, but there is a TODO item to fix this.
|
||||
|
||||
### events that increase CPU activity
|
||||
|
||||
* At cold boot: The initial state (after setup() has run) is DARK
|
||||
* While in DARK: if we receive EVENT_BOOT, transition to ON (and show the bootscreen). This event will be sent if we detect we woke due to reset (as opposed to deep sleep)
|
||||
* While in LS: Once every position_broadcast_secs (default 15 mins) - the unit will wake into DARK mode and broadcast a "networkPing" (our position) and stay alive for wait_bluetooth_secs (default 30 seconds). This allows other nodes to have a record of our last known position if we go away and allows a paired phone to hear from us and download messages.
|
||||
* While in LS: Every send_owner_interval (defaults to 4, i.e. one hour), when we wake to send our position we _also_ broadcast our owner. This lets new nodes on the network find out about us or correct duplicate node number assignments.
|
||||
* While in LS/NB/DARK: If the user presses a button (EVENT_PRESS) we go to full ON mode for screen_on_secs (default 30 seconds). Multiple presses keeps resetting this timeout
|
||||
* While in LS/NB/DARK: If we receive new text messages (EVENT_RECEIVED_TEXT_MSG), we go to full ON mode for screen_on_secs (same as if user pressed a button)
|
||||
* While in LS: while we receive packets on the radio (EVENT_RECEIVED_PACKET) we will wake and handle them and stay awake in NB mode for min_wake_secs (default 10 seconds)
|
||||
* While in NB: If we do have packets the phone (EVENT_PACKETS_FOR_PHONE) would want we transition to DARK mode for wait_bluetooth secs.
|
||||
* 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)
|
||||
- At cold boot: The initial state (after setup() has run) is DARK
|
||||
- While in DARK: if we receive EVENT_BOOT, transition to ON (and show the bootscreen). This event will be sent if we detect we woke due to reset (as opposed to deep sleep)
|
||||
- While in LS: Once every position_broadcast_secs (default 15 mins) - the unit will wake into DARK mode and broadcast a "networkPing" (our position) and stay alive for wait_bluetooth_secs (default 30 seconds). This allows other nodes to have a record of our last known position if we go away and allows a paired phone to hear from us and download messages.
|
||||
- While in LS: Every send*owner_interval (defaults to 4, i.e. one hour), when we wake to send our position we \_also* broadcast our owner. This lets new nodes on the network find out about us or correct duplicate node number assignments.
|
||||
- While in LS/NB/DARK: If the user presses a button (EVENT_PRESS) we go to full ON mode for screen_on_secs (default 30 seconds). Multiple presses keeps resetting this timeout
|
||||
- While in LS/NB/DARK: If we receive new text messages (EVENT_RECEIVED_TEXT_MSG), we go to full ON mode for screen_on_secs (same as if user pressed a button)
|
||||
- While in LS: while we receive packets on the radio (EVENT_RECEIVED_PACKET) we will wake and handle them and stay awake in NB mode for min_wake_secs (default 10 seconds)
|
||||
- While in NB: If we do have packets the phone (EVENT_PACKETS_FOR_PHONE) would want we transition to DARK mode for wait_bluetooth secs.
|
||||
- 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)
|
||||
|
||||
### events that decrease cpu activity
|
||||
|
||||
* 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
|
||||
* While in DARK or NB: If nothing above is forcing us to stay in a higher mode (wait_bluetooth_secs, min_wake_secs) we will lower down to LS state
|
||||
* While in LS: If either phone_sds_timeout_secs (default 2 hr) or mesh_sds_timeout_secs (default 2 hr) are exceeded we will lower into SDS mode for sds_secs (default 1 yr) (or a button press). (Note: phone_sds_timeout_secs is currently disabled for now, because most users
|
||||
are using without a phone)
|
||||
* Any time we enter LS mode: We stay in that until an interrupt, button press or other state transition. Every ls_secs (default 1 hr) and let the arduino loop() run one iteration (FIXME, not sure if we need this at all), and then immediately reenter lightsleep mode on the CPU.
|
||||
- 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
|
||||
- While in DARK or NB: If nothing above is forcing us to stay in a higher mode (wait_bluetooth_secs, min_wake_secs) we will lower down to LS state
|
||||
- While in LS: If either phone_sds_timeout_secs (default 2 hr) or mesh_sds_timeout_secs (default 2 hr) are exceeded we will lower into SDS mode for sds_secs (default 1 yr) (or a button press). (Note: phone_sds_timeout_secs is currently disabled for now, because most users
|
||||
are using without a phone)
|
||||
- Any time we enter LS mode: We stay in that until an interrupt, button press or other state transition. Every ls_secs (default 1 hr) and let the arduino loop() run one iteration (FIXME, not sure if we need this at all), and then immediately reenter lightsleep mode on the CPU.
|
||||
|
||||
TODO: Eventually these scheduled intervals should be synchronized to the GPS clock, so that we can consider leaving the lora receiver off to save even more power.
|
||||
TODO: In NB mode we should put cpu into light sleep any time we really aren't that busy (without declaring LS state) - i.e. we should leave GPS on etc...
|
||||
|
||||
# Low power consumption tasks
|
||||
|
||||
General ideas to hit the power draws our spreadsheet predicts. Do the easy ones before beta, the last 15% can be done after 1.0.
|
||||
General ideas to hit the power draws our spreadsheet predicts. Do the easy ones before beta, the last 15% can be done after 1.0.
|
||||
|
||||
* don't even power on the gps until someone else wants our position, just stay in lora deep sleep until press or rxpacket (except for once an hour updates)
|
||||
* (possibly bad idea - better to have lora radio always listen - check spreadsheet) have every node wake at the same tick and do their position syncs then go back to deep sleep
|
||||
* lower BT announce interval to save battery
|
||||
* change to use RXcontinuous mode and config to drop packets with bad CRC (see section 6.4 of datasheet) - I think this is already the case
|
||||
* have mesh service run in a thread that stays blocked until a packet arrives from the RF95
|
||||
* platformio sdkconfig CONFIG_PM and turn on modem sleep mode
|
||||
* keep cpu 100% in deepsleep until irq from radio wakes it. Then stay awake for 30 secs to attempt delivery to phone.
|
||||
* use https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/ association sleep pattern to save power - but see https://github.com/espressif/esp-idf/issues/2070 and https://esp32.com/viewtopic.php?f=13&t=12182 it seems with BLE on the 'easy' draw people are getting is 80mA
|
||||
* stop using loop() instead use a job queue and let cpu sleep
|
||||
* measure power consumption and calculate battery life assuming no deep sleep
|
||||
* do lowest sleep level possible where BT still works during normal sleeping, make sure cpu stays in that mode unless lora rx packet happens, bt rx packet happens or button press happens
|
||||
* optionally do lora messaging only during special scheduled intervals (unless nodes are told to go to low latency mode), then deep sleep except during those intervals - before implementing calculate what battery life would be with this feature
|
||||
* see section 7.3 of https://cdn.sparkfun.com/assets/learn_tutorials/8/0/4/RFM95_96_97_98W.pdf and have hope radio wake only when a valid packet is received. Possibly even wake the ESP32 from deep sleep via GPIO.
|
||||
* never enter deep sleep while connected to USB power (but still go to other low power modes)
|
||||
* when main cpu is idle (in loop), turn cpu clock rate down and/or activate special sleep modes. We want almost everything shutdown until it gets an interrupt.
|
||||
- don't even power on the gps until someone else wants our position, just stay in lora deep sleep until press or rxpacket (except for once an hour updates)
|
||||
- (possibly bad idea - better to have lora radio always listen - check spreadsheet) have every node wake at the same tick and do their position syncs then go back to deep sleep
|
||||
- lower BT announce interval to save battery
|
||||
- change to use RXcontinuous mode and config to drop packets with bad CRC (see section 6.4 of datasheet) - I think this is already the case
|
||||
- have mesh service run in a thread that stays blocked until a packet arrives from the RF95
|
||||
- platformio sdkconfig CONFIG_PM and turn on modem sleep mode
|
||||
- keep cpu 100% in deepsleep until irq from radio wakes it. Then stay awake for 30 secs to attempt delivery to phone.
|
||||
- use https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/ association sleep pattern to save power - but see https://github.com/espressif/esp-idf/issues/2070 and https://esp32.com/viewtopic.php?f=13&t=12182 it seems with BLE on the 'easy' draw people are getting is 80mA
|
||||
- stop using loop() instead use a job queue and let cpu sleep
|
||||
- measure power consumption and calculate battery life assuming no deep sleep
|
||||
- do lowest sleep level possible where BT still works during normal sleeping, make sure cpu stays in that mode unless lora rx packet happens, bt rx packet happens or button press happens
|
||||
- optionally do lora messaging only during special scheduled intervals (unless nodes are told to go to low latency mode), then deep sleep except during those intervals - before implementing calculate what battery life would be with this feature
|
||||
- see section 7.3 of https://cdn.sparkfun.com/assets/learn_tutorials/8/0/4/RFM95_96_97_98W.pdf and have hope radio wake only when a valid packet is received. Possibly even wake the ESP32 from deep sleep via GPIO.
|
||||
- never enter deep sleep while connected to USB power (but still go to other low power modes)
|
||||
- when main cpu is idle (in loop), turn cpu clock rate down and/or activate special sleep modes. We want almost everything shutdown until it gets an interrupt.
|
||||
|
||||
@@ -1,70 +1,68 @@
|
||||
#include "BluetoothUtil.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include <esp_gatt_defs.h>
|
||||
#include <BLE2902.h>
|
||||
#include <Arduino.h>
|
||||
#include <Update.h>
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <BLE2902.h>
|
||||
#include <Update.h>
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
SimpleAllocator btPool;
|
||||
|
||||
/**
|
||||
* Create standard device info service
|
||||
**/
|
||||
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion = "")
|
||||
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));
|
||||
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);
|
||||
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);
|
||||
/*
|
||||
* Mandatory characteristic for device info service?
|
||||
|
||||
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);
|
||||
BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ);
|
||||
// m_manufacturerCharacteristic->setValue(name);
|
||||
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);
|
||||
|
||||
/* add these later?
|
||||
ESP_GATT_UUID_SYSTEM_ID
|
||||
*/
|
||||
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
|
||||
// BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
|
||||
|
||||
// caller must call service->start();
|
||||
return deviceInfoService;
|
||||
/* add these later?
|
||||
ESP_GATT_UUID_SYSTEM_ID
|
||||
*/
|
||||
|
||||
// caller must call service->start();
|
||||
return deviceInfoService;
|
||||
}
|
||||
|
||||
bool _BLEClientConnected = false;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks
|
||||
{
|
||||
void onConnect(BLEServer *pServer)
|
||||
{
|
||||
_BLEClientConnected = true;
|
||||
};
|
||||
void onConnect(BLEServer *pServer) { _BLEClientConnected = true; };
|
||||
|
||||
void onDisconnect(BLEServer *pServer)
|
||||
{
|
||||
_BLEClientConnected = false;
|
||||
}
|
||||
void onDisconnect(BLEServer *pServer) { _BLEClientConnected = false; }
|
||||
};
|
||||
|
||||
#define MAX_DESCRIPTORS 32
|
||||
@@ -78,34 +76,34 @@ static size_t numDescs;
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLECharacteristic *addBLECharacteristic(BLECharacteristic *c)
|
||||
{
|
||||
assert(numChars < MAX_CHARACTERISTICS);
|
||||
chars[numChars++] = c;
|
||||
return c;
|
||||
assert(numChars < MAX_CHARACTERISTICS);
|
||||
chars[numChars++] = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c)
|
||||
{
|
||||
assert(numDescs < MAX_DESCRIPTORS);
|
||||
descs[numDescs++] = c;
|
||||
assert(numDescs < MAX_DESCRIPTORS);
|
||||
descs[numDescs++] = c;
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Help routine to add a description to any BLECharacteristic and add it to the service
|
||||
// We default to require an encrypted BOND for all these these characterstics
|
||||
void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description)
|
||||
{
|
||||
c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
|
||||
BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1);
|
||||
assert(desc);
|
||||
desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
desc->setValue(description);
|
||||
c->addDescriptor(desc);
|
||||
service->addCharacteristic(c);
|
||||
addBLECharacteristic(c);
|
||||
addBLEDescriptor(desc);
|
||||
BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1);
|
||||
assert(desc);
|
||||
desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
desc->setValue(description);
|
||||
c->addDescriptor(desc);
|
||||
service->addCharacteristic(c);
|
||||
addBLECharacteristic(c);
|
||||
addBLEDescriptor(desc);
|
||||
}
|
||||
|
||||
static BLECharacteristic *batteryLevelC;
|
||||
@@ -115,19 +113,20 @@ static BLECharacteristic *batteryLevelC;
|
||||
*/
|
||||
BLEService *createBatteryService(BLEServer *server)
|
||||
{
|
||||
// Create the BLE Service
|
||||
BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F));
|
||||
// Create the BLE Service
|
||||
BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F));
|
||||
|
||||
batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL),
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
|
||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
|
||||
// I don't think we need to advertise this
|
||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||
pBattery->start();
|
||||
// I don't think we need to advertise this
|
||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||
pBattery->start();
|
||||
|
||||
return pBattery;
|
||||
return pBattery;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,87 +135,82 @@ BLEService *createBatteryService(BLEServer *server)
|
||||
*/
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
// Pretend to update battery levels - fixme do elsewhere
|
||||
if (batteryLevelC)
|
||||
{
|
||||
batteryLevelC->setValue(&level, 1);
|
||||
batteryLevelC->notify();
|
||||
}
|
||||
// Pretend to update battery levels - fixme do elsewhere
|
||||
if (batteryLevelC) {
|
||||
batteryLevelC->setValue(&level, 1);
|
||||
batteryLevelC->notify();
|
||||
}
|
||||
}
|
||||
|
||||
void dumpCharacteristic(BLECharacteristic *c)
|
||||
{
|
||||
std::string value = c->getValue();
|
||||
std::string value = c->getValue();
|
||||
|
||||
if (value.length() > 0)
|
||||
{
|
||||
DEBUG_MSG("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
DEBUG_MSG("%c", value[i]);
|
||||
if (value.length() > 0) {
|
||||
DEBUG_MSG("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
DEBUG_MSG("%c", value[i]);
|
||||
|
||||
DEBUG_MSG("\n");
|
||||
}
|
||||
DEBUG_MSG("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** converting endianness pull out a 32 bit value */
|
||||
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue)
|
||||
{
|
||||
std::string value = c->getValue();
|
||||
uint32_t r = defaultValue;
|
||||
std::string value = c->getValue();
|
||||
uint32_t r = defaultValue;
|
||||
|
||||
if (value.length() == 4)
|
||||
r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL);
|
||||
if (value.length() == 4)
|
||||
r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL);
|
||||
|
||||
return r;
|
||||
return r;
|
||||
}
|
||||
|
||||
class MySecurity : public BLESecurityCallbacks
|
||||
{
|
||||
protected:
|
||||
bool onConfirmPIN(uint32_t pin)
|
||||
{
|
||||
Serial.printf("onConfirmPIN %u\n", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
Serial.println("onPassKeyRequest");
|
||||
return 123511; // not used
|
||||
}
|
||||
|
||||
void onPassKeyNotify(uint32_t pass_key)
|
||||
{
|
||||
Serial.printf("onPassKeyNotify %u\n", pass_key);
|
||||
startCb(pass_key);
|
||||
}
|
||||
|
||||
bool onSecurityRequest()
|
||||
{
|
||||
Serial.println("onSecurityRequest");
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl)
|
||||
{
|
||||
if (cmpl.success)
|
||||
protected:
|
||||
bool onConfirmPIN(uint32_t pin)
|
||||
{
|
||||
uint16_t length;
|
||||
esp_ble_gap_get_whitelist_size(&length);
|
||||
Serial.printf(" onAuthenticationComplete -> success size: %d\n", length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("onAuthenticationComplete -> fail %d\n", cmpl.fail_reason);
|
||||
Serial.printf("onConfirmPIN %u\n", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove our custom PIN request screen.
|
||||
stopCb();
|
||||
}
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
Serial.println("onPassKeyRequest");
|
||||
return 123511; // not used
|
||||
}
|
||||
|
||||
public:
|
||||
StartBluetoothPinScreenCallback startCb;
|
||||
StopBluetoothPinScreenCallback stopCb;
|
||||
void onPassKeyNotify(uint32_t pass_key)
|
||||
{
|
||||
Serial.printf("onPassKeyNotify %u\n", pass_key);
|
||||
startCb(pass_key);
|
||||
}
|
||||
|
||||
bool onSecurityRequest()
|
||||
{
|
||||
Serial.println("onSecurityRequest");
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl)
|
||||
{
|
||||
if (cmpl.success) {
|
||||
uint16_t length;
|
||||
esp_ble_gap_get_whitelist_size(&length);
|
||||
Serial.printf(" authenticated and connected to phone\n");
|
||||
} else {
|
||||
Serial.printf("phone authenticate failed %d\n", cmpl.fail_reason);
|
||||
}
|
||||
|
||||
// Remove our custom PIN request screen.
|
||||
stopCb();
|
||||
}
|
||||
|
||||
public:
|
||||
StartBluetoothPinScreenCallback startCb;
|
||||
StopBluetoothPinScreenCallback stopCb;
|
||||
};
|
||||
|
||||
BLEServer *pServer;
|
||||
@@ -225,88 +219,88 @@ BLEService *pDevInfo, *pUpdate;
|
||||
|
||||
void deinitBLE()
|
||||
{
|
||||
assert(pServer);
|
||||
assert(pServer);
|
||||
|
||||
pServer->getAdvertising()->stop();
|
||||
pServer->getAdvertising()->stop();
|
||||
|
||||
destroyUpdateService();
|
||||
destroyUpdateService();
|
||||
|
||||
pUpdate->stop();
|
||||
pDevInfo->stop();
|
||||
pUpdate->stop(); // we delete them below
|
||||
pUpdate->stop();
|
||||
pDevInfo->stop();
|
||||
pUpdate->stop(); // we delete them below
|
||||
|
||||
// First shutdown bluetooth
|
||||
BLEDevice::deinit(false);
|
||||
// First shutdown bluetooth
|
||||
BLEDevice::deinit(false);
|
||||
|
||||
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
|
||||
// delete pServer->getAdvertising();
|
||||
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
|
||||
// delete pServer->getAdvertising();
|
||||
|
||||
delete pUpdate;
|
||||
delete pDevInfo;
|
||||
delete pServer;
|
||||
delete pUpdate;
|
||||
delete pDevInfo;
|
||||
delete pServer;
|
||||
|
||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||
|
||||
for (int i = 0; i < numChars; i++)
|
||||
delete chars[i];
|
||||
numChars = 0;
|
||||
for (int i = 0; i < numChars; i++)
|
||||
delete chars[i];
|
||||
numChars = 0;
|
||||
|
||||
for (int i = 0; i < numDescs; i++)
|
||||
delete descs[i];
|
||||
numDescs = 0;
|
||||
for (int i = 0; i < numDescs; i++)
|
||||
delete descs[i];
|
||||
numDescs = 0;
|
||||
|
||||
btPool.reset();
|
||||
btPool.reset();
|
||||
}
|
||||
|
||||
BLEServer *initBLE(
|
||||
StartBluetoothPinScreenCallback startBtPinScreen,
|
||||
StopBluetoothPinScreenCallback stopBtPinScreen,
|
||||
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||
BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
|
||||
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||
{
|
||||
BLEDevice::init(deviceName);
|
||||
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
|
||||
BLEDevice::init(deviceName);
|
||||
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
|
||||
|
||||
/*
|
||||
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
|
||||
*/
|
||||
static MySecurity mySecurity;
|
||||
mySecurity.startCb = startBtPinScreen;
|
||||
mySecurity.stopCb = stopBtPinScreen;
|
||||
BLEDevice::setSecurityCallbacks(&mySecurity);
|
||||
/*
|
||||
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
|
||||
*/
|
||||
static MySecurity mySecurity;
|
||||
mySecurity.startCb = startBtPinScreen;
|
||||
mySecurity.stopCb = stopBtPinScreen;
|
||||
BLEDevice::setSecurityCallbacks(&mySecurity);
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
static MyServerCallbacks myCallbacks;
|
||||
pServer->setCallbacks(&myCallbacks);
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
static MyServerCallbacks myCallbacks;
|
||||
pServer->setCallbacks(&myCallbacks);
|
||||
|
||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||
|
||||
// We now let users create the battery service only if they really want (not all devices have a battery)
|
||||
// BLEService *pBattery = createBatteryService(pServer);
|
||||
// We now let users create the battery service only if they really want (not all devices have a battery)
|
||||
// BLEService *pBattery = createBatteryService(pServer);
|
||||
|
||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion, hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
||||
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||
|
||||
// It seems only one service can be advertised - so for now don't advertise our updater
|
||||
// pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID());
|
||||
// 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();
|
||||
// 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 phone and configured) but only let whitelist phones connect
|
||||
// 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
|
||||
// phone and configured) but only let whitelist phones connect
|
||||
|
||||
static BLESecurity security; // static to avoid allocs
|
||||
BLESecurity *pSecurity = &security;
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
static BLESecurity security; // static to avoid allocs
|
||||
BLESecurity *pSecurity = &security;
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
|
||||
return pServer;
|
||||
return pServer;
|
||||
}
|
||||
|
||||
// Called from loop
|
||||
void loopBLE()
|
||||
{
|
||||
bluetoothRebootCheck();
|
||||
bluetoothRebootCheck();
|
||||
}
|
||||
|
||||
34
lib/segger_rtt/License.txt
Normal file
34
lib/segger_rtt/License.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
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
|
||||
20
lib/segger_rtt/README.txt
Normal file
20
lib/segger_rtt/README.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
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.
|
||||
121
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_GCC.c
Normal file
121
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_GCC.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*********************************************************************
|
||||
* 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 *************************************************/
|
||||
115
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_IAR.c
Normal file
115
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_IAR.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*********************************************************************
|
||||
* 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 *************************************************/
|
||||
386
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_KEIL.c
Normal file
386
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_KEIL.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
247
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_SES.c
Normal file
247
lib/segger_rtt/Syscalls/SEGGER_RTT_Syscalls_SES.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*********************************************************************
|
||||
* 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 *************************************************/
|
||||
43
lib/segger_rtt/examples/Main_RTT_InputEchoApp.c
Normal file
43
lib/segger_rtt/examples/Main_RTT_InputEchoApp.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
70
lib/segger_rtt/examples/Main_RTT_MenuApp.c
Normal file
70
lib/segger_rtt/examples/Main_RTT_MenuApp.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
118
lib/segger_rtt/examples/Main_RTT_PrintfTest.c
Normal file
118
lib/segger_rtt/examples/Main_RTT_PrintfTest.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
69
lib/segger_rtt/examples/Main_RTT_SpeedTestApp.c
Normal file
69
lib/segger_rtt/examples/Main_RTT_SpeedTestApp.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*********************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
321
lib/segger_rtt/include/SEGGER_RTT.h
Normal file
321
lib/segger_rtt/include/SEGGER_RTT.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
384
lib/segger_rtt/include/SEGGER_RTT_Conf.h
Normal file
384
lib/segger_rtt/include/SEGGER_RTT_Conf.h
Normal file
@@ -0,0 +1,384 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
2005
lib/segger_rtt/src/SEGGER_RTT.c
Normal file
2005
lib/segger_rtt/src/SEGGER_RTT.c
Normal file
File diff suppressed because it is too large
Load Diff
235
lib/segger_rtt/src/SEGGER_RTT_ASM_ARMv7M.S
Normal file
235
lib/segger_rtt/src/SEGGER_RTT_ASM_ARMv7M.S
Normal file
@@ -0,0 +1,235 @@
|
||||
/*********************************************************************
|
||||
* (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 ****************************/
|
||||
500
lib/segger_rtt/src/SEGGER_RTT_printf.c
Normal file
500
lib/segger_rtt/src/SEGGER_RTT_printf.c
Normal file
@@ -0,0 +1,500 @@
|
||||
/*********************************************************************
|
||||
* 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 ****************************/
|
||||
107
platformio.ini
107
platformio.ini
@@ -9,11 +9,20 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam ; Note: the github actions CI test build can't yet build NRF52 targets
|
||||
|
||||
[env:esp32]
|
||||
[common]
|
||||
; common is not currently used
|
||||
|
||||
; REQUIRED environment variables - if not set the specified default will be sued
|
||||
; The following environment variables must be set in the shell if you'd like to override them.
|
||||
; They are used in this ini file as systenv.VARNAME, so in your shell do export "VARNAME=fish"
|
||||
; HW_VERSION (default US)
|
||||
; APP_VERSION (default emptystring)
|
||||
; HW_VERSION (default emptystring)
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
board = ttgo-t-beam
|
||||
; board = heltec_wifi_lora_32_V2
|
||||
framework = arduino
|
||||
|
||||
; customize the partition table
|
||||
@@ -22,24 +31,27 @@ 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 = -Wall -Wextra -Wno-missing-field-initializers -Isrc -Os -Wl,-Map,.pio/build/esp32/output.map -DAXP_DEBUG_PORT=Serial
|
||||
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/rf95 -Isrc/mesh -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DHW_VERSION_${sysenv.COUNTRY}
|
||||
-DAPP_VERSION=${sysenv.APP_VERSION}
|
||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||
|
||||
; not needed included in ttgo-t-beam board file
|
||||
; also to use PSRAM https://docs.platformio.org/en/latest/platforms/espressif32.html#external-ram-psram
|
||||
; -DBOARD_HAS_PSRAM
|
||||
; -mfix-esp32-psram-cache-issue
|
||||
|
||||
|
||||
; -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
|
||||
upload_speed = 921600
|
||||
upload_port = /dev/ttyUSB0
|
||||
; leave this commented out to avoid breaking Windows
|
||||
;upload_port = /dev/ttyUSB0
|
||||
;monitor_port = /dev/ttyUSB0
|
||||
|
||||
; the default is esptool
|
||||
; upload_protocol = esp-prog
|
||||
|
||||
monitor_port = /dev/ttyUSB0
|
||||
monitor_speed = 115200
|
||||
monitor_speed = 921600
|
||||
|
||||
# debug_tool = esp-prog
|
||||
# debug_port = /dev/ttyACM0
|
||||
@@ -53,24 +65,83 @@ debug_tool = jlink
|
||||
;debug_init_cmds =
|
||||
; monitor adapter_khz 10000
|
||||
|
||||
debug_init_break = tbreak setup
|
||||
|
||||
; Note: some libraries are specified by #ID where there are conflicting library
|
||||
; names.
|
||||
lib_deps =
|
||||
https://github.com/meshtastic/RadioHead.git
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||
AXP202X_Library
|
||||
SPI
|
||||
; 1260 ; OneButton - not used yet
|
||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
https://github.com/meshtastic/arduino-fsm.git
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
||||
https://github.com/meshtastic/SX126x-Arduino.git
|
||||
Ticker ; Needed for SX126x-Arduino on ESP32
|
||||
|
||||
;[env:tbeam]
|
||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||
[esp32_base]
|
||||
src_filter =
|
||||
${env.src_filter} -<nrf52/>
|
||||
upload_speed = 921600
|
||||
debug_init_break = tbreak setup
|
||||
build_flags =
|
||||
${env.build_flags} -Wall -Wextra
|
||||
lib_ignore = segger_rtt
|
||||
|
||||
; The 1.0 release of the TBEAM board
|
||||
[env:tbeam]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
AXP202X_Library
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TBEAM_V10
|
||||
|
||||
; The original TBEAM board without the AXP power chip and a few other changes
|
||||
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
||||
;[env:tbeam0.7]
|
||||
;extends = esp32_base
|
||||
;board = ttgo-t-beam
|
||||
;build_flags =
|
||||
; ${esp32_base.build_flags} -D TBEAM_V07
|
||||
|
||||
;[env:heltec]
|
||||
[env:heltec]
|
||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||
;board = heltec_wifi_lora_32_V2
|
||||
extends = esp32_base
|
||||
board = heltec_wifi_lora_32_V2
|
||||
|
||||
[env:ttgo-lora32-v1]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TTGO_LORA_V1
|
||||
|
||||
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
|
||||
[env:ttgo-lora32-v2]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TTGO_LORA_V2
|
||||
|
||||
|
||||
; The NRF52840-dk development board
|
||||
[env:nrf52dk]
|
||||
platform = nordicnrf52
|
||||
board = ppr
|
||||
framework = arduino
|
||||
debug_tool = jlink
|
||||
build_flags =
|
||||
${env.build_flags} -Wno-unused-variable -Isrc/nrf52
|
||||
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
|
||||
|
||||
; Set initial breakpoint (defaults to main)
|
||||
debug_init_break =
|
||||
;debug_init_break = tbreak loop
|
||||
;debug_init_break = tbreak Reset_Handler
|
||||
2
proto
2
proto
Submodule proto updated: 1b2449b50d...8427b23016
38
src/GPS.cpp
38
src/GPS.cpp
@@ -2,12 +2,17 @@
|
||||
#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
|
||||
|
||||
RTC_DATA_ATTR bool timeSetFromGPS; // We only reset our time once per _boot_ after that point just run from the internal clock
|
||||
// (even across sleeps)
|
||||
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
|
||||
|
||||
GPS gps;
|
||||
|
||||
@@ -16,17 +21,21 @@ 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 hasValidLocation; // default to false, until we complete our first read
|
||||
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);
|
||||
|
||||
@@ -39,7 +48,7 @@ void GPS::setup()
|
||||
isConnected = ublox.begin(_serial_gps);
|
||||
|
||||
if (isConnected) {
|
||||
DEBUG_MSG("Connected to GPS successfully, TXpin=%d\n", GPS_TX_PIN);
|
||||
DEBUG_MSG("Connected to GPS successfully\n");
|
||||
|
||||
bool factoryReset = false;
|
||||
bool ok;
|
||||
@@ -77,7 +86,6 @@ void GPS::setup()
|
||||
// checkUblox cyclically)
|
||||
ublox.assumeAutoPVT(true, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPS::readFromRTC()
|
||||
@@ -99,19 +107,17 @@ 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>
|
||||
|
||||
// for the time being we need to rapidly read from the serial port to prevent overruns
|
||||
void GPS::loop()
|
||||
{
|
||||
PeriodicTask::loop();
|
||||
}
|
||||
|
||||
uint32_t GPS::getTime()
|
||||
{
|
||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||
@@ -137,7 +143,6 @@ void GPS::prepareSleep()
|
||||
|
||||
void GPS::doTask()
|
||||
{
|
||||
#ifdef GPS_RX_PIN
|
||||
uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix
|
||||
|
||||
if (isConnected) {
|
||||
@@ -177,8 +182,10 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
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);
|
||||
|
||||
perhapsSetRTC(&tv);
|
||||
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
|
||||
@@ -192,12 +199,11 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||
if (hasValidLocation) {
|
||||
wantNewLocation = false;
|
||||
notifyObservers();
|
||||
notifyObservers(NULL);
|
||||
// ublox.powerOff();
|
||||
}
|
||||
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
||||
wantNewLocation = true;
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* When new data is available it will notify observers.
|
||||
*/
|
||||
class GPS : public PeriodicTask, public Observable
|
||||
class GPS : public PeriodicTask, public Observable<void *>
|
||||
{
|
||||
SFE_UBLOX_GPS ublox;
|
||||
|
||||
@@ -29,7 +29,6 @@ class GPS : public PeriodicTask, public Observable
|
||||
|
||||
void setup();
|
||||
|
||||
virtual void loop();
|
||||
|
||||
virtual void doTask();
|
||||
|
||||
@@ -45,8 +44,13 @@ class GPS : public PeriodicTask, public Observable
|
||||
/// 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;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// low level types
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef uint8_t NodeNum;
|
||||
|
||||
#define NODENUM_BROADCAST 255
|
||||
#define ERRNO_OK 0
|
||||
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
|
||||
|
||||
typedef int ErrorCode;
|
||||
@@ -1,13 +1,2 @@
|
||||
#include "Observer.h"
|
||||
|
||||
Observer::~Observer()
|
||||
{
|
||||
if (observed)
|
||||
observed->removeObserver(this);
|
||||
observed = NULL;
|
||||
}
|
||||
|
||||
void Observer::observe(Observable *o)
|
||||
{
|
||||
o->addObserver(this);
|
||||
}
|
||||
@@ -1,41 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
|
||||
class Observable;
|
||||
template <class T> class Observable;
|
||||
|
||||
class Observer
|
||||
/**
|
||||
* An observer which can be mixed in as a baseclass. Implement onNotify as a method in your class.
|
||||
*/
|
||||
template <class T> class Observer
|
||||
{
|
||||
Observable *observed;
|
||||
Observable<T> *observed = NULL;
|
||||
|
||||
public:
|
||||
Observer() : observed(NULL) {}
|
||||
|
||||
virtual ~Observer();
|
||||
|
||||
void observe(Observable *o);
|
||||
void observe(Observable<T> *o);
|
||||
|
||||
private:
|
||||
friend class Observable;
|
||||
friend class Observable<T>;
|
||||
|
||||
virtual void onNotify(Observable *o) = 0;
|
||||
protected:
|
||||
/**
|
||||
* returns 0 if other observers should continue to be called
|
||||
* returns !0 if the observe calls should be aborted and this result code returned for notifyObservers
|
||||
**/
|
||||
virtual int onNotify(T arg) = 0;
|
||||
};
|
||||
|
||||
class Observable
|
||||
/**
|
||||
* An observer that calls an arbitrary method
|
||||
*/
|
||||
template <class Callback, class T> class CallbackObserver : public Observer<T>
|
||||
{
|
||||
std::list<Observer *> observers;
|
||||
typedef int (Callback::*ObserverCallback)(T arg);
|
||||
|
||||
Callback *objPtr;
|
||||
ObserverCallback method;
|
||||
|
||||
public:
|
||||
void notifyObservers()
|
||||
CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {}
|
||||
|
||||
protected:
|
||||
virtual int onNotify(T arg) { return (objPtr->*method)(arg); }
|
||||
};
|
||||
|
||||
/**
|
||||
* An observable class that will notify observers anytime notifyObservers is called. Argument type T can be any type, but for
|
||||
* performance reasons a pointer or word sized object is recommended.
|
||||
*/
|
||||
template <class T> class Observable
|
||||
{
|
||||
std::list<Observer<T> *> observers;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Tell all observers about a change, observers can process arg as they wish
|
||||
*
|
||||
* returns !0 if an observer chose to abort processing by returning this code
|
||||
*/
|
||||
int notifyObservers(T arg)
|
||||
{
|
||||
for (std::list<Observer *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) {
|
||||
(*iterator)->onNotify(this);
|
||||
for (typename std::list<Observer<T> *>::const_iterator iterator = observers.begin(); iterator != observers.end();
|
||||
++iterator) {
|
||||
int result = (*iterator)->onNotify(arg);
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void addObserver(Observer *o) { observers.push_back(o); }
|
||||
private:
|
||||
friend class Observer<T>;
|
||||
|
||||
void removeObserver(Observer *o) { observers.remove(o); }
|
||||
// Not called directly, instead call observer.observe
|
||||
void addObserver(Observer<T> *o) { observers.push_back(o); }
|
||||
|
||||
void removeObserver(Observer<T> *o) { observers.remove(o); }
|
||||
};
|
||||
|
||||
template <class T> Observer<T>::~Observer()
|
||||
{
|
||||
if (observed)
|
||||
observed->removeObserver(this);
|
||||
observed = NULL;
|
||||
}
|
||||
|
||||
template <class T> void Observer<T>::observe(Observable<T> *o)
|
||||
{
|
||||
// We can only watch one thing at a time
|
||||
assert(!observed);
|
||||
|
||||
observed = o;
|
||||
o->addObserver(this);
|
||||
}
|
||||
@@ -1,21 +1,39 @@
|
||||
#include "PeriodicTask.h"
|
||||
#include "Periodic.h"
|
||||
PeriodicScheduler periodicScheduler;
|
||||
|
||||
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
||||
|
||||
/// call this from loop
|
||||
void PeriodicTask::loop()
|
||||
void PeriodicTask::setup()
|
||||
{
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
uint32_t now = millis();
|
||||
if (!period || (now - lastMsec) < period) {
|
||||
return;
|
||||
periodicScheduler.schedule(this);
|
||||
}
|
||||
|
||||
/// call this from loop
|
||||
void PeriodicScheduler::loop()
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
|
||||
uint32_t now = millis();
|
||||
for (auto t : tasks) {
|
||||
if (t->period && (now - t->lastMsec) >= t->period) {
|
||||
|
||||
t->doTask();
|
||||
t->lastMsec = now;
|
||||
}
|
||||
lastMsec = now;
|
||||
}
|
||||
// Release the lock in case the task wants to change the period.
|
||||
doTask();
|
||||
}
|
||||
|
||||
void PeriodicScheduler::schedule(PeriodicTask *t)
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
tasks.insert(t);
|
||||
}
|
||||
|
||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
tasks.erase(t);
|
||||
}
|
||||
|
||||
void Periodic::doTask()
|
||||
|
||||
@@ -1,8 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "lock.h"
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
|
||||
class PeriodicTask;
|
||||
|
||||
/**
|
||||
* Runs all PeriodicTasks in the system.
|
||||
*
|
||||
* Currently called from main loop() but eventually should be its own thread blocked on a freertos timer.
|
||||
*/
|
||||
class PeriodicScheduler
|
||||
{
|
||||
friend class PeriodicTask;
|
||||
|
||||
/**
|
||||
* This really should be some form of heap, and when the period gets changed on a task it should get
|
||||
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
||||
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
||||
*/
|
||||
std::unordered_set<PeriodicTask *> tasks;
|
||||
|
||||
// Protects the above variables.
|
||||
meshtastic::Lock lock;
|
||||
|
||||
public:
|
||||
/// Run any next tasks which are due for execution
|
||||
void loop();
|
||||
|
||||
private:
|
||||
void schedule(PeriodicTask *t);
|
||||
void unschedule(PeriodicTask *t);
|
||||
};
|
||||
|
||||
extern PeriodicScheduler periodicScheduler;
|
||||
|
||||
/**
|
||||
* A base class for tasks that want their doTask() method invoked periodically
|
||||
@@ -13,26 +45,33 @@
|
||||
*/
|
||||
class PeriodicTask
|
||||
{
|
||||
friend class PeriodicScheduler;
|
||||
|
||||
uint32_t lastMsec = 0;
|
||||
uint32_t period = 1; // call soon after creation
|
||||
|
||||
// Protects the above variables.
|
||||
meshtastic::Lock lock;
|
||||
|
||||
public:
|
||||
virtual ~PeriodicTask() {}
|
||||
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
||||
|
||||
/**
|
||||
* Constructor (will schedule with the global PeriodicScheduler)
|
||||
*/
|
||||
PeriodicTask(uint32_t initialPeriod = 1);
|
||||
|
||||
/// call this from loop
|
||||
virtual void loop();
|
||||
/** MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
||||
*/
|
||||
void setup();
|
||||
|
||||
/// Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
||||
void setPeriod(uint32_t p)
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
period = p;
|
||||
}
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
/**
|
||||
* Syntatic sugar for suspending tasks
|
||||
*/
|
||||
void disable() { setPeriod(0); }
|
||||
|
||||
protected:
|
||||
virtual void doTask() = 0;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
static void sdsEnter()
|
||||
{
|
||||
@@ -30,22 +31,6 @@ static void lsEnter()
|
||||
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", radioConfig.preferences.ls_secs);
|
||||
screen.setOn(false);
|
||||
|
||||
uint32_t now = millis();
|
||||
while (!service.radio.rf95.canSleep()) {
|
||||
delay(10); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
||||
|
||||
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
||||
recordCriticalError(ErrSleepEnterWait);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gps.prepareSleep(); // abandon in-process parsing
|
||||
|
||||
// if (!isUSBPowered) // FIXME - temp hack until we can put gps in sleep mode, if we have AC when we go to sleep then
|
||||
// leave GPS on
|
||||
// setGPSPower(false); // kill GPS power
|
||||
|
||||
DEBUG_MSG("lsEnter end\n");
|
||||
}
|
||||
|
||||
@@ -53,6 +38,7 @@ static void lsIdle()
|
||||
{
|
||||
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;
|
||||
@@ -82,7 +68,12 @@ static void lsIdle()
|
||||
} else {
|
||||
DEBUG_MSG("wakeCause %d\n", wakeCause);
|
||||
|
||||
if (!digitalRead(BUTTON_PIN)) // If we woke because of press, instead generate a PRESS event.
|
||||
#ifdef BUTTON_PIN
|
||||
bool pressed = !digitalRead(BUTTON_PIN);
|
||||
#else
|
||||
bool pressed = false;
|
||||
#endif
|
||||
if (pressed) // If we woke because of press, instead generate a PRESS event.
|
||||
{
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
} else {
|
||||
@@ -90,6 +81,7 @@ static void lsIdle()
|
||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lsExit()
|
||||
@@ -108,6 +100,7 @@ static void nbEnter()
|
||||
|
||||
static void darkEnter()
|
||||
{
|
||||
setBluetoothEnable(true);
|
||||
screen.setOn(false);
|
||||
}
|
||||
|
||||
@@ -180,10 +173,13 @@ void PowerFSM_setup()
|
||||
|
||||
powerFSM.add_timed_transition(&stateDARK, &stateNB, radioConfig.preferences.phone_timeout_secs * 1000, NULL, "Phone timeout");
|
||||
|
||||
#ifndef NRF52_SERIES
|
||||
// We never enter light-sleep state on NRF52 (because the CPU uses so little power normally)
|
||||
powerFSM.add_timed_transition(&stateNB, &stateLS, radioConfig.preferences.min_wake_secs * 1000, NULL, "Min wake timeout");
|
||||
|
||||
powerFSM.add_timed_transition(&stateDARK, &stateLS, radioConfig.preferences.wait_bluetooth_secs * 1000, NULL,
|
||||
"Bluetooth timeout");
|
||||
#endif
|
||||
|
||||
powerFSM.add_timed_transition(&stateLS, &stateSDS, radioConfig.preferences.mesh_sds_timeout_secs * 1000, NULL,
|
||||
"mesh timeout");
|
||||
|
||||
13
src/RedirectablePrint.cpp
Normal file
13
src/RedirectablePrint.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "RedirectablePrint.h"
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
*/
|
||||
NoopPrint noopPrint;
|
||||
|
||||
void RedirectablePrint::setDestination(Print *_dest)
|
||||
{
|
||||
assert(_dest);
|
||||
dest = _dest;
|
||||
}
|
||||
34
src/RedirectablePrint.h
Normal file
34
src/RedirectablePrint.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <Print.h>
|
||||
|
||||
/**
|
||||
* A Printable that can be switched to squirt its bytes to a different sink.
|
||||
* This class is mostly useful to allow debug printing to be redirected away from Serial
|
||||
* to some other transport if we switch Serial usage (on the fly) to some other purpose.
|
||||
*/
|
||||
class RedirectablePrint : public Print
|
||||
{
|
||||
Print *dest;
|
||||
|
||||
public:
|
||||
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
/**
|
||||
* Set a new destination
|
||||
*/
|
||||
void setDestination(Print *dest);
|
||||
|
||||
virtual size_t write(uint8_t c) { return dest->write(c); }
|
||||
};
|
||||
|
||||
class NoopPrint : public Print
|
||||
{
|
||||
public:
|
||||
virtual size_t write(uint8_t c) { return 1; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
*/
|
||||
extern NoopPrint noopPrint;
|
||||
36
src/SerialConsole.cpp
Normal file
36
src/SerialConsole.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "SerialConsole.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#define Port Serial
|
||||
|
||||
SerialConsole console;
|
||||
|
||||
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
|
||||
{
|
||||
canWrite = false; // We don't send packets to our port until it has talked to us first
|
||||
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
|
||||
}
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
void SerialConsole::init()
|
||||
{
|
||||
Port.begin(SERIAL_BAUD);
|
||||
StreamAPI::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
canWrite = true;
|
||||
|
||||
StreamAPI::handleToRadio(buf, len);
|
||||
}
|
||||
24
src/SerialConsole.h
Normal file
24
src/SerialConsole.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "RedirectablePrint.h"
|
||||
#include "StreamAPI.h"
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class SerialConsole : public StreamAPI, public RedirectablePrint
|
||||
{
|
||||
public:
|
||||
SerialConsole();
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
virtual void init();
|
||||
|
||||
/**
|
||||
* we override this to notice when we've received a protobuf over the serial stream. Then we shunt off
|
||||
* debug serial output.
|
||||
*/
|
||||
virtual void handleToRadio(const uint8_t *buf, size_t len);
|
||||
};
|
||||
|
||||
extern SerialConsole console;
|
||||
@@ -31,47 +31,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// If app version is not specified we assume we are not being invoked by the build script
|
||||
#ifndef APP_VERSION
|
||||
#define APP_VERSION 0.0.0 // this def normally comes from build-all.sh
|
||||
#define HW_VERSION 1.0 - US // normally comes from build-all.sh and contains the region code
|
||||
#error APP_VERSION, HW_VERSION, and HW_VERSION_countryname must be set by the build environment
|
||||
//#define APP_VERSION 0.0.0 // this def normally comes from build-all.sh
|
||||
//#define HW_VERSION 1.0 - US // normally comes from build-all.sh and contains the region code
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Configuration
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Select which board is being used. If the outside build environment has sent a choice, just use that
|
||||
#if !defined(T_BEAM_V10) && !defined(HELTEC_LORA32)
|
||||
#define T_BEAM_V10 // AKA Rev1 (second board released)
|
||||
// #define HELTEC_LORA32
|
||||
|
||||
#define HW_VERSION_US // We encode the hardware freq range in the hw version string, so sw update can eventually install the
|
||||
// correct build
|
||||
#endif
|
||||
|
||||
// If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled)
|
||||
// we don't support jtag on the ttgo - access to gpio 12 is a PITA
|
||||
#ifdef HELTEC_LORA32
|
||||
#ifdef ARDUINO_HELTEC_WIFI_LORA_32_V2
|
||||
//#define USE_JTAG
|
||||
#endif
|
||||
|
||||
#define DEBUG_PORT Serial // Serial debug port
|
||||
#define SERIAL_BAUD 115200 // Serial debug baud rate
|
||||
|
||||
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
|
||||
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DEBUG
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifdef DEBUG_PORT
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_MSG(...)
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OLED
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -82,6 +61,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// devices. Comment this out to not rotate screen 180 degrees.
|
||||
#define FLIP_SCREEN_VERTICALLY
|
||||
|
||||
// DEBUG LED
|
||||
|
||||
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPS
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -105,9 +88,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MOSI_GPIO 27
|
||||
#define NSS_GPIO 18
|
||||
|
||||
#if defined(T_BEAM_V10)
|
||||
#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"
|
||||
#define HW_VENDOR "tbeam"
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
@@ -119,40 +102,155 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 14
|
||||
#endif
|
||||
#define DIO0_GPIO 26
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 33 // Note: not really used on this board
|
||||
#define DIO2_GPIO 32 // Note: not really used on this board
|
||||
|
||||
// Leave undefined to disable our PMU IRQ handler
|
||||
#define PMU_IRQ 35
|
||||
|
||||
#elif defined(HELTEC_LORA32)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "HELTEC"
|
||||
#define AXP192_SLAVE_ADDRESS 0x34
|
||||
|
||||
#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag
|
||||
#define I2C_SDA 4
|
||||
#elif defined(TBEAM_V07)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tbeam0.7"
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define BUTTON_PIN 39
|
||||
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 23
|
||||
#endif
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 33 // Note: not really used on this board
|
||||
#define DIO2_GPIO 32 // Note: not really used on this board
|
||||
|
||||
// This board has different GPS pins than all other boards
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 12
|
||||
#define GPS_TX_PIN 15
|
||||
|
||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "heltec"
|
||||
|
||||
#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
#endif
|
||||
|
||||
#define RESET_OLED 16
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25
|
||||
#define BUTTON_PIN 0
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
|
||||
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 14
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#endif
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#elif defined(TTGO_LORA_V1)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "ttgo-lora32-v1"
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
// #define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 2 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
|
||||
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#elif defined(TTGO_LORA_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "ttgo-lora32-v2"
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN \
|
||||
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
|
||||
|
||||
// FIXME, use variant.h defs for all of this!!!
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
#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
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DEBUG
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#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 DEBUG_PORT
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_MSG(...)
|
||||
#endif
|
||||
#define DIO0_GPIO 26
|
||||
#define DIO1_GPIO 35
|
||||
#define DIO2_GPIO 34
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// AXP192 (Rev1-specific options)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// #define AXP192_SLAVE_ADDRESS 0x34 // Now defined in axp20x.h
|
||||
#define GPS_POWER_CTRL_CH 3
|
||||
#define LORA_POWER_CTRL_CH 2
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "freertosinc.h"
|
||||
#include "configuration.h"
|
||||
|
||||
namespace meshtastic
|
||||
@@ -12,9 +10,11 @@ namespace meshtastic
|
||||
|
||||
void printThreadInfo(const char *extra)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
|
||||
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", extra, taskHandle, xPortGetCoreID(),
|
||||
uxTaskGetStackHighWaterMark(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace meshtastic
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/// Error codes for critical error
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait };
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified };
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(CriticalErrorCode code, uint32_t address = 0);
|
||||
|
||||
@@ -6,19 +6,44 @@
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
#include "CallbackCharacteristic.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PhoneAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
#include "GPS.h"
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// proccess at once
|
||||
static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
|
||||
static CallbackCharacteristic *meshFromNumCharacteristic;
|
||||
|
||||
BLEService *meshService;
|
||||
|
||||
// If defined we will also support the old API
|
||||
#define SUPPORT_OLD_BLE_API
|
||||
|
||||
class BluetoothPhoneAPI : public PhoneAPI
|
||||
{
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum)
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
if (meshFromNumCharacteristic) { // this ptr might change from sleep to sleep, or even be null
|
||||
meshFromNumCharacteristic->setValue(fromRadioNum);
|
||||
meshFromNumCharacteristic->notify();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
class ProtobufCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
const pb_msgdesc_t *fields;
|
||||
@@ -58,6 +83,7 @@ class ProtobufCharacteristic : public CallbackCharacteristic
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef SUPPORT_OLD_BLE_API
|
||||
class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCallbacks
|
||||
{
|
||||
public:
|
||||
@@ -106,14 +132,15 @@ class RadioCharacteristic : public ProtobufCharacteristic
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
DEBUG_MSG("Reading radio config\n");
|
||||
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);
|
||||
service.reloadConfig();
|
||||
bluetoothPhoneAPI->handleSetRadio(radioConfig);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,92 +161,11 @@ class OwnerCharacteristic : public ProtobufCharacteristic
|
||||
|
||||
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)) {
|
||||
int changed = 0;
|
||||
|
||||
if (*o.long_name) {
|
||||
changed |= strcmp(owner.long_name, o.long_name);
|
||||
strcpy(owner.long_name, o.long_name);
|
||||
}
|
||||
if (*o.short_name) {
|
||||
changed |= strcmp(owner.short_name, o.short_name);
|
||||
strcpy(owner.short_name, o.short_name);
|
||||
}
|
||||
if (*o.id) {
|
||||
changed |= strcmp(owner.id, o.id);
|
||||
strcpy(owner.id, o.id);
|
||||
}
|
||||
|
||||
if (changed) // If nothing really changed, don't broadcast on the network or write to flash
|
||||
service.reloadOwner();
|
||||
bluetoothPhoneAPI->handleSetOwner(o);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ToRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
BLEKeepAliveCallbacks::onWrite(c);
|
||||
DEBUG_MSG("Got on write\n");
|
||||
|
||||
service.handleToRadio(c->getValue());
|
||||
}
|
||||
};
|
||||
|
||||
class FromRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
|
||||
{
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
BLEKeepAliveCallbacks::onRead(c);
|
||||
MeshPacket *mp = service.getForPhone();
|
||||
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
if (!mp) {
|
||||
DEBUG_MSG("toPhone queue is empty\n");
|
||||
c->setValue((uint8_t *)"", 0);
|
||||
} else {
|
||||
static FromRadio fRadio;
|
||||
|
||||
// Encapsulate as a FromRadio packet
|
||||
memset(&fRadio, 0, sizeof(fRadio));
|
||||
fRadio.which_variant = FromRadio_packet_tag;
|
||||
fRadio.variant.packet = *mp;
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fRadio);
|
||||
DEBUG_MSG("delivering toPhone packet to phone %d bytes\n", numbytes);
|
||||
c->setValue(trBytes, numbytes);
|
||||
|
||||
service.releaseToPool(mp); // we just copied the bytes, so don't need this buffer anymore
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FromNumCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromNumCharacteristic()
|
||||
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_NOTIFY)
|
||||
{
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
BLEKeepAliveCallbacks::onRead(c);
|
||||
DEBUG_MSG("FIXME implement fromnum read\n");
|
||||
}
|
||||
};
|
||||
|
||||
class MyNodeInfoCharacteristic : public ProtobufCharacteristic
|
||||
{
|
||||
public:
|
||||
@@ -241,29 +187,75 @@ class MyNodeInfoCharacteristic : public ProtobufCharacteristic
|
||||
}
|
||||
};
|
||||
|
||||
FromNumCharacteristic *meshFromNumCharacteristic;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Tell any bluetooth clients that the number of rx packets has changed
|
||||
*/
|
||||
void bluetoothNotifyFromNum(uint32_t newValue)
|
||||
class ToRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
if (meshFromNumCharacteristic) {
|
||||
// if bt not running ignore
|
||||
meshFromNumCharacteristic->setValue(newValue);
|
||||
meshFromNumCharacteristic->notify();
|
||||
}
|
||||
}
|
||||
public:
|
||||
ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
|
||||
|
||||
BLEService *meshService;
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
BLEKeepAliveCallbacks::onWrite(c);
|
||||
DEBUG_MSG("Got on write\n");
|
||||
|
||||
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
|
||||
}
|
||||
};
|
||||
|
||||
class FromRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
// or make empty if the queue is empty
|
||||
if (numBytes) {
|
||||
c->setValue(trBytes, numBytes);
|
||||
} else {
|
||||
c->setValue((uint8_t *)"", 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FromNumCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromNumCharacteristic()
|
||||
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_NOTIFY)
|
||||
{
|
||||
// observe(&service.fromNumChanged);
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
BLEKeepAliveCallbacks::onRead(c);
|
||||
DEBUG_MSG("FIXME implement fromnum read\n");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
See bluetooth-api.md for documentation.
|
||||
*/
|
||||
BLEService *createMeshBluetoothService(BLEServer *server)
|
||||
{
|
||||
// Only create our phone API object once
|
||||
if (!bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
bluetoothPhoneAPI->init();
|
||||
}
|
||||
|
||||
// Create the BLE Service, we need more than the default of 15 handles
|
||||
BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 25, 0);
|
||||
BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0);
|
||||
|
||||
assert(!meshFromNumCharacteristic);
|
||||
meshFromNumCharacteristic = new FromNumCharacteristic;
|
||||
@@ -271,10 +263,12 @@ 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
|
||||
|
||||
233
src/esp32/main-esp32.cpp
Normal file
233
src/esp32/main-esp32.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "BluetoothUtil.h"
|
||||
#include "MeshBluetoothService.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "power.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
bool bluetoothOn;
|
||||
|
||||
// This routine is called multiple times, once each time we come back from sleep
|
||||
void reinitBluetooth()
|
||||
{
|
||||
DEBUG_MSG("Starting bluetooth\n");
|
||||
|
||||
// FIXME - we are leaking like crazy
|
||||
// AllocatorScope scope(btPool);
|
||||
|
||||
// Note: these callbacks might be coming in from a different thread.
|
||||
BLEServer *serve = initBLE(
|
||||
[](uint32_t pin) {
|
||||
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
|
||||
createMeshBluetoothService(serve);
|
||||
|
||||
// Start advertising - this must be done _after_ creating all services
|
||||
serve->getAdvertising()->start();
|
||||
}
|
||||
|
||||
// Enable/disable bluetooth.
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bluetoothOn) {
|
||||
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
|
||||
|
||||
bluetoothOn = on;
|
||||
if (on) {
|
||||
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
reinitBluetooth();
|
||||
} else {
|
||||
// We have to totally teardown our bluetooth objects to prevent leaks
|
||||
stopMeshBluetoothService(); // Must do before shutting down bluetooth
|
||||
deinitBLE();
|
||||
destroyMeshBluetoothService(); // must do after deinit, because it frees our service
|
||||
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
// heap_trace_dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
||||
}
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
|
||||
// FIXME. nasty hack cleanup how we load axp192
|
||||
#undef AXP192_SLAVE_ADDRESS
|
||||
#include "axp20x.h"
|
||||
AXP20X_Class axp;
|
||||
bool pmu_irq = false;
|
||||
|
||||
/// Reads power status to powerStatus singleton.
|
||||
//
|
||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||
void readPowerStatus()
|
||||
{
|
||||
powerStatus.haveBattery = axp.isBatteryConnect();
|
||||
if (powerStatus.haveBattery) {
|
||||
powerStatus.batteryVoltageMv = axp.getBattVoltage();
|
||||
}
|
||||
powerStatus.usb = axp.isVBUSPlug();
|
||||
powerStatus.charging = axp.isChargeing();
|
||||
}
|
||||
#endif // TBEAM_V10
|
||||
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
/**
|
||||
* Init the power manager chip
|
||||
*
|
||||
* axp192 power
|
||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
||||
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
||||
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||
*/
|
||||
void axp192Init()
|
||||
{
|
||||
if (axp192_found) {
|
||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||
|
||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("----------------------------------------\n");
|
||||
|
||||
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
||||
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
||||
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
||||
axp.setDCDC1Voltage(3300); // for the OLED power
|
||||
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
|
||||
#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;
|
||||
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
||||
|
||||
//not used
|
||||
//val = 0x46;
|
||||
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
||||
#endif
|
||||
axp.debugCharging();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
pinMode(PMU_IRQ, INPUT);
|
||||
attachInterrupt(
|
||||
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
||||
|
||||
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
|
||||
axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ |
|
||||
AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
|
||||
1);
|
||||
|
||||
axp.clearIRQ();
|
||||
#endif
|
||||
readPowerStatus();
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 Begin FAIL\n");
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 not found\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp32Setup()
|
||||
{
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
axp192Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Turn off for now
|
||||
|
||||
uint32_t axpDebugRead()
|
||||
{
|
||||
axp.debugCharging();
|
||||
DEBUG_MSG("vbus current %f\n", axp.getVbusCurrent());
|
||||
DEBUG_MSG("charge current %f\n", axp.getBattChargeCurrent());
|
||||
DEBUG_MSG("bat voltage %f\n", axp.getBattVoltage());
|
||||
DEBUG_MSG("batt pct %d\n", axp.getBattPercentage());
|
||||
DEBUG_MSG("is battery connected %d\n", axp.isBatteryConnect());
|
||||
DEBUG_MSG("is USB connected %d\n", axp.isVBUSPlug());
|
||||
DEBUG_MSG("is charging %d\n", axp.isChargeing());
|
||||
|
||||
return 30 * 1000;
|
||||
}
|
||||
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
#endif
|
||||
|
||||
/// loop code specific to ESP32 targets
|
||||
void esp32Loop()
|
||||
{
|
||||
loopBLE();
|
||||
|
||||
// for debug printing
|
||||
// radio.radioIf.canSleep();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
if (pmu_irq) {
|
||||
pmu_irq = false;
|
||||
axp.readIRQ();
|
||||
|
||||
DEBUG_MSG("pmu irq!\n");
|
||||
|
||||
if (axp.isChargingIRQ()) {
|
||||
DEBUG_MSG("Battery start charging\n");
|
||||
}
|
||||
if (axp.isChargingDoneIRQ()) {
|
||||
DEBUG_MSG("Battery fully charged\n");
|
||||
}
|
||||
if (axp.isVbusRemoveIRQ()) {
|
||||
DEBUG_MSG("USB unplugged\n");
|
||||
}
|
||||
if (axp.isVbusPlugInIRQ()) {
|
||||
DEBUG_MSG("USB plugged In\n");
|
||||
}
|
||||
if (axp.isBattPlugInIRQ()) {
|
||||
DEBUG_MSG("Battery inserted\n");
|
||||
}
|
||||
if (axp.isBattRemoveIRQ()) {
|
||||
DEBUG_MSG("Battery removed\n");
|
||||
}
|
||||
if (axp.isPEKShortPressIRQ()) {
|
||||
DEBUG_MSG("PEK short button press\n");
|
||||
}
|
||||
|
||||
readPowerStatus();
|
||||
axp.clearIRQ();
|
||||
}
|
||||
#endif // T_BEAM_V10
|
||||
}
|
||||
16
src/freertosinc.h
Normal file
16
src/freertosinc.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
// The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with platformio gcc options
|
||||
// so this is my quick hack to make things work
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/queue.h>
|
||||
#else
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <semphr.h>
|
||||
#include <queue.h>
|
||||
#endif
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include "freertosinc.h"
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
275
src/main.cpp
275
src/main.cpp
@@ -21,45 +21,39 @@
|
||||
|
||||
*/
|
||||
|
||||
#include "BluetoothUtil.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshBluetoothService.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Periodic.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "esp32/pm.h"
|
||||
#include "esp_pm.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "error.h"
|
||||
#include "power.h"
|
||||
// #include "rom/rtc.h"
|
||||
#include "FloodingRouter.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "sleep.h"
|
||||
#include <Wire.h>
|
||||
#include <driver/rtc_io.h>
|
||||
// #include <driver/rtc_io.h>
|
||||
|
||||
#ifdef T_BEAM_V10
|
||||
#include "axp20x.h"
|
||||
AXP20X_Class axp;
|
||||
bool pmu_irq = false;
|
||||
#ifndef NO_ESP32
|
||||
#include "BluetoothUtil.h"
|
||||
#endif
|
||||
|
||||
// Global Screen singleton
|
||||
#ifdef I2C_SDA
|
||||
meshtastic::Screen screen(SSD1306_ADDRESS, I2C_SDA, I2C_SCL);
|
||||
#else
|
||||
// Fake values for pins to keep build happy, we won't ever initialize it.
|
||||
meshtastic::Screen screen(SSD1306_ADDRESS, 0, 0);
|
||||
#endif
|
||||
// We always create a screen object, but we only init it if we find the hardware
|
||||
meshtastic::Screen screen(SSD1306_ADDRESS);
|
||||
|
||||
// these flags are all in bss so they default false
|
||||
bool isCharging;
|
||||
bool isUSBPowered;
|
||||
// Global power status singleton
|
||||
meshtastic::PowerStatus powerStatus;
|
||||
|
||||
bool ssd1306_found;
|
||||
bool axp192_found;
|
||||
|
||||
bool bluetoothOn;
|
||||
FloodingRouter realRouter;
|
||||
Router &router = realRouter; // Users of router don't care what sort of subclass implements that API
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Application
|
||||
@@ -81,7 +75,7 @@ void scanI2Cdevice(void)
|
||||
ssd1306_found = true;
|
||||
DEBUG_MSG("ssd1306 display found\n");
|
||||
}
|
||||
#ifdef T_BEAM_V10
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
if (addr == AXP192_SLAVE_ADDRESS) {
|
||||
axp192_found = true;
|
||||
DEBUG_MSG("axp192 PMU found\n");
|
||||
@@ -97,92 +91,11 @@ void scanI2Cdevice(void)
|
||||
DEBUG_MSG("done\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the power manager chip
|
||||
*
|
||||
* axp192 power
|
||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
||||
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
||||
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||
*/
|
||||
void axp192Init()
|
||||
{
|
||||
#ifdef T_BEAM_V10
|
||||
if (axp192_found) {
|
||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||
|
||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("----------------------------------------\n");
|
||||
|
||||
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
||||
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
||||
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
||||
axp.setDCDC1Voltage(3300); // for the OLED power
|
||||
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
|
||||
#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;
|
||||
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
||||
|
||||
//not used
|
||||
//val = 0x46;
|
||||
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
||||
#endif
|
||||
axp.debugCharging();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
pinMode(PMU_IRQ, INPUT_PULLUP);
|
||||
attachInterrupt(
|
||||
PMU_IRQ, [] { pmu_irq = true; }, RISING);
|
||||
|
||||
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
|
||||
axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ,
|
||||
1);
|
||||
axp.clearIRQ();
|
||||
#endif
|
||||
|
||||
isCharging = axp.isChargeing() ? 1 : 0;
|
||||
isUSBPowered = axp.isVBUSPlug() ? 1 : 0;
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 Begin FAIL\n");
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 not found\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *getDeviceName()
|
||||
{
|
||||
uint8_t dmac[6];
|
||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
||||
|
||||
getMacAddr(dmac);
|
||||
|
||||
// Meshtastic_ab3c
|
||||
static char name[20];
|
||||
@@ -190,11 +103,30 @@ const char *getDeviceName()
|
||||
return name;
|
||||
}
|
||||
|
||||
static MeshRadio *radio = NULL;
|
||||
|
||||
static uint32_t ledBlinker()
|
||||
{
|
||||
static bool ledOn;
|
||||
ledOn ^= 1;
|
||||
|
||||
setLed(ledOn);
|
||||
|
||||
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
||||
return powerStatus.charging ? 1000 : (ledOn ? 2 : 1000);
|
||||
}
|
||||
|
||||
Periodic ledPeriodic(ledBlinker);
|
||||
|
||||
void setup()
|
||||
{
|
||||
#ifdef USE_SEGGER
|
||||
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
||||
#endif
|
||||
|
||||
// Debug
|
||||
#ifdef DEBUG_PORT
|
||||
DEBUG_PORT.begin(SERIAL_BAUD);
|
||||
DEBUG_PORT.init(); // Set serial baud rate and init our mesh console
|
||||
#endif
|
||||
|
||||
initDeepSleep();
|
||||
@@ -211,8 +143,10 @@ void setup()
|
||||
|
||||
#ifdef I2C_SDA
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
scanI2Cdevice();
|
||||
#else
|
||||
Wire.begin();
|
||||
#endif
|
||||
scanI2Cdevice();
|
||||
|
||||
// Buttons & LED
|
||||
#ifdef BUTTON_PIN
|
||||
@@ -221,22 +155,30 @@ void setup()
|
||||
#endif
|
||||
#ifdef LED_PIN
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, 1); // turn on for now
|
||||
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
||||
#endif
|
||||
|
||||
ledPeriodic.setup();
|
||||
|
||||
// Hello
|
||||
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", xstr(APP_VERSION), xstr(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
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
|
||||
ssd1306_found = false; // forget we even have the hardware
|
||||
|
||||
esp32Setup();
|
||||
#endif
|
||||
|
||||
#ifdef NRF52_SERIES
|
||||
nrf52Setup();
|
||||
#endif
|
||||
|
||||
// Initialize the screen first so we can show the logo while we start up everything else.
|
||||
if (ssd1306_found)
|
||||
screen.setup();
|
||||
|
||||
axp192Init();
|
||||
|
||||
screen.print("Started...\n");
|
||||
|
||||
// Init GPS
|
||||
@@ -244,6 +186,15 @@ void setup()
|
||||
|
||||
service.init();
|
||||
|
||||
realRouter.setup(); // required for our periodic task (kinda skanky FIXME)
|
||||
|
||||
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
|
||||
radio = new MeshRadio();
|
||||
router.addInterface(&radio->radioIf);
|
||||
|
||||
if (radio && !radio->init())
|
||||
recordCriticalError(ErrNoRadio);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -251,115 +202,44 @@ void setup()
|
||||
setCPUFast(false); // 80MHz is fine for our slow peripherals
|
||||
}
|
||||
|
||||
void initBluetooth()
|
||||
{
|
||||
DEBUG_MSG("Starting bluetooth\n");
|
||||
|
||||
// FIXME - we are leaking like crazy
|
||||
// AllocatorScope scope(btPool);
|
||||
|
||||
// Note: these callbacks might be coming in from a different thread.
|
||||
BLEServer *serve = initBLE(
|
||||
[](uint32_t pin) {
|
||||
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
|
||||
createMeshBluetoothService(serve);
|
||||
|
||||
// Start advertising - this must be done _after_ creating all services
|
||||
serve->getAdvertising()->start();
|
||||
}
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bluetoothOn) {
|
||||
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
|
||||
|
||||
bluetoothOn = on;
|
||||
if (on) {
|
||||
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
initBluetooth();
|
||||
} else {
|
||||
// We have to totally teardown our bluetooth objects to prevent leaks
|
||||
stopMeshBluetoothService(); // Must do before shutting down bluetooth
|
||||
deinitBLE();
|
||||
destroyMeshBluetoothService(); // must do after deinit, because it frees our service
|
||||
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
// heap_trace_dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ledBlinker()
|
||||
{
|
||||
static bool ledOn;
|
||||
ledOn ^= 1;
|
||||
|
||||
setLed(ledOn);
|
||||
|
||||
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
||||
return isCharging ? 1000 : (ledOn ? 2 : 1000);
|
||||
}
|
||||
|
||||
Periodic ledPeriodic(ledBlinker);
|
||||
|
||||
#if 0
|
||||
// Turn off for now
|
||||
|
||||
uint32_t axpReads()
|
||||
uint32_t axpDebugRead()
|
||||
{
|
||||
axp.debugCharging();
|
||||
DEBUG_MSG("vbus current %f\n", axp.getVbusCurrent());
|
||||
DEBUG_MSG("charge current %f\n", axp.getBattChargeCurrent());
|
||||
DEBUG_MSG("bat voltage %f\n", axp.getBattVoltage());
|
||||
DEBUG_MSG("batt pct %d\n", axp.getBattPercentage());
|
||||
DEBUG_MSG("is battery connected %d\n", axp.isBatteryConnect());
|
||||
DEBUG_MSG("is USB connected %d\n", axp.isVBUSPlug());
|
||||
DEBUG_MSG("is charging %d\n", axp.isChargeing());
|
||||
|
||||
return 30 * 1000;
|
||||
}
|
||||
|
||||
Periodic axpDebugOutput(axpReads);
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
axpDebugOutput.setup();
|
||||
#endif
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop?
|
||||
|
||||
router.loop();
|
||||
powerFSM.run_machine();
|
||||
gps.loop();
|
||||
screen.loop();
|
||||
service.loop();
|
||||
|
||||
ledPeriodic.loop();
|
||||
periodicScheduler.loop();
|
||||
// axpDebugOutput.loop();
|
||||
loopBLE();
|
||||
|
||||
// for debug printing
|
||||
// service.radio.rf95.canSleep();
|
||||
|
||||
#ifdef T_BEAM_V10
|
||||
if (axp192_found) {
|
||||
#ifdef PMU_IRQ
|
||||
if (pmu_irq) {
|
||||
pmu_irq = false;
|
||||
axp.readIRQ();
|
||||
|
||||
DEBUG_MSG("pmu irq!\n");
|
||||
|
||||
isCharging = axp.isChargeing() ? 1 : 0;
|
||||
isUSBPowered = axp.isVBUSPlug() ? 1 : 0;
|
||||
|
||||
axp.clearIRQ();
|
||||
}
|
||||
|
||||
// FIXME AXP192 interrupt is not firing, remove this temporary polling of battery state
|
||||
isCharging = axp.isChargeing() ? 1 : 0;
|
||||
isUSBPowered = axp.isVBUSPlug() ? 1 : 0;
|
||||
#ifdef DEBUG_PORT
|
||||
DEBUG_PORT.loop(); // Send/receive protobufs over the serial port
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NO_ESP32
|
||||
esp32Loop();
|
||||
#endif
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
@@ -390,6 +270,13 @@ void loop()
|
||||
showingBootScreen = false;
|
||||
}
|
||||
|
||||
// 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" : ":(");
|
||||
|
||||
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
||||
// i.e. don't just keep spinning in loop as fast as we can.
|
||||
// DEBUG_MSG("msecs %d\n", msecstosleep);
|
||||
|
||||
@@ -9,3 +9,10 @@ extern bool isUSBPowered;
|
||||
|
||||
// Global Screen singleton.
|
||||
extern meshtastic::Screen screen;
|
||||
|
||||
// Return a human readable string of the form "Meshtastic_ab13"
|
||||
const char *getDeviceName();
|
||||
|
||||
void getMacAddr(uint8_t *dmac);
|
||||
|
||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
|
||||
@@ -1,19 +1,15 @@
|
||||
#include "RH_RF95.h"
|
||||
#include "error.h"
|
||||
#include <RHMesh.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>
|
||||
|
||||
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
|
||||
/**
|
||||
* ## LoRaWAN for North America
|
||||
|
||||
@@ -28,18 +24,10 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts
|
||||
/// Sometimes while debugging it is useful to set this false, to disable rf95 accesses
|
||||
bool useHardware = true;
|
||||
|
||||
MeshRadio::MeshRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) : rf95(_pool, _rxDest), manager(rf95)
|
||||
MeshRadio::MeshRadio() // , manager(radioIf)
|
||||
{
|
||||
myNodeInfo.num_channels = NUM_CHANNELS;
|
||||
|
||||
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth so
|
||||
// incompatible radios can talk together
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 23;
|
||||
memcpy(&channelSettings.psk, &defaultpsk, sizeof(channelSettings.psk));
|
||||
strcpy(channelSettings.name, "Default");
|
||||
// Can't print strings this early - serial not setup yet
|
||||
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||
}
|
||||
@@ -51,6 +39,10 @@ bool MeshRadio::init()
|
||||
|
||||
DEBUG_MSG("Starting meshradio init...\n");
|
||||
|
||||
configChangedObserver.observe(&service.configChanged);
|
||||
preflightSleepObserver.observe(&preflightSleep);
|
||||
notifyDeepSleepObserver.observe(¬ifyDeepSleep);
|
||||
|
||||
#ifdef RESET_GPIO
|
||||
pinMode(RESET_GPIO, OUTPUT); // Deassert reset
|
||||
digitalWrite(RESET_GPIO, HIGH);
|
||||
@@ -62,10 +54,10 @@ bool MeshRadio::init()
|
||||
delay(10);
|
||||
#endif
|
||||
|
||||
manager.setThisAddress(
|
||||
radioIf.setThisAddress(
|
||||
nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor time.
|
||||
|
||||
if (!manager.init()) {
|
||||
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;
|
||||
@@ -95,13 +87,13 @@ unsigned long hash(char *str)
|
||||
return hash;
|
||||
}
|
||||
|
||||
void MeshRadio::reloadConfig()
|
||||
int MeshRadio::reloadConfig(void *unused)
|
||||
{
|
||||
rf95.setModeIdle(); // Need to be idle before doing init
|
||||
radioIf.setModeIdle(); // Need to be idle before doing init
|
||||
|
||||
// Set up default configuration
|
||||
// No Sync Words in LORA mode.
|
||||
rf95.setModemConfig(
|
||||
radioIf.setModemConfig(
|
||||
(RH_RF95::ModemConfigChoice)channelSettings.modem_config); // Radio default
|
||||
// setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
|
||||
// rf95.setPreambleLength(8); // Default is 8
|
||||
@@ -109,7 +101,7 @@ void MeshRadio::reloadConfig()
|
||||
// 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 (!rf95.setFrequency(center_freq)) {
|
||||
if (!radioIf.setFrequency(center_freq)) {
|
||||
DEBUG_MSG("setFrequency failed\n");
|
||||
assert(0); // fixme panic
|
||||
}
|
||||
@@ -120,42 +112,14 @@ void MeshRadio::reloadConfig()
|
||||
// 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.
|
||||
rf95.setTxPower(channelSettings.tx_power, false);
|
||||
radioIf.setTxPower(channelSettings.tx_power, false);
|
||||
|
||||
DEBUG_MSG("Set radio: name=%s. config=%u, ch=%d, txpower=%d\n", channelSettings.name, channelSettings.modem_config,
|
||||
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
|
||||
rf95.setModeRx();
|
||||
radioIf.setModeRx();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ErrorCode MeshRadio::send(MeshPacket *p)
|
||||
{
|
||||
lastTxStart = millis();
|
||||
|
||||
if (useHardware)
|
||||
return rf95.send(p);
|
||||
else {
|
||||
rf95.pool.release(p);
|
||||
return ERRNO_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#define TX_WATCHDOG_TIMEOUT 30 * 1000
|
||||
|
||||
void MeshRadio::loop()
|
||||
{
|
||||
// It should never take us more than 30 secs to send a packet, if it does, we have a bug, FIXME, move most of this
|
||||
// into CustomRF95
|
||||
uint32_t now = millis();
|
||||
if (lastTxStart != 0 && (now - lastTxStart) > TX_WATCHDOG_TIMEOUT && rf95.mode() == RHGenericDriver::RHModeTx) {
|
||||
DEBUG_MSG("ERROR! Bug! Tx packet took too long to send, forcing radio into rx mode");
|
||||
rf95.setModeRx();
|
||||
if (rf95.sendingPacket) { // There was probably a packet we were trying to send, free it
|
||||
rf95.pool.release(rf95.sendingPacket);
|
||||
rf95.sendingPacket = NULL;
|
||||
}
|
||||
recordCriticalError(ErrTxWatchdog);
|
||||
lastTxStart = 0; // Stop checking for now, because we just warned the developer
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
#include "CustomRF95.h"
|
||||
#include "MemoryPool.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh.pb.h"
|
||||
#include <RHMesh.h>
|
||||
|
||||
// US channel settings
|
||||
#define CH0_US 903.08f // MHz
|
||||
@@ -55,7 +55,10 @@
|
||||
#define CH_SPACING CH_SPACING_JP
|
||||
#define NUM_CHANNELS NUM_CHANNELS_JP
|
||||
#else
|
||||
#error "HW_VERSION not set"
|
||||
// HW version not set - assume US
|
||||
#define CH0 CH0_US
|
||||
#define CH_SPACING CH_SPACING_US
|
||||
#define NUM_CHANNELS NUM_CHANNELS_US
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -64,38 +67,41 @@
|
||||
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
|
||||
rf95; // the raw radio interface - for now I'm leaving public - because this class is shrinking to be almost nothing
|
||||
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(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
|
||||
MeshRadio();
|
||||
|
||||
bool init();
|
||||
|
||||
/// 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 send(MeshPacket *p);
|
||||
private:
|
||||
CallbackObserver<MeshRadio, void *> configChangedObserver =
|
||||
CallbackObserver<MeshRadio, void *>(this, &MeshRadio::reloadConfig);
|
||||
|
||||
/// Do loop callback operations (we currently FIXME poll the receive mailbox here)
|
||||
/// for received packets it will call the rx handler
|
||||
void loop();
|
||||
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
|
||||
void reloadConfig();
|
||||
int reloadConfig(void *unused = NULL);
|
||||
|
||||
private:
|
||||
// RHReliableDatagram manager; // don't use mesh yet
|
||||
RHMesh manager;
|
||||
/// Return 0 if sleep is okay
|
||||
int preflightSleepCb(void *unused = NULL) { return radioIf.canSleep() ? 0 : 1; }
|
||||
|
||||
/// Used for the tx timer watchdog, to check for bugs in our transmit code, msec of last time we did a send
|
||||
uint32_t lastTxStart = 0;
|
||||
|
||||
/// low level send, might block for mutiple seconds
|
||||
ErrorCode sendTo(NodeNum dest, const uint8_t *buf, size_t len);
|
||||
|
||||
/// enqueue a received packet in rxDest
|
||||
void handleReceive(MeshPacket *p);
|
||||
int notifyDeepSleepDb(void *unused = NULL)
|
||||
{
|
||||
radioIf.sleep();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
#include "GPS.h"
|
||||
#include "MeshBluetoothService.h"
|
||||
//#include "MeshBluetoothService.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Periodic.h"
|
||||
@@ -43,32 +44,41 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
|
||||
|
||||
MeshService service;
|
||||
|
||||
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
|
||||
#define MAX_PACKETS \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + MAX_TX_QUEUE + \
|
||||
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||
#include "Router.h"
|
||||
|
||||
#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
|
||||
#define NUM_PACKET_ID 255 // 0 is consider invalid
|
||||
|
||||
MeshService::MeshService()
|
||||
: packetPool(MAX_PACKETS), toPhoneQueue(MAX_RX_TOPHONE), fromRadioQueue(MAX_RX_FROMRADIO), fromNum(0),
|
||||
radio(packetPool, fromRadioQueue)
|
||||
static uint32_t sendOwnerCb()
|
||||
{
|
||||
service.sendOurOwner();
|
||||
|
||||
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
void MeshService::init()
|
||||
{
|
||||
sendOwnerPeriod.setup();
|
||||
nodeDB.init();
|
||||
|
||||
if (!radio.init())
|
||||
DEBUG_MSG("radio init failed\n");
|
||||
|
||||
gps.addObserver(this);
|
||||
|
||||
// No need to call this here, our periodic task will fire quite soon
|
||||
// sendOwnerPeriod();
|
||||
gpsObserver.observe(&gps);
|
||||
packetReceivedObserver.observe(&router.notifyPacketReceived);
|
||||
}
|
||||
|
||||
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
||||
@@ -76,8 +86,8 @@ void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
||||
MeshPacket *p = allocForSending();
|
||||
p->to = dest;
|
||||
p->payload.want_response = wantReplies;
|
||||
p->payload.which_variant = SubPacket_user_tag;
|
||||
User &u = p->payload.variant.user;
|
||||
p->payload.has_user = true;
|
||||
User &u = p->payload.user;
|
||||
u = owner;
|
||||
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
||||
|
||||
@@ -85,19 +95,18 @@ void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
||||
}
|
||||
|
||||
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
|
||||
MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
|
||||
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.variant.user.macaddr, sizeof(owner.macaddr)) < 0;
|
||||
bool weWin = memcmp(&owner.macaddr, &mp->payload.user.macaddr, sizeof(owner.macaddr)) < 0;
|
||||
|
||||
if (isCollision) {
|
||||
if (weWin) {
|
||||
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
|
||||
|
||||
packetPool.release(mp); // discard it
|
||||
mp = NULL;
|
||||
|
||||
sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum
|
||||
@@ -118,21 +127,21 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
|
||||
|
||||
sendOurOwner(mp->from);
|
||||
|
||||
String lcd = String("Joined: ") + mp->payload.variant.user.long_name + "\n";
|
||||
String lcd = String("Joined: ") + mp->payload.user.long_name + "\n";
|
||||
screen.print(lcd.c_str());
|
||||
}
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
void MeshService::handleIncomingPosition(MeshPacket *mp)
|
||||
void MeshService::handleIncomingPosition(const MeshPacket *mp)
|
||||
{
|
||||
if (mp->has_payload && mp->payload.which_variant == SubPacket_position_tag) {
|
||||
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.variant.position.time);
|
||||
if (mp->has_payload && mp->payload.has_position) {
|
||||
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.position.time);
|
||||
|
||||
if (mp->payload.variant.position.time) {
|
||||
if (mp->payload.position.time) {
|
||||
struct timeval tv;
|
||||
uint32_t secs = mp->payload.variant.position.time;
|
||||
uint32_t secs = mp->payload.position.time;
|
||||
|
||||
tv.tv_sec = secs;
|
||||
tv.tv_usec = 0;
|
||||
@@ -144,20 +153,18 @@ void MeshService::handleIncomingPosition(MeshPacket *mp)
|
||||
}
|
||||
}
|
||||
|
||||
void MeshService::handleFromRadio(MeshPacket *mp)
|
||||
int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||
{
|
||||
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
||||
|
||||
mp->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone
|
||||
|
||||
// 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 (!myNodeInfo.has_gps)
|
||||
if (!gps.isConnected)
|
||||
handleIncomingPosition(mp);
|
||||
else {
|
||||
DEBUG_MSG("Ignoring incoming time, because we have a GPS\n");
|
||||
}
|
||||
|
||||
if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag) {
|
||||
if (mp->has_payload && mp->payload.has_user) {
|
||||
mp = handleFromRadioUser(mp);
|
||||
}
|
||||
|
||||
@@ -174,84 +181,65 @@ void MeshService::handleFromRadio(MeshPacket *mp)
|
||||
if (d)
|
||||
releaseToPool(d);
|
||||
}
|
||||
assert(toPhoneQueue.enqueue(mp, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
|
||||
|
||||
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)
|
||||
sendNetworkPing(mp->from);
|
||||
} else
|
||||
DEBUG_MSG("Dropping vetoed User message\n");
|
||||
}
|
||||
|
||||
void MeshService::handleFromRadio()
|
||||
{
|
||||
MeshPacket *mp;
|
||||
uint32_t oldFromNum = fromNum;
|
||||
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
|
||||
handleFromRadio(mp);
|
||||
} else {
|
||||
DEBUG_MSG("Not delivering vetoed User message\n");
|
||||
}
|
||||
if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets
|
||||
bluetoothNotifyFromNum(fromNum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t sendOwnerCb()
|
||||
{
|
||||
service.sendOurOwner();
|
||||
|
||||
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
||||
}
|
||||
|
||||
Periodic sendOwnerPeriod(sendOwnerCb);
|
||||
|
||||
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
||||
void MeshService::loop()
|
||||
{
|
||||
radio.loop(); // FIXME, possibly move radio interaction to own thread
|
||||
|
||||
handleFromRadio();
|
||||
|
||||
// occasionally send our owner info
|
||||
sendOwnerPeriod.loop();
|
||||
if (oldFromNum != fromNum) { // We don't want to generate extra notifies for multiple new packets
|
||||
fromNumChanged.notifyObservers(fromNum);
|
||||
oldFromNum = fromNum;
|
||||
}
|
||||
}
|
||||
|
||||
/// The radioConfig object just changed, call this to force the hw to change to the new settings
|
||||
void MeshService::reloadConfig()
|
||||
{
|
||||
// If we can successfully set this radio to these settings, save them to disk
|
||||
radio.reloadConfig();
|
||||
nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||
configChanged.notifyObservers(NULL);
|
||||
nodeDB.saveToDisk();
|
||||
}
|
||||
|
||||
/// Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
void MeshService::handleToRadio(std::string s)
|
||||
/**
|
||||
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a
|
||||
* reference
|
||||
*/
|
||||
void MeshService::handleToRadio(MeshPacket &p)
|
||||
{
|
||||
static ToRadio r; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||
handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock
|
||||
|
||||
if (pb_decode_from_bytes((const uint8_t *)s.c_str(), s.length(), ToRadio_fields, &r)) {
|
||||
switch (r.which_variant) {
|
||||
case ToRadio_packet_tag: {
|
||||
// If our phone is sending a position, see if we can use it to set our RTC
|
||||
handleIncomingPosition(&r.variant.packet); // If it is a position packet, perhaps set our clock
|
||||
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
|
||||
p.from = nodeDB.getNodeNum();
|
||||
|
||||
r.variant.packet.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone (so we update our
|
||||
// nodedb for the local node)
|
||||
if (p.id == 0)
|
||||
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
||||
|
||||
// Send the packet into the mesh
|
||||
sendToMesh(packetPool.allocCopy(r.variant.packet));
|
||||
p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone
|
||||
// (so we update our nodedb for the local node)
|
||||
|
||||
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
|
||||
if (loopback) {
|
||||
MeshPacket *mp = packetPool.allocCopy(r.variant.packet);
|
||||
handleFromRadio(mp);
|
||||
bluetoothNotifyFromNum(fromNum); // tell the phone a new packet arrived
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_MSG("Error: unexpected ToRadio variant\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("Error: ignoring malformed toradio\n");
|
||||
// Send the packet into the mesh
|
||||
|
||||
sendToMesh(packetPool.allocCopy(p));
|
||||
|
||||
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
|
||||
if (loopback) {
|
||||
// no need to copy anymore because handle from radio assumes it should _not_ delete
|
||||
// packetPool.allocCopy(r.variant.packet);
|
||||
handleFromRadio(&p);
|
||||
// handleFromRadio will tell the phone a new packet arrived
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,12 +250,12 @@ 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.which_variant == SubPacket_position_tag) {
|
||||
if (!myNodeInfo.has_gps) {
|
||||
DEBUG_MSG("Stripping time %u from position send\n", p->payload.variant.position.time);
|
||||
p->payload.variant.position.time = 0;
|
||||
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;
|
||||
} else
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p->payload.variant.position.time);
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p->payload.position.time);
|
||||
}
|
||||
|
||||
// If the phone sent a packet just to us, don't send it out into the network
|
||||
@@ -275,8 +263,10 @@ void MeshService::sendToMesh(MeshPacket *p)
|
||||
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 (radio.send(p) != ERRNO_OK)
|
||||
DEBUG_MSG("Dropped packet because send queue was full!\n");
|
||||
if (router.send(p) != ERRNO_OK) {
|
||||
DEBUG_MSG("No radio was able to send packet, discarding...\n");
|
||||
releaseToPool(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +277,7 @@ MeshPacket *MeshService::allocForSending()
|
||||
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;
|
||||
@@ -313,21 +304,22 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = allocForSending();
|
||||
p->to = dest;
|
||||
p->payload.which_variant = SubPacket_position_tag;
|
||||
p->payload.variant.position = node->position;
|
||||
p->payload.has_position = true;
|
||||
p->payload.position = node->position;
|
||||
p->payload.want_response = wantReplies;
|
||||
p->payload.variant.position.time =
|
||||
gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||
p->payload.position.time = gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||
sendToMesh(p);
|
||||
}
|
||||
|
||||
void MeshService::onGPSChanged()
|
||||
int MeshService::onGPSChanged(void *unused)
|
||||
{
|
||||
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.which_variant = SubPacket_position_tag;
|
||||
p->payload.has_position = true;
|
||||
|
||||
Position &pos = p->payload.variant.position;
|
||||
Position &pos = p->payload.position;
|
||||
// !zero or !zero lat/long means valid
|
||||
if (gps.latitude != 0 || gps.longitude != 0) {
|
||||
if (gps.altitude != 0)
|
||||
@@ -351,10 +343,6 @@ void MeshService::onGPSChanged()
|
||||
|
||||
releaseToPool(p);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshService::onNotify(Observable *o)
|
||||
{
|
||||
DEBUG_MSG("got gps notify\n");
|
||||
onGPSChanged();
|
||||
return 0;
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
#include "MemoryPool.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "mesh.pb.h"
|
||||
@@ -13,9 +15,11 @@
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
*
|
||||
*/
|
||||
class MeshService : private Observer
|
||||
class MeshService
|
||||
{
|
||||
MemoryPool<MeshPacket> packetPool;
|
||||
CallbackObserver<MeshService, void *> gpsObserver = CallbackObserver<MeshService, void *>(this, &MeshService::onGPSChanged);
|
||||
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
|
||||
CallbackObserver<MeshService, const MeshPacket *>(this, &MeshService::handleFromRadio);
|
||||
|
||||
/// received packets waiting for the phone to process them
|
||||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||
@@ -23,15 +27,18 @@ class MeshService : private Observer
|
||||
/// FIXME - save this to flash on deep sleep
|
||||
PointerQueue<MeshPacket> toPhoneQueue;
|
||||
|
||||
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
|
||||
/// forwarded to the phone.
|
||||
PointerQueue<MeshPacket> fromRadioQueue;
|
||||
|
||||
/// The current nonce for the newest packet which has been queued for the phone
|
||||
uint32_t fromNum;
|
||||
uint32_t fromNum = 0;
|
||||
|
||||
/// Updated in loop() to detect when fromNum changes
|
||||
uint32_t oldFromNum = 0;
|
||||
|
||||
public:
|
||||
MeshRadio radio;
|
||||
/// Called when some new packets have arrived from one of the radios
|
||||
Observable<uint32_t> fromNumChanged;
|
||||
|
||||
/// Called when radio config has changed (radios should observe this and set their hardware as required)
|
||||
Observable<void *> configChanged;
|
||||
|
||||
MeshService();
|
||||
|
||||
@@ -47,8 +54,12 @@ class MeshService : private Observer
|
||||
/// Allows the bluetooth handler to free packets after they have been sent
|
||||
void releaseToPool(MeshPacket *p) { packetPool.release(p); }
|
||||
|
||||
/// Given a ToRadio buffer (from bluetooth) parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
void handleToRadio(std::string s);
|
||||
/**
|
||||
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep
|
||||
* a reference
|
||||
*/
|
||||
void handleToRadio(MeshPacket &p);
|
||||
|
||||
/// The radioConfig object just changed, call this to force the hw to change to the new settings
|
||||
void reloadConfig();
|
||||
@@ -76,21 +87,18 @@ class MeshService : private Observer
|
||||
void sendToMesh(MeshPacket *p);
|
||||
|
||||
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
|
||||
void onGPSChanged();
|
||||
/// returns 0 to allow futher processing
|
||||
int onGPSChanged(void *arg);
|
||||
|
||||
virtual void onNotify(Observable *o);
|
||||
|
||||
/// handle all the packets that just arrived from the mesh radio
|
||||
void handleFromRadio();
|
||||
|
||||
/// Handle a packet that just arrived from the radio
|
||||
void handleFromRadio(MeshPacket *p);
|
||||
/// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs
|
||||
/// to keep the packet around it makes a copy
|
||||
int handleFromRadio(const MeshPacket *p);
|
||||
|
||||
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
|
||||
MeshPacket *handleFromRadioUser(MeshPacket *mp);
|
||||
const MeshPacket *handleFromRadioUser(const MeshPacket *mp);
|
||||
|
||||
/// look at inbound packets and if they contain a position with time, possibly set our clock
|
||||
void handleIncomingPosition(MeshPacket *mp);
|
||||
void handleIncomingPosition(const MeshPacket *mp);
|
||||
};
|
||||
|
||||
extern MeshService service;
|
||||
20
src/mesh/MeshTypes.h
Normal file
20
src/mesh/MeshTypes.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
// low level types
|
||||
|
||||
#include "MemoryPool.h"
|
||||
#include "mesh.pb.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef uint8_t NodeNum;
|
||||
typedef uint8_t PacketId; // A packet sequence number
|
||||
|
||||
#define NODENUM_BROADCAST 255
|
||||
#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
|
||||
|
||||
typedef int ErrorCode;
|
||||
|
||||
/// Alloc and free packets to our global, ISR safe pool
|
||||
extern MemoryPool<MeshPacket> packetPool;
|
||||
@@ -30,7 +30,12 @@ DeviceState versions used to be defined in the .proto file but really only this
|
||||
#define DEVICESTATE_CUR_VER 7
|
||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#define FS SPIFFS
|
||||
#endif
|
||||
|
||||
// FIXME - move this somewhere else
|
||||
extern void getMacAddr(uint8_t *dmac);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -44,9 +49,46 @@ static uint8_t ourMacAddr[6];
|
||||
|
||||
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
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
|
||||
if (radioConfig.preferences.sds_secs == 0) {
|
||||
DEBUG_MSG("RadioConfig reset!\n");
|
||||
radioConfig.preferences.send_owner_interval = 4; // per sw-design.md
|
||||
radioConfig.preferences.position_broadcast_secs = 15 * 60;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 120;
|
||||
radioConfig.preferences.screen_on_secs = 5 * 60;
|
||||
radioConfig.preferences.mesh_sds_timeout_secs = 2 * 60 * 60;
|
||||
radioConfig.preferences.phone_sds_timeout_sec = 2 * 60 * 60;
|
||||
radioConfig.preferences.sds_secs = 365 * 24 * 60 * 60; // one year
|
||||
radioConfig.preferences.ls_secs = 60 * 60;
|
||||
radioConfig.preferences.phone_timeout_secs = 15 * 60;
|
||||
radioConfig.has_channel_settings = true;
|
||||
radioConfig.has_preferences = true;
|
||||
|
||||
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth
|
||||
// so incompatible radios can talk together
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 23;
|
||||
memcpy(&channelSettings.psk, &defaultpsk, sizeof(channelSettings.psk));
|
||||
strcpy(channelSettings.name, "Default");
|
||||
}
|
||||
|
||||
// temp hack for quicker testing
|
||||
/*
|
||||
radioConfig.preferences.screen_on_secs = 30;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||
radioConfig.preferences.position_broadcast_secs = 15;
|
||||
*/
|
||||
}
|
||||
|
||||
void NodeDB::init()
|
||||
{
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_radio = true;
|
||||
@@ -57,15 +99,7 @@ void NodeDB::init()
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.receive_queue_count = 0;
|
||||
|
||||
radioConfig.preferences.send_owner_interval = 4; // per sw-design.md
|
||||
radioConfig.preferences.position_broadcast_secs = 15 * 60;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 120;
|
||||
radioConfig.preferences.screen_on_secs = 30;
|
||||
radioConfig.preferences.mesh_sds_timeout_secs = 2 * 60 * 60;
|
||||
radioConfig.preferences.phone_sds_timeout_sec = 2 * 60 * 60;
|
||||
radioConfig.preferences.sds_secs = 365 * 24 * 60 * 60; // one year
|
||||
radioConfig.preferences.ls_secs = 60 * 60;
|
||||
radioConfig.preferences.phone_timeout_secs = 15 * 60;
|
||||
resetRadioConfig();
|
||||
|
||||
// default to no GPS, until one has been found by probing
|
||||
myNodeInfo.has_gps = false;
|
||||
@@ -75,7 +109,7 @@ void NodeDB::init()
|
||||
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||
|
||||
// Init our blank owner info to reasonable defaults
|
||||
esp_efuse_mac_get_default(ourMacAddr);
|
||||
getMacAddr(ourMacAddr);
|
||||
sprintf(owner.id, "!%02x%02x%02x%02x%02x%02x", ourMacAddr[0], ourMacAddr[1], ourMacAddr[2], ourMacAddr[3], ourMacAddr[4],
|
||||
ourMacAddr[5]);
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
@@ -94,14 +128,9 @@ void NodeDB::init()
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
if (!FS.begin(true)) // FIXME - do this in main?
|
||||
{
|
||||
DEBUG_MSG("ERROR SPIFFS Mount Failed\n");
|
||||
// FIXME - report failure to phone
|
||||
}
|
||||
|
||||
// saveToDisk();
|
||||
loadFromDisk();
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
|
||||
DEBUG_MSG("NODENUM=0x%x, dbsize=%d\n", myNodeInfo.my_node_num, *numNodes);
|
||||
}
|
||||
@@ -134,8 +163,15 @@ const char *preftmp = "/db.proto.tmp";
|
||||
|
||||
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);
|
||||
if (f) {
|
||||
DEBUG_MSG("Loading saved preferences\n");
|
||||
@@ -162,10 +198,14 @@ void NodeDB::loadFromDisk()
|
||||
} else {
|
||||
DEBUG_MSG("No saved preferences found\n");
|
||||
}
|
||||
#else
|
||||
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void NodeDB::saveToDisk()
|
||||
{
|
||||
#ifdef FS
|
||||
File f = FS.open(preftmp, "w");
|
||||
if (f) {
|
||||
DEBUG_MSG("Writing preferences\n");
|
||||
@@ -190,6 +230,9 @@ void NodeDB::saveToDisk()
|
||||
} else {
|
||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||
}
|
||||
#else
|
||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
const NodeInfo *NodeDB::readNextInfo()
|
||||
@@ -233,7 +276,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
{
|
||||
if (mp.has_payload) {
|
||||
const SubPacket &p = mp.payload;
|
||||
DEBUG_MSG("Update DB node 0x%x for variant %d, rx_time=%u\n", mp.from, p.which_variant, mp.rx_time);
|
||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||
|
||||
int oldNumNodes = *numNodes;
|
||||
NodeInfo *info = getOrCreateNode(mp.from);
|
||||
@@ -246,22 +289,19 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
info->position.time = mp.rx_time;
|
||||
}
|
||||
|
||||
switch (p.which_variant) {
|
||||
case SubPacket_position_tag: {
|
||||
if (p.has_position) {
|
||||
// we carefully preserve the old time, because we always trust our local timestamps more
|
||||
uint32_t oldtime = info->position.time;
|
||||
info->position = p.variant.position;
|
||||
info->position = p.position;
|
||||
info->position.time = oldtime;
|
||||
info->has_position = true;
|
||||
updateGUIforNode = info;
|
||||
break;
|
||||
}
|
||||
|
||||
case SubPacket_data_tag: {
|
||||
if (p.has_data) {
|
||||
// Keep a copy of the most recent text message.
|
||||
if (p.variant.data.typ == Data_Type_CLEAR_TEXT) {
|
||||
DEBUG_MSG("Received text msg from=0%0x, msg=%.*s\n", mp.from, p.variant.data.payload.size,
|
||||
p.variant.data.payload.bytes);
|
||||
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);
|
||||
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
|
||||
// We only store/display messages destined for us.
|
||||
devicestate.rx_text_message = mp;
|
||||
@@ -270,16 +310,15 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SubPacket_user_tag: {
|
||||
if (p.has_user) {
|
||||
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.variant.user,
|
||||
bool changed = memcmp(&info->user, &p.user,
|
||||
sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay
|
||||
|
||||
info->user = p.variant.user;
|
||||
info->user = p.user;
|
||||
DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name);
|
||||
info->has_user = true;
|
||||
|
||||
@@ -291,11 +330,6 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
// We just changed something important about the user, store our DB
|
||||
// saveToDisk();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break; // Ignore other packet types
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,9 @@ class NodeDB
|
||||
/// write to flash
|
||||
void saveToDisk();
|
||||
|
||||
// Reinit radio config if needed, because sometimes a buggy android app might send us bogus settings
|
||||
void resetRadioConfig();
|
||||
|
||||
/// given a subpacket sniffed from the network, update our DB state
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
void updateFrom(const MeshPacket &p);
|
||||
237
src/mesh/PhoneAPI.cpp
Normal file
237
src/mesh/PhoneAPI.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "PhoneAPI.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include <assert.h>
|
||||
|
||||
PhoneAPI::PhoneAPI()
|
||||
{
|
||||
assert(FromRadio_size <= MAX_TO_FROM_RADIO_SIZE);
|
||||
assert(ToRadio_size <= MAX_TO_FROM_RADIO_SIZE);
|
||||
}
|
||||
|
||||
void PhoneAPI::init()
|
||||
{
|
||||
observe(&service.fromNumChanged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a ToRadio protobuf
|
||||
*/
|
||||
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
{
|
||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||
switch (toRadioScratch.which_variant) {
|
||||
case ToRadio_packet_tag: {
|
||||
// If our phone is sending a position, see if we can use it to set our RTC
|
||||
MeshPacket &p = toRadioScratch.variant.packet;
|
||||
service.handleToRadio(p);
|
||||
break;
|
||||
}
|
||||
case ToRadio_want_config_id_tag:
|
||||
config_nonce = toRadioScratch.variant.want_config_id;
|
||||
DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce);
|
||||
state = STATE_SEND_MY_INFO;
|
||||
|
||||
DEBUG_MSG("Reset nodeinfo read pointer\n");
|
||||
nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos
|
||||
nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because
|
||||
// this will break once we have multiple instances of PhoneAPI running independently
|
||||
break;
|
||||
|
||||
case ToRadio_set_owner_tag:
|
||||
DEBUG_MSG("Client is setting owner\n");
|
||||
handleSetOwner(toRadioScratch.variant.set_owner);
|
||||
break;
|
||||
|
||||
case ToRadio_set_radio_tag:
|
||||
DEBUG_MSG("Client is setting radio\n");
|
||||
handleSetRadio(toRadioScratch.variant.set_radio);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_MSG("Error: unexpected ToRadio variant\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("Error: ignoring malformed toradio\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next packet we want to send to the phone, or NULL if no such packet is available.
|
||||
*
|
||||
* We assume buf is at least FromRadio_size bytes long.
|
||||
*
|
||||
* Our sending states progress in the following sequence (the client app ASSUMES THIS SEQUENCE, DO NOT CHANGE IT):
|
||||
* STATE_SEND_MY_INFO, // send our my info record
|
||||
STATE_SEND_RADIO,
|
||||
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
*/
|
||||
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
{
|
||||
if (!available())
|
||||
return false;
|
||||
|
||||
// In case we send a FromRadio packet
|
||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||
|
||||
// Advance states as needed
|
||||
switch (state) {
|
||||
case STATE_SEND_NOTHING:
|
||||
break;
|
||||
|
||||
case STATE_SEND_MY_INFO:
|
||||
fromRadioScratch.which_variant = FromRadio_my_info_tag;
|
||||
fromRadioScratch.variant.my_info = myNodeInfo;
|
||||
state = STATE_SEND_RADIO;
|
||||
break;
|
||||
|
||||
case STATE_SEND_RADIO:
|
||||
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
||||
fromRadioScratch.variant.radio = radioConfig;
|
||||
state = STATE_SEND_NODEINFO;
|
||||
break;
|
||||
|
||||
case STATE_SEND_NODEINFO: {
|
||||
const NodeInfo *info = nodeInfoForPhone;
|
||||
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
|
||||
|
||||
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);
|
||||
fromRadioScratch.which_variant = FromRadio_node_info_tag;
|
||||
fromRadioScratch.variant.node_info = *info;
|
||||
// Stay in current state until done sending nodeinfos
|
||||
} else {
|
||||
DEBUG_MSG("Done sending nodeinfos\n");
|
||||
state = STATE_SEND_COMPLETE_ID;
|
||||
// Go ahead and send that ID right now
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
fromRadioScratch.which_variant = FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.variant.config_complete_id = config_nonce;
|
||||
config_nonce = 0;
|
||||
state = STATE_SEND_PACKETS;
|
||||
break;
|
||||
|
||||
case STATE_LEGACY: // Treat as the same as send packets
|
||||
case STATE_SEND_PACKETS:
|
||||
// Do we have a message from the mesh?
|
||||
if (packetForPhone) {
|
||||
// Encapsulate as a FromRadio packet
|
||||
fromRadioScratch.which_variant = FromRadio_packet_tag;
|
||||
fromRadioScratch.variant.packet = *packetForPhone;
|
||||
|
||||
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
|
||||
packetForPhone = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
}
|
||||
|
||||
// Do we have a message from the mesh?
|
||||
if (fromRadioScratch.which_variant != 0) {
|
||||
// Encapsulate as a FromRadio packet
|
||||
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_variant);
|
||||
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
|
||||
DEBUG_MSG(", %d bytes\n", numbytes);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
DEBUG_MSG("no FromRadio packet available\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have data available to send to the phone
|
||||
*/
|
||||
bool PhoneAPI::available()
|
||||
{
|
||||
switch (state) {
|
||||
case STATE_SEND_NOTHING:
|
||||
return false;
|
||||
|
||||
case STATE_SEND_MY_INFO:
|
||||
return true;
|
||||
|
||||
case STATE_SEND_NODEINFO:
|
||||
if (!nodeInfoForPhone)
|
||||
nodeInfoForPhone = nodeDB.readNextInfo();
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
case STATE_SEND_RADIO:
|
||||
return true;
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
return true;
|
||||
|
||||
case STATE_LEGACY: // Treat as the same as send packets
|
||||
case STATE_SEND_PACKETS:
|
||||
// Try to pull a new packet from the service (if we haven't already)
|
||||
if (!packetForPhone)
|
||||
packetForPhone = service.getForPhone();
|
||||
return !!packetForPhone;
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// The following routines are only public for now - until the rev1 bluetooth API is removed
|
||||
//
|
||||
|
||||
void PhoneAPI::handleSetOwner(const User &o)
|
||||
{
|
||||
int changed = 0;
|
||||
|
||||
if (*o.long_name) {
|
||||
changed |= strcmp(owner.long_name, o.long_name);
|
||||
strcpy(owner.long_name, o.long_name);
|
||||
}
|
||||
if (*o.short_name) {
|
||||
changed |= strcmp(owner.short_name, o.short_name);
|
||||
strcpy(owner.short_name, o.short_name);
|
||||
}
|
||||
if (*o.id) {
|
||||
changed |= strcmp(owner.id, o.id);
|
||||
strcpy(owner.id, o.id);
|
||||
}
|
||||
|
||||
if (changed) // If nothing really changed, don't broadcast on the network or write to flash
|
||||
service.reloadOwner();
|
||||
}
|
||||
|
||||
void PhoneAPI::handleSetRadio(const RadioConfig &r)
|
||||
{
|
||||
radioConfig = r;
|
||||
|
||||
service.reloadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
|
||||
*/
|
||||
void PhoneAPI::handleToRadioPacket(MeshPacket *p) {}
|
||||
|
||||
/// If the mesh service tells us fromNum has changed, tell the phone
|
||||
int PhoneAPI::onNotify(uint32_t newValue)
|
||||
{
|
||||
if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) {
|
||||
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
|
||||
onNowHasData(newValue);
|
||||
} else
|
||||
DEBUG_MSG("(Client not yet interested in packets)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
101
src/mesh/PhoneAPI.h
Normal file
101
src/mesh/PhoneAPI.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "Observer.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh.pb.h"
|
||||
#include <string>
|
||||
|
||||
// Make sure that we never let our packets grow too large for one BLE packet
|
||||
#define MAX_TO_FROM_RADIO_SIZE 512
|
||||
|
||||
/**
|
||||
* Provides our protobuf based API which phone/PC clients can use to talk to our device
|
||||
* over UDP, bluetooth or serial.
|
||||
*
|
||||
* Subclass to customize behavior for particular type of transport (BLE, UDP, TCP, serial)
|
||||
*
|
||||
* Eventually there should be once instance of this class for each live connection (because it has a bit of state
|
||||
* for that connection)
|
||||
*/
|
||||
class PhoneAPI
|
||||
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
||||
{
|
||||
enum State {
|
||||
STATE_LEGACY, // Temporary default state - until Android apps are all updated, uses the old BLE API
|
||||
STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config
|
||||
STATE_SEND_MY_INFO, // send our my info record
|
||||
STATE_SEND_RADIO,
|
||||
// STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb
|
||||
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
};
|
||||
|
||||
State state = STATE_LEGACY;
|
||||
|
||||
/**
|
||||
* Each packet sent to the phone has an incrementing count
|
||||
*/
|
||||
uint32_t fromRadioNum = 0;
|
||||
|
||||
/// We temporarily keep the packet here between the call to available and getFromRadio. We will free it after the phone
|
||||
/// downloads it
|
||||
MeshPacket *packetForPhone = NULL;
|
||||
|
||||
/// 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;
|
||||
|
||||
public:
|
||||
PhoneAPI();
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
virtual void init();
|
||||
|
||||
/**
|
||||
* Handle a ToRadio protobuf
|
||||
*/
|
||||
virtual void handleToRadio(const uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Get the next packet we want to send to the phone
|
||||
*
|
||||
* We assume buf is at least FromRadio_size bytes long.
|
||||
* Returns number of bytes in the FromRadio packet (or 0 if no packet available)
|
||||
*/
|
||||
size_t getFromRadio(uint8_t *buf);
|
||||
|
||||
/**
|
||||
* Return true if we have data available to send to the phone
|
||||
*/
|
||||
bool available();
|
||||
|
||||
//
|
||||
// The following routines are only public for now - until the rev1 bluetooth API is removed
|
||||
//
|
||||
|
||||
void handleSetOwner(const User &o);
|
||||
void handleSetRadio(const RadioConfig &r);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
void onNowHasData(uint32_t fromRadioNum) {}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
|
||||
*/
|
||||
void handleToRadioPacket(MeshPacket *p);
|
||||
|
||||
/// If the mesh service tells us fromNum has changed, tell the phone
|
||||
virtual int onNotify(uint32_t newValue);
|
||||
};
|
||||
71
src/mesh/StreamAPI.cpp
Normal file
71
src/mesh/StreamAPI.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "StreamAPI.h"
|
||||
|
||||
#define START1 0x94
|
||||
#define START2 0xc3
|
||||
#define HEADER_LEN 4
|
||||
|
||||
void StreamAPI::loop()
|
||||
{
|
||||
writeStream();
|
||||
readStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read any rx chars from the link and call handleToRadio
|
||||
*/
|
||||
void StreamAPI::readStream()
|
||||
{
|
||||
while (stream->available()) { // Currently we never want to block
|
||||
uint8_t c = stream->read();
|
||||
|
||||
// Use the read pointer for a little state machine, first look for framing, then length bytes, then payload
|
||||
size_t ptr = rxPtr++; // assume we will probably advance the rxPtr
|
||||
|
||||
rxBuf[ptr] = c; // store all bytes (including framing)
|
||||
|
||||
if (ptr == 0) { // looking for START1
|
||||
if (c != START1)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // looking for START2
|
||||
if (c != START2)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr >= HEADER_LEN) { // we have at least read our 4 byte framing
|
||||
uint16_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing
|
||||
|
||||
if (ptr == HEADER_LEN) {
|
||||
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
|
||||
// protobuf also)
|
||||
if (len > MAX_TO_FROM_RADIO_SIZE)
|
||||
rxPtr = 0; // length is bogus, restart search for framing
|
||||
}
|
||||
|
||||
if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) {
|
||||
// If we didn't just fail the packet and we now have the right # of bytes, parse it
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
rxPtr = 0; // start over again
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* call getFromRadio() and deliver encapsulated packets to the Stream
|
||||
*/
|
||||
void StreamAPI::writeStream()
|
||||
{
|
||||
if (canWrite) {
|
||||
uint32_t len;
|
||||
do {
|
||||
// Send every packet we can
|
||||
len = getFromRadio(txBuf + HEADER_LEN);
|
||||
if (len != 0) {
|
||||
txBuf[0] = START1;
|
||||
txBuf[1] = START2;
|
||||
txBuf[2] = (len >> 8) & 0xff;
|
||||
txBuf[3] = len & 0xff;
|
||||
|
||||
stream->write(txBuf, len + HEADER_LEN);
|
||||
}
|
||||
} while (len);
|
||||
}
|
||||
}
|
||||
66
src/mesh/StreamAPI.h
Normal file
66
src/mesh/StreamAPI.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "PhoneAPI.h"
|
||||
#include "Stream.h"
|
||||
|
||||
// A To/FromRadio packet + our 32 bit header
|
||||
#define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t))
|
||||
|
||||
/**
|
||||
* A version of our 'phone' API that talks over a Stream. So therefore well suited to use with serial links
|
||||
* or TCP connections.
|
||||
*
|
||||
* ## Wire encoding
|
||||
|
||||
When sending protobuf packets over serial or TCP each packet is preceded by uint32 sent in network byte order (big endian).
|
||||
The upper 16 bits must be 0x94C3. The lower 16 bits are packet length (this encoding gives room to eventually allow quite large
|
||||
packets).
|
||||
|
||||
Implementations validate length against the maximum possible size of a BLE packet (our lowest common denominator) of 512 bytes. If
|
||||
the length provided is larger than that we assume the packet is corrupted and begin again looking for 0x4403 framing.
|
||||
|
||||
The packets flowing towards the device are ToRadio protobufs, the packets flowing from the device are FromRadio protobufs.
|
||||
The 0x94C3 marker can be used as framing to (eventually) resync if packets are corrupted over the wire.
|
||||
|
||||
Note: the 0x94C3 framing was chosen to prevent confusion with the 7 bit ascii character set. It also doesn't collide with any
|
||||
valid utf8 encoding. This makes it a bit easier to start a device outputting regular debug output on its serial port and then only
|
||||
after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding.
|
||||
|
||||
*/
|
||||
class StreamAPI : public PhoneAPI
|
||||
{
|
||||
/**
|
||||
* The stream we read/write from
|
||||
*/
|
||||
Stream *stream;
|
||||
|
||||
uint8_t rxBuf[MAX_STREAM_BUF_SIZE];
|
||||
size_t rxPtr = 0;
|
||||
|
||||
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
|
||||
|
||||
public:
|
||||
StreamAPI(Stream *_stream) : stream(_stream) {}
|
||||
|
||||
/**
|
||||
* Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the
|
||||
* phone.
|
||||
* FIXME, to support better power behavior instead move to a thread and block on serial reads.
|
||||
*/
|
||||
void loop();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read any rx chars from the link and call handleToRadio
|
||||
*/
|
||||
void readStream();
|
||||
|
||||
/**
|
||||
* call getFromRadio() and deliver encapsulated packets to the Stream
|
||||
*/
|
||||
void writeStream();
|
||||
|
||||
protected:
|
||||
/// Are we allowed to write packets to our output stream (subclasses can turn this off - i.e. SerialConsole)
|
||||
bool canWrite = true;
|
||||
};
|
||||
@@ -35,8 +35,9 @@ bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msg
|
||||
/// Read from an Arduino File
|
||||
bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
|
||||
{
|
||||
bool status = false;
|
||||
#ifndef NO_ESP32
|
||||
File *file = (File *)stream->state;
|
||||
bool status;
|
||||
|
||||
if (buf == NULL) {
|
||||
while (count-- && file->read() != EOF)
|
||||
@@ -48,14 +49,18 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
|
||||
|
||||
if (file->available() == 0)
|
||||
stream->bytes_left = 0;
|
||||
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
/// Write to an arduino file
|
||||
bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
File *file = (File *)stream->state;
|
||||
// DEBUG_MSG("writing %d bytes to protobuf file\n", count);
|
||||
return file->write(buf, count) == count;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
@@ -15,6 +15,9 @@ PB_BIND(Data, Data, 2)
|
||||
PB_BIND(User, User, AUTO)
|
||||
|
||||
|
||||
PB_BIND(RouteDiscovery, RouteDiscovery, AUTO)
|
||||
|
||||
|
||||
PB_BIND(SubPacket, SubPacket, 2)
|
||||
|
||||
|
||||
@@ -39,6 +42,9 @@ PB_BIND(MyNodeInfo, MyNodeInfo, AUTO)
|
||||
PB_BIND(DeviceState, DeviceState, 4)
|
||||
|
||||
|
||||
PB_BIND(DebugString, DebugString, 2)
|
||||
|
||||
|
||||
PB_BIND(FromRadio, FromRadio, 2)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef enum _Constants {
|
||||
} Constants;
|
||||
|
||||
typedef enum _Data_Type {
|
||||
Data_Type_SIGNAL_OPAQUE = 0,
|
||||
Data_Type_OPAQUE = 0,
|
||||
Data_Type_CLEAR_TEXT = 1,
|
||||
Data_Type_CLEAR_READACK = 2
|
||||
} Data_Type;
|
||||
@@ -32,6 +32,10 @@ typedef enum _ChannelSettings_ModemConfig {
|
||||
} ChannelSettings_ModemConfig;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _RouteDiscovery {
|
||||
pb_callback_t route;
|
||||
} RouteDiscovery;
|
||||
|
||||
typedef struct _ChannelSettings {
|
||||
int32_t tx_power;
|
||||
ChannelSettings_ModemConfig modem_config;
|
||||
@@ -45,12 +49,16 @@ typedef struct _Data {
|
||||
Data_payload_t payload;
|
||||
} Data;
|
||||
|
||||
typedef struct _DebugString {
|
||||
char message[256];
|
||||
} DebugString;
|
||||
|
||||
typedef struct _MyNodeInfo {
|
||||
int32_t my_node_num;
|
||||
bool has_gps;
|
||||
int32_t num_channels;
|
||||
char region[12];
|
||||
char hw_model[12];
|
||||
char hw_model[16];
|
||||
char firmware_version[12];
|
||||
uint32_t error_code;
|
||||
uint32_t error_address;
|
||||
@@ -106,12 +114,12 @@ typedef struct _RadioConfig {
|
||||
} RadioConfig;
|
||||
|
||||
typedef struct _SubPacket {
|
||||
pb_size_t which_variant;
|
||||
union {
|
||||
Position position;
|
||||
Data data;
|
||||
User user;
|
||||
} variant;
|
||||
bool has_position;
|
||||
Position position;
|
||||
bool has_data;
|
||||
Data data;
|
||||
bool has_user;
|
||||
User user;
|
||||
bool want_response;
|
||||
} SubPacket;
|
||||
|
||||
@@ -121,6 +129,8 @@ typedef struct _MeshPacket {
|
||||
bool has_payload;
|
||||
SubPacket payload;
|
||||
uint32_t rx_time;
|
||||
int32_t rx_snr;
|
||||
uint32_t id;
|
||||
} MeshPacket;
|
||||
|
||||
typedef struct _DeviceState {
|
||||
@@ -144,6 +154,11 @@ typedef struct _FromRadio {
|
||||
pb_size_t which_variant;
|
||||
union {
|
||||
MeshPacket packet;
|
||||
MyNodeInfo my_info;
|
||||
NodeInfo node_info;
|
||||
RadioConfig radio;
|
||||
DebugString debug_string;
|
||||
uint32_t config_complete_id;
|
||||
} variant;
|
||||
} FromRadio;
|
||||
|
||||
@@ -151,6 +166,9 @@ typedef struct _ToRadio {
|
||||
pb_size_t which_variant;
|
||||
union {
|
||||
MeshPacket packet;
|
||||
uint32_t want_config_id;
|
||||
RadioConfig set_radio;
|
||||
User set_owner;
|
||||
} variant;
|
||||
} ToRadio;
|
||||
|
||||
@@ -160,7 +178,7 @@ typedef struct _ToRadio {
|
||||
#define _Constants_MAX Constants_Unused
|
||||
#define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1))
|
||||
|
||||
#define _Data_Type_MIN Data_Type_SIGNAL_OPAQUE
|
||||
#define _Data_Type_MIN Data_Type_OPAQUE
|
||||
#define _Data_Type_MAX Data_Type_CLEAR_READACK
|
||||
#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1))
|
||||
|
||||
@@ -173,37 +191,43 @@ typedef struct _ToRadio {
|
||||
#define Position_init_default {0, 0, 0, 0, 0}
|
||||
#define Data_init_default {_Data_Type_MIN, {0, {0}}}
|
||||
#define User_init_default {"", "", "", {0}}
|
||||
#define SubPacket_init_default {0, {Position_init_default}, 0}
|
||||
#define MeshPacket_init_default {0, 0, false, SubPacket_init_default, 0}
|
||||
#define RouteDiscovery_init_default {{{NULL}, NULL}}
|
||||
#define SubPacket_init_default {false, Position_init_default, false, Data_init_default, false, User_init_default, 0}
|
||||
#define MeshPacket_init_default {0, 0, false, SubPacket_init_default, 0, 0, 0}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0}
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default}, false, MeshPacket_init_default, 0}
|
||||
#define DebugString_init_default {""}
|
||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
#define Position_init_zero {0, 0, 0, 0, 0}
|
||||
#define Data_init_zero {_Data_Type_MIN, {0, {0}}}
|
||||
#define User_init_zero {"", "", "", {0}}
|
||||
#define SubPacket_init_zero {0, {Position_init_zero}, 0}
|
||||
#define MeshPacket_init_zero {0, 0, false, SubPacket_init_zero, 0}
|
||||
#define RouteDiscovery_init_zero {{{NULL}, NULL}}
|
||||
#define SubPacket_init_zero {false, Position_init_zero, false, Data_init_zero, false, User_init_zero, 0}
|
||||
#define MeshPacket_init_zero {0, 0, false, SubPacket_init_zero, 0, 0, 0}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero}, false, MeshPacket_init_zero, 0}
|
||||
#define DebugString_init_zero {""}
|
||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define ChannelSettings_tx_power_tag 1
|
||||
#define ChannelSettings_modem_config_tag 3
|
||||
#define ChannelSettings_psk_tag 4
|
||||
#define ChannelSettings_name_tag 5
|
||||
#define Data_typ_tag 1
|
||||
#define Data_payload_tag 2
|
||||
#define DebugString_message_tag 1
|
||||
#define MyNodeInfo_my_node_num_tag 1
|
||||
#define MyNodeInfo_has_gps_tag 2
|
||||
#define MyNodeInfo_num_channels_tag 3
|
||||
@@ -250,6 +274,8 @@ typedef struct _ToRadio {
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_payload_tag 3
|
||||
#define MeshPacket_rx_time_tag 4
|
||||
#define MeshPacket_rx_snr_tag 5
|
||||
#define MeshPacket_id_tag 6
|
||||
#define DeviceState_radio_tag 1
|
||||
#define DeviceState_my_node_tag 2
|
||||
#define DeviceState_owner_tag 3
|
||||
@@ -258,8 +284,16 @@ typedef struct _ToRadio {
|
||||
#define DeviceState_version_tag 8
|
||||
#define DeviceState_rx_text_message_tag 7
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
#define FromRadio_node_info_tag 4
|
||||
#define FromRadio_radio_tag 6
|
||||
#define FromRadio_debug_string_tag 7
|
||||
#define FromRadio_config_complete_id_tag 8
|
||||
#define FromRadio_num_tag 1
|
||||
#define ToRadio_packet_tag 1
|
||||
#define ToRadio_want_config_id_tag 100
|
||||
#define ToRadio_set_radio_tag 101
|
||||
#define ToRadio_set_owner_tag 102
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define Position_FIELDLIST(X, a) \
|
||||
@@ -285,22 +319,29 @@ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4)
|
||||
#define User_CALLBACK NULL
|
||||
#define User_DEFAULT NULL
|
||||
|
||||
#define RouteDiscovery_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, REPEATED, INT32, route, 2)
|
||||
#define RouteDiscovery_CALLBACK pb_default_field_callback
|
||||
#define RouteDiscovery_DEFAULT NULL
|
||||
|
||||
#define SubPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,position,variant.position), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,data,variant.data), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,user,variant.user), 4) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, position, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, data, 3) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, user, 4) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_response, 5)
|
||||
#define SubPacket_CALLBACK NULL
|
||||
#define SubPacket_DEFAULT NULL
|
||||
#define SubPacket_variant_position_MSGTYPE Position
|
||||
#define SubPacket_variant_data_MSGTYPE Data
|
||||
#define SubPacket_variant_user_MSGTYPE User
|
||||
#define SubPacket_position_MSGTYPE Position
|
||||
#define SubPacket_data_MSGTYPE Data
|
||||
#define SubPacket_user_MSGTYPE User
|
||||
|
||||
#define MeshPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, INT32, to, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, payload, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, rx_time, 4)
|
||||
X(a, STATIC, SINGULAR, UINT32, rx_time, 4) \
|
||||
X(a, STATIC, SINGULAR, SINT32, rx_snr, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 6)
|
||||
#define MeshPacket_CALLBACK NULL
|
||||
#define MeshPacket_DEFAULT NULL
|
||||
#define MeshPacket_payload_MSGTYPE SubPacket
|
||||
@@ -379,22 +420,42 @@ X(a, STATIC, SINGULAR, UINT32, version, 8)
|
||||
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
||||
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
||||
|
||||
#define DebugString_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, message, 1)
|
||||
#define DebugString_CALLBACK NULL
|
||||
#define DebugString_DEFAULT NULL
|
||||
|
||||
#define FromRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,debug_string,variant.debug_string), 7) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8)
|
||||
#define FromRadio_CALLBACK NULL
|
||||
#define FromRadio_DEFAULT NULL
|
||||
#define FromRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define FromRadio_variant_my_info_MSGTYPE MyNodeInfo
|
||||
#define FromRadio_variant_node_info_MSGTYPE NodeInfo
|
||||
#define FromRadio_variant_radio_MSGTYPE RadioConfig
|
||||
#define FromRadio_variant_debug_string_MSGTYPE DebugString
|
||||
|
||||
#define ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,want_config_id,variant.want_config_id), 100) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,variant.set_radio), 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102)
|
||||
#define ToRadio_CALLBACK NULL
|
||||
#define ToRadio_DEFAULT NULL
|
||||
#define ToRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define ToRadio_variant_set_radio_MSGTYPE RadioConfig
|
||||
#define ToRadio_variant_set_owner_MSGTYPE User
|
||||
|
||||
extern const pb_msgdesc_t Position_msg;
|
||||
extern const pb_msgdesc_t Data_msg;
|
||||
extern const pb_msgdesc_t User_msg;
|
||||
extern const pb_msgdesc_t RouteDiscovery_msg;
|
||||
extern const pb_msgdesc_t SubPacket_msg;
|
||||
extern const pb_msgdesc_t MeshPacket_msg;
|
||||
extern const pb_msgdesc_t ChannelSettings_msg;
|
||||
@@ -403,6 +464,7 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
||||
extern const pb_msgdesc_t NodeInfo_msg;
|
||||
extern const pb_msgdesc_t MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t DeviceState_msg;
|
||||
extern const pb_msgdesc_t DebugString_msg;
|
||||
extern const pb_msgdesc_t FromRadio_msg;
|
||||
extern const pb_msgdesc_t ToRadio_msg;
|
||||
|
||||
@@ -410,6 +472,7 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define Position_fields &Position_msg
|
||||
#define Data_fields &Data_msg
|
||||
#define User_fields &User_msg
|
||||
#define RouteDiscovery_fields &RouteDiscovery_msg
|
||||
#define SubPacket_fields &SubPacket_msg
|
||||
#define MeshPacket_fields &MeshPacket_msg
|
||||
#define ChannelSettings_fields &ChannelSettings_msg
|
||||
@@ -418,6 +481,7 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define NodeInfo_fields &NodeInfo_msg
|
||||
#define MyNodeInfo_fields &MyNodeInfo_msg
|
||||
#define DeviceState_fields &DeviceState_msg
|
||||
#define DebugString_fields &DebugString_msg
|
||||
#define FromRadio_fields &FromRadio_msg
|
||||
#define ToRadio_fields &ToRadio_msg
|
||||
|
||||
@@ -425,16 +489,18 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define Position_size 46
|
||||
#define Data_size 256
|
||||
#define User_size 72
|
||||
#define SubPacket_size 261
|
||||
#define MeshPacket_size 292
|
||||
/* RouteDiscovery_size depends on runtime parameters */
|
||||
#define SubPacket_size 383
|
||||
#define MeshPacket_size 426
|
||||
#define ChannelSettings_size 44
|
||||
#define RadioConfig_size 120
|
||||
#define RadioConfig_UserPreferences_size 72
|
||||
#define NodeInfo_size 155
|
||||
#define MyNodeInfo_size 81
|
||||
#define DeviceState_size 15076
|
||||
#define FromRadio_size 301
|
||||
#define ToRadio_size 295
|
||||
#define MyNodeInfo_size 85
|
||||
#define DeviceState_size 19502
|
||||
#define DebugString_size 258
|
||||
#define FromRadio_size 435
|
||||
#define ToRadio_size 429
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
3
src/nrf52/FS.h
Normal file
3
src/nrf52/FS.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
// FIXME - make a FS abstraction for NRF52
|
||||
241
src/nrf52/NRF52Bluetooth.cpp
Normal file
241
src/nrf52/NRF52Bluetooth.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "NRF52Bluetooth.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include <bluefruit.h>
|
||||
|
||||
/* HRM Service Definitions
|
||||
* Heart Rate Monitor Service: 0x180D
|
||||
* Heart Rate Measurement Char: 0x2A37
|
||||
* Body Sensor Location Char: 0x2A38
|
||||
*/
|
||||
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
|
||||
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
|
||||
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
|
||||
|
||||
BLEDis bledis; // DIS (Device Information Service) helper class instance
|
||||
BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
BLEDfu bledfu; // DFU software update helper service
|
||||
|
||||
uint8_t bps = 0;
|
||||
|
||||
void connect_callback(uint16_t conn_handle)
|
||||
{
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
|
||||
DEBUG_MSG("Connected to %s\n", central_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked when a connection is dropped
|
||||
* @param conn_handle connection where this event happens
|
||||
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
|
||||
*/
|
||||
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
(void)conn_handle;
|
||||
(void)reason;
|
||||
|
||||
DEBUG_MSG("Disconnected, reason = 0x%x\n", reason);
|
||||
}
|
||||
|
||||
void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
{
|
||||
// Display the raw request packet
|
||||
DEBUG_MSG("CCCD Updated: %u\n", cccd_value);
|
||||
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
if (chr->uuid == hrmc.uuid) {
|
||||
if (chr->notifyEnabled(conn_hdl)) {
|
||||
DEBUG_MSG("Heart Rate Measurement 'Notify' enabled\n");
|
||||
} else {
|
||||
DEBUG_MSG("Heart Rate Measurement 'Notify' disabled\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startAdv(void)
|
||||
{
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
Bluefruit.Advertising.addTxPower();
|
||||
|
||||
// Include HRM Service UUID
|
||||
Bluefruit.Advertising.addService(hrms);
|
||||
|
||||
// Include Name
|
||||
Bluefruit.Advertising.addName();
|
||||
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
|
||||
}
|
||||
|
||||
void setupHRM(void)
|
||||
{
|
||||
// Configure the Heart Rate Monitor service
|
||||
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
|
||||
// Supported Characteristics:
|
||||
// Name UUID Requirement Properties
|
||||
// ---------------------------- ------ ----------- ----------
|
||||
// Heart Rate Measurement 0x2A37 Mandatory Notify
|
||||
// Body Sensor Location 0x2A38 Optional Read
|
||||
// Heart Rate Control Point 0x2A39 Conditional Write <-- Not used here
|
||||
hrms.begin();
|
||||
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
|
||||
// Configure the Heart Rate Measurement characteristic
|
||||
// See:
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
|
||||
// Properties = Notify
|
||||
// Min Len = 1
|
||||
// Max Len = 8
|
||||
// B0 = UINT8 - Flag (MANDATORY)
|
||||
// b5:7 = Reserved
|
||||
// b4 = RR-Internal (0 = Not present, 1 = Present)
|
||||
// b3 = Energy expended status (0 = Not present, 1 = Present)
|
||||
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and
|
||||
// detected) b0 = Value format (0 = UINT8, 1 = UINT16)
|
||||
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
|
||||
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
|
||||
// B4:5 = UINT16 - Energy expended in joules
|
||||
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
|
||||
hrmc.setProperties(CHR_PROPS_NOTIFY);
|
||||
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
|
||||
hrmc.setFixedLen(2);
|
||||
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
|
||||
hrmc.begin();
|
||||
uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and detected
|
||||
hrmc.write(hrmdata, 2);
|
||||
|
||||
// Configure the Body Sensor Location characteristic
|
||||
// See:
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
|
||||
// Properties = Read
|
||||
// Min Len = 1
|
||||
// Max Len = 1
|
||||
// B0 = UINT8 - Body Sensor Location
|
||||
// 0 = Other
|
||||
// 1 = Chest
|
||||
// 2 = Wrist
|
||||
// 3 = Finger
|
||||
// 4 = Hand
|
||||
// 5 = Ear Lobe
|
||||
// 6 = Foot
|
||||
// 7:255 = Reserved
|
||||
bslc.setProperties(CHR_PROPS_READ);
|
||||
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
|
||||
bslc.setFixedLen(1);
|
||||
bslc.begin();
|
||||
bslc.write8(2); // Set the characteristic to 'Wrist' (2)
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
DEBUG_MSG("Initialise the Bluefruit nRF52 module\n");
|
||||
Bluefruit.begin();
|
||||
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName()); // FIXME
|
||||
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(connect_callback);
|
||||
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
||||
|
||||
// Configure and Start the Device Information Service
|
||||
DEBUG_MSG("Configuring the Device Information Service\n");
|
||||
bledis.setManufacturer("meshtastic.org");
|
||||
bledis.setModel("NRF52-meshtastic"); // FIXME
|
||||
bledis.begin();
|
||||
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
DEBUG_MSG("Configuring the Battery Service\n");
|
||||
blebas.begin();
|
||||
blebas.write(42); // FIXME, report real power levels
|
||||
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
DEBUG_MSG("Configuring the Heart Rate Monitor Service\n");
|
||||
setupHRM();
|
||||
|
||||
// Setup the advertising packet(s)
|
||||
DEBUG_MSG("Setting up the advertising payload(s)\n");
|
||||
startAdv();
|
||||
|
||||
DEBUG_MSG("Advertising\n");
|
||||
}
|
||||
|
||||
/*
|
||||
void loop()
|
||||
{
|
||||
digitalToggle(LED_RED);
|
||||
|
||||
if ( Bluefruit.connected() ) {
|
||||
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
|
||||
|
||||
// Note: We use .notify instead of .write!
|
||||
// If it is connected but CCCD is not enabled
|
||||
// The characteristic's value is still updated although notification is not sent
|
||||
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
|
||||
Serial.print("Heart Rate Measurement updated to: "); Serial.println(bps);
|
||||
}else{
|
||||
Serial.println("ERROR: Notify not set in the CCCD or not connected!");
|
||||
}
|
||||
}
|
||||
|
||||
// Only send update once per second
|
||||
delay(1000);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
examples of advanced characteristics. use setReadAuthorizeCallback to prepare data for reads by others
|
||||
|
||||
err_t BLEDfu::begin(void)
|
||||
{
|
||||
// Invoke base class begin()
|
||||
VERIFY_STATUS( BLEService::begin() );
|
||||
|
||||
// No need to keep packet & revision characteristics
|
||||
BLECharacteristic chr_packet(UUID128_CHR_DFU_PACKET);
|
||||
chr_packet.setTempMemory();
|
||||
chr_packet.setProperties(CHR_PROPS_WRITE_WO_RESP);
|
||||
chr_packet.setMaxLen(20);
|
||||
VERIFY_STATUS( chr_packet.begin() );
|
||||
|
||||
_chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_NOTIFY);
|
||||
_chr_control.setMaxLen(23);
|
||||
_chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
|
||||
VERIFY_STATUS( _chr_control.begin() );
|
||||
|
||||
BLECharacteristic chr_revision(UUID128_CHR_DFU_REVISON);
|
||||
chr_revision.setTempMemory();
|
||||
chr_revision.setProperties(CHR_PROPS_READ);
|
||||
chr_revision.setFixedLen(2);
|
||||
VERIFY_STATUS( chr_revision.begin());
|
||||
chr_revision.write16(DFU_REV_APPMODE);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
*/
|
||||
8
src/nrf52/NRF52Bluetooth.h
Normal file
8
src/nrf52/NRF52Bluetooth.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
class NRF52Bluetooth
|
||||
{
|
||||
public:
|
||||
void setup();
|
||||
};
|
||||
|
||||
97
src/nrf52/PmuBQ25703A.cpp
Normal file
97
src/nrf52/PmuBQ25703A.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "PmuBQ25703A.h"
|
||||
#include <assert.h>
|
||||
|
||||
// Default address for device. Note, it is without read/write bit. When read with analyser,
|
||||
// this will appear 1 bit shifted to the left
|
||||
#define BQ25703ADevaddr 0xD6
|
||||
|
||||
const byte Lorro_BQ25703A::BQ25703Aaddr = BQ25703ADevaddr;
|
||||
|
||||
void PmuBQ25703A::init()
|
||||
{
|
||||
// Set the watchdog timer to not have a timeout
|
||||
regs.chargeOption0.set_WDTMR_ADJ(0);
|
||||
assert(writeRegEx(regs.chargeOption0)); // FIXME, instead log a critical hw failure and reboot
|
||||
delay(15); // FIXME, why are these delays required? - check datasheet
|
||||
|
||||
// Set the ADC on IBAT and PSYS to record values
|
||||
// When changing bitfield values, call the writeRegEx function
|
||||
// This is so you can change all the bits you want before sending out the byte.
|
||||
regs.chargeOption1.set_EN_IBAT(1);
|
||||
regs.chargeOption1.set_EN_PSYS(1);
|
||||
assert(writeRegEx(regs.chargeOption1));
|
||||
delay(15);
|
||||
|
||||
// Set ADC to make continuous readings. (uses more power)
|
||||
regs.aDCOption.set_ADC_CONV(1);
|
||||
// Set individual ADC registers to read. All have default off.
|
||||
regs.aDCOption.set_EN_ADC_VBUS(1);
|
||||
regs.aDCOption.set_EN_ADC_PSYS(1);
|
||||
regs.aDCOption.set_EN_ADC_IDCHG(1);
|
||||
regs.aDCOption.set_EN_ADC_ICHG(1);
|
||||
regs.aDCOption.set_EN_ADC_VSYS(1);
|
||||
regs.aDCOption.set_EN_ADC_VBAT(1);
|
||||
// Once bits have been twiddled, send bytes to device
|
||||
assert(writeRegEx(regs.aDCOption));
|
||||
delay(15);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
//Initialise the device and library
|
||||
Lorro_BQ25703A BQ25703A;
|
||||
|
||||
//Instantiate with reference to global set
|
||||
extern Lorro_BQ25703A::Regt BQ25703Areg;
|
||||
|
||||
uint32_t previousMillis;
|
||||
uint16_t loopInterval = 1000;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
uint32_t currentMillis = millis();
|
||||
|
||||
if( currentMillis - previousMillis > loopInterval ){
|
||||
previousMillis = currentMillis;
|
||||
|
||||
Serial.print( "Voltage of VBUS: " );
|
||||
Serial.print( BQ25703Areg.aDCVBUSPSYS.get_VBUS() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "System power usage: " );
|
||||
Serial.print( BQ25703Areg.aDCVBUSPSYS.get_sysPower() );
|
||||
Serial.println( "W" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VBAT: " );
|
||||
Serial.print( BQ25703Areg.aDCVSYSVBAT.get_VBAT() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VSYS: " );
|
||||
Serial.print( BQ25703Areg.aDCVSYSVBAT.get_VSYS() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Charging current: " );
|
||||
Serial.print( BQ25703Areg.aDCIBAT.get_ICHG() );
|
||||
Serial.println( "mA" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VSYS: " );
|
||||
Serial.print( BQ25703Areg.aDCIBAT.get_IDCHG() );
|
||||
Serial.println( "mA" );
|
||||
delay( 15 );
|
||||
|
||||
}
|
||||
|
||||
}*/
|
||||
22
src/nrf52/PmuBQ25703A.h
Normal file
22
src/nrf52/PmuBQ25703A.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Lorro_BQ25703A.h>
|
||||
|
||||
class PmuBQ25703A : private Lorro_BQ25703A
|
||||
{
|
||||
Lorro_BQ25703A::Regt regs;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Configure the PMU for our board
|
||||
*/
|
||||
void init();
|
||||
|
||||
// Methods to have a common API with AXP192
|
||||
bool isBatteryConnect() { return true; } // FIXME
|
||||
bool isVBUSPlug() { return true; }
|
||||
bool isChargeing() { return true; } // FIXME, intentional misspelling
|
||||
|
||||
/// battery voltage in mV
|
||||
int getBattVoltage() { return 3200; }
|
||||
};
|
||||
3
src/nrf52/SPIFFS.h
Normal file
3
src/nrf52/SPIFFS.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
// FIXME - make a FS abstraction for NRF52
|
||||
2
src/nrf52/main-bare.cpp
Normal file
2
src/nrf52/main-bare.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "target_specific.h"
|
||||
|
||||
70
src/nrf52/main-nrf52.cpp
Normal file
70
src/nrf52/main-nrf52.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "NRF52Bluetooth.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
#include <ble_gap.h>
|
||||
#include <memory.h>
|
||||
#include <nrf52840.h>
|
||||
|
||||
// #define USE_SOFTDEVICE
|
||||
|
||||
static inline void debugger_break(void)
|
||||
{
|
||||
__asm volatile("bkpt #0x01\n\t"
|
||||
"mov pc, lr\n\t");
|
||||
}
|
||||
|
||||
// handle standard gcc assert failures
|
||||
void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *failedexpr)
|
||||
{
|
||||
DEBUG_MSG("assert failed %s: %d, %s, test=%s\n", file, line, func, failedexpr);
|
||||
debugger_break();
|
||||
while (1)
|
||||
; // FIXME, reboot!
|
||||
}
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
ble_gap_addr_t addr;
|
||||
|
||||
#ifdef USE_SOFTDEVICE
|
||||
uint32_t res = sd_ble_gap_addr_get(&addr);
|
||||
assert(res == NRF_SUCCESS);
|
||||
memcpy(dmac, addr.addr, 6);
|
||||
#else
|
||||
const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR;
|
||||
dmac[5] = src[0];
|
||||
dmac[4] = src[1];
|
||||
dmac[3] = src[2];
|
||||
dmac[2] = src[3];
|
||||
dmac[1] = src[4];
|
||||
dmac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
|
||||
#endif
|
||||
}
|
||||
|
||||
NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
static bool bleOn = false;
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bleOn) {
|
||||
if (on) {
|
||||
if (!nrf52Bluetooth) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("FIXME: implement BLE disable\n");
|
||||
}
|
||||
bleOn = on;
|
||||
}
|
||||
}
|
||||
|
||||
#include "PmuBQ25703A.h"
|
||||
|
||||
PmuBQ25703A pmu;
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
// Not yet on board
|
||||
// pmu.init();
|
||||
}
|
||||
20
src/power.h
Normal file
20
src/power.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
/// Describes the state of the power system.
|
||||
struct PowerStatus {
|
||||
/// Whether we have a battery connected
|
||||
bool haveBattery;
|
||||
/// Battery voltage in mV, valid if haveBattery is true
|
||||
int batteryVoltageMv;
|
||||
/// Whether USB is connected
|
||||
bool usb;
|
||||
/// Whether we are charging the battery
|
||||
bool charging;
|
||||
};
|
||||
|
||||
} // namespace meshtastic
|
||||
|
||||
extern meshtastic::PowerStatus powerStatus;
|
||||
@@ -1,24 +1,28 @@
|
||||
#include "CustomRF95.h"
|
||||
#include "NodeDB.h"
|
||||
#include "NodeDB.h" // FIXME, this class should not need to touch nodedb
|
||||
#include "assert.h"
|
||||
#include "configuration.h"
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
#ifdef RF95_IRQ_GPIO
|
||||
|
||||
/// A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
|
||||
#define MAX_RHPACKETLEN 251
|
||||
static uint8_t radiobuf[MAX_RHPACKETLEN];
|
||||
|
||||
CustomRF95::CustomRF95(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest)
|
||||
: RH_RF95(NSS_GPIO, DIO0_GPIO), pool(_pool), rxDest(_rxDest), txQueue(MAX_TX_QUEUE), sendingPacket(NULL)
|
||||
{
|
||||
}
|
||||
CustomRF95::CustomRF95() : RH_RF95(NSS_GPIO, RF95_IRQ_GPIO), txQueue(MAX_TX_QUEUE) {}
|
||||
|
||||
bool CustomRF95::canSleep()
|
||||
{
|
||||
// We allow initializing mode, because sometimes while testing we don't ever call init() to turn on the hardware
|
||||
DEBUG_MSG("canSleep, mode=%d, isRx=%d, txEmpty=%d, txGood=%d\n", _mode, _isReceiving, txQueue.isEmpty(), _txGood);
|
||||
return (_mode == RHModeInitialising || _mode == RHModeIdle || _mode == RHModeRx) && !_isReceiving && txQueue.isEmpty();
|
||||
bool isRx = isReceiving();
|
||||
|
||||
bool res = (_mode == RHModeInitialising || _mode == RHModeIdle || _mode == RHModeRx) && !isRx && txQueue.isEmpty();
|
||||
if (!res) // only print debug messages if we are vetoing sleep
|
||||
DEBUG_MSG("radio wait to sleep, mode=%d, isRx=%d, txEmpty=%d, txGood=%d\n", _mode, isRx, txQueue.isEmpty(), _txGood);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CustomRF95::sleep()
|
||||
@@ -45,9 +49,16 @@ ErrorCode CustomRF95::send(MeshPacket *p)
|
||||
// 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.
|
||||
if (_mode == RHModeIdle || (_mode == RHModeRx && !_isReceiving)) {
|
||||
if (_mode == RHModeIdle || (_mode == RHModeRx && !isReceiving())) {
|
||||
// if the radio is idle, we can send right away
|
||||
DEBUG_MSG("immedate send on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", txGood(), rxGood(), rxBad());
|
||||
DEBUG_MSG("immediate send on mesh fr=0x%x,to=0x%x,id=%d\n (txGood=%d,rxGood=%d,rxBad=%d)\n", p->from, p->to, p->id,
|
||||
txGood(), rxGood(), rxBad());
|
||||
|
||||
waitPacketSent(); // Make sure we dont interrupt an outgoing message
|
||||
|
||||
if (!waitCAD())
|
||||
return false; // Check channel activity
|
||||
|
||||
startSend(p);
|
||||
return ERRNO_OK;
|
||||
} else {
|
||||
@@ -55,7 +66,7 @@ ErrorCode CustomRF95::send(MeshPacket *p)
|
||||
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
|
||||
pool.release(p);
|
||||
packetPool.release(p);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -67,13 +78,12 @@ void CustomRF95::handleInterrupt()
|
||||
{
|
||||
RH_RF95::handleInterrupt();
|
||||
|
||||
BaseType_t higherPriWoken = false;
|
||||
if (_mode == RHModeIdle) // We are now done sending or receiving
|
||||
{
|
||||
if (sendingPacket) // Were we sending?
|
||||
{
|
||||
// We are done sending that packet, release it
|
||||
pool.releaseFromISR(sendingPacket, &higherPriWoken);
|
||||
packetPool.release(sendingPacket);
|
||||
sendingPacket = NULL;
|
||||
// DEBUG_MSG("Done with send\n");
|
||||
}
|
||||
@@ -91,12 +101,14 @@ void CustomRF95::handleInterrupt()
|
||||
// DEBUG_MSG("Received packet from mesh src=0x%x,dest=0x%x,id=%d,len=%d rxGood=%d,rxBad=%d,freqErr=%d,snr=%d\n",
|
||||
// srcaddr, destaddr, id, rxlen, rf95.rxGood(), rf95.rxBad(), freqerr, snr);
|
||||
|
||||
MeshPacket *mp = pool.allocZeroed();
|
||||
MeshPacket *mp = packetPool.allocZeroed();
|
||||
|
||||
SubPacket *p = &mp->payload;
|
||||
|
||||
mp->from = _rxHeaderFrom;
|
||||
mp->to = _rxHeaderTo;
|
||||
mp->id = _rxHeaderId;
|
||||
|
||||
//_rxHeaderId = _buf[2];
|
||||
//_rxHeaderFlags = _buf[3];
|
||||
|
||||
@@ -110,43 +122,35 @@ void CustomRF95::handleInterrupt()
|
||||
}
|
||||
|
||||
if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) {
|
||||
pool.releaseFromISR(mp, &higherPriWoken);
|
||||
packetPool.release(mp);
|
||||
} else {
|
||||
// parsing was successful, queue for our recipient
|
||||
mp->has_payload = true;
|
||||
|
||||
assert(rxDest.enqueueFromISR(mp, &higherPriWoken)); // NOWAIT - fixme, if queue is full, delete older messages
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
|
||||
clearRxBuf(); // This message accepted and cleared
|
||||
}
|
||||
|
||||
higherPriWoken |= handleIdleISR();
|
||||
handleIdleISR();
|
||||
}
|
||||
|
||||
// If we call this _IT WILL NOT RETURN_
|
||||
if (higherPriWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
/** The ISR doesn't have any good work to do, give a new assignment.
|
||||
*
|
||||
* Return true if a higher pri task has woken
|
||||
*/
|
||||
bool CustomRF95::handleIdleISR()
|
||||
void CustomRF95::handleIdleISR()
|
||||
{
|
||||
BaseType_t higherPriWoken = false;
|
||||
|
||||
// First send any outgoing packets we have ready
|
||||
MeshPacket *txp = txQueue.dequeuePtrFromISR(0);
|
||||
MeshPacket *txp = txQueue.dequeuePtr(0);
|
||||
if (txp)
|
||||
startSend(txp);
|
||||
else {
|
||||
// Nothing to send, let's switch back to receive mode
|
||||
setModeRx();
|
||||
}
|
||||
|
||||
return higherPriWoken;
|
||||
}
|
||||
|
||||
/// This routine might be called either from user space or ISR
|
||||
@@ -157,14 +161,18 @@ void CustomRF95::startSend(MeshPacket *txp)
|
||||
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
||||
assert(txp->has_payload);
|
||||
|
||||
lastTxStart = millis();
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(radiobuf, sizeof(radiobuf), SubPacket_fields, &txp->payload);
|
||||
|
||||
sendingPacket = txp;
|
||||
|
||||
setHeaderTo(txp->to);
|
||||
setHeaderFrom(nodeDB.getNodeNum()); // We must do this before each send, because we might have just changed our nodenum
|
||||
setHeaderId(txp->id);
|
||||
|
||||
// setHeaderId(0);
|
||||
// if the sender nodenum is zero, that means uninitialized
|
||||
assert(txp->from);
|
||||
setHeaderFrom(txp->from); // We must do this before each send, because we might have just changed our nodenum
|
||||
|
||||
assert(numbytes <= 251); // Make sure we don't overflow the tiny max packet size
|
||||
|
||||
@@ -173,3 +181,28 @@ void CustomRF95::startSend(MeshPacket *txp)
|
||||
int res = RH_RF95::send(radiobuf, numbytes);
|
||||
assert(res);
|
||||
}
|
||||
|
||||
#define TX_WATCHDOG_TIMEOUT 30 * 1000
|
||||
|
||||
#include "error.h"
|
||||
|
||||
void CustomRF95::loop()
|
||||
{
|
||||
RH_RF95::loop();
|
||||
|
||||
// It should never take us more than 30 secs to send a packet, if it does, we have a bug, FIXME, move most of this
|
||||
// into CustomRF95
|
||||
uint32_t now = millis();
|
||||
if (lastTxStart != 0 && (now - lastTxStart) > TX_WATCHDOG_TIMEOUT && mode() == RHGenericDriver::RHModeTx) {
|
||||
DEBUG_MSG("ERROR! Bug! Tx packet took too long to send, forcing radio into rx mode\n");
|
||||
setModeRx();
|
||||
if (sendingPacket) { // There was probably a packet we were trying to send, free it
|
||||
packetPool.release(sendingPacket);
|
||||
sendingPacket = NULL;
|
||||
}
|
||||
recordCriticalError(ErrTxWatchdog);
|
||||
lastTxStart = 0; // Stop checking for now, because we just warned the developer
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "MemoryPool.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "mesh.pb.h"
|
||||
#include <RHMesh.h>
|
||||
#include <RH_RF95.h>
|
||||
|
||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||
@@ -12,20 +9,19 @@
|
||||
/**
|
||||
* A version of the RF95 driver which is smart enough to manage packets via queues (no polling or blocking in user threads!)
|
||||
*/
|
||||
class CustomRF95 : public RH_RF95
|
||||
class CustomRF95 : public RH_RF95, public RadioInterface
|
||||
{
|
||||
friend class MeshRadio; // for debugging we let that class touch pool
|
||||
|
||||
MemoryPool<MeshPacket> &pool;
|
||||
PointerQueue<MeshPacket> &rxDest;
|
||||
PointerQueue<MeshPacket> txQueue;
|
||||
|
||||
MeshPacket *sendingPacket; // The packet we are currently sending
|
||||
uint32_t lastTxStart = 0L;
|
||||
|
||||
public:
|
||||
/** 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
|
||||
*/
|
||||
CustomRF95(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
|
||||
CustomRF95();
|
||||
|
||||
/**
|
||||
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
|
||||
@@ -44,6 +40,8 @@ class CustomRF95 : public RH_RF95
|
||||
|
||||
bool init();
|
||||
|
||||
void loop(); // Idle processing
|
||||
|
||||
protected:
|
||||
// After doing standard behavior, check to see if a new packet arrived or one was sent and start a new send or receive as
|
||||
// necessary
|
||||
@@ -54,5 +52,5 @@ class CustomRF95 : public RH_RF95
|
||||
void startSend(MeshPacket *txp);
|
||||
|
||||
/// Return true if a higher pri task has woken
|
||||
bool handleIdleISR();
|
||||
void handleIdleISR();
|
||||
};
|
||||
126
src/rf95/FloodingRouter.cpp
Normal file
126
src/rf95/FloodingRouter.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "FloodingRouter.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
|
||||
/// We clear our old flood record five minute after we see the last of it
|
||||
#define FLOOD_EXPIRE_TIME (5 * 60 * 1000L)
|
||||
|
||||
FloodingRouter::FloodingRouter() : toResend(MAX_NUM_NODES)
|
||||
{
|
||||
recentBroadcasts.reserve(MAX_NUM_NODES); // Prealloc the worst case # of records - to prevent heap fragmentation
|
||||
// setup our periodic task
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// We update our table of recent broadcasts, even for messages we send
|
||||
wasSeenRecently(p);
|
||||
|
||||
return Router::send(p);
|
||||
}
|
||||
|
||||
// Return a delay in msec before sending the next packet
|
||||
uint32_t getRandomDelay()
|
||||
{
|
||||
return random(200, 10 * 1000L); // between 200ms and 10s
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 method will free the provided packet
|
||||
*/
|
||||
void FloodingRouter::handleReceived(MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) {
|
||||
DEBUG_MSG("Ignoring incoming floodmsg, because we've already seen it\n");
|
||||
packetPool.release(p);
|
||||
} else {
|
||||
if (p->to == NODENUM_BROADCAST) {
|
||||
if (p->id != 0) {
|
||||
uint32_t delay = getRandomDelay();
|
||||
|
||||
DEBUG_MSG("Rebroadcasting received floodmsg to neighbors in %u msec, fr=0x%x,to=0x%x,id=%d\n", delay, p->from,
|
||||
p->to, p->id);
|
||||
|
||||
MeshPacket *tosend = packetPool.allocCopy(*p);
|
||||
toResend.enqueue(tosend);
|
||||
setPeriod(delay); // This will work even if we were already waiting a random delay
|
||||
} else {
|
||||
DEBUG_MSG("Ignoring a simple (0 hop) broadcast\n");
|
||||
}
|
||||
}
|
||||
|
||||
// handle the packet as normal
|
||||
Router::handleReceived(p);
|
||||
}
|
||||
}
|
||||
|
||||
void FloodingRouter::doTask()
|
||||
{
|
||||
MeshPacket *p = toResend.dequeuePtr(0);
|
||||
|
||||
if (p) {
|
||||
DEBUG_MSG("Sending delayed message!\n");
|
||||
// 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(p);
|
||||
}
|
||||
|
||||
if (toResend.isEmpty())
|
||||
disable(); // no more work right now
|
||||
else {
|
||||
setPeriod(getRandomDelay());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update recentBroadcasts and return true if we have already seen this packet
|
||||
*/
|
||||
bool FloodingRouter::wasSeenRecently(const MeshPacket *p)
|
||||
{
|
||||
if (p->to != NODENUM_BROADCAST)
|
||||
return false; // Not a broadcast, so we don't care
|
||||
|
||||
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 (int i = 0; i < recentBroadcasts.size();) {
|
||||
BroadcastRecord &r = recentBroadcasts[i];
|
||||
|
||||
if ((now - r.rxTimeMsec) >= FLOOD_EXPIRE_TIME) {
|
||||
DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
||||
recentBroadcasts.erase(recentBroadcasts.begin() + i); // delete old record
|
||||
} else {
|
||||
if (r.id == p->id && r.sender == p->from) {
|
||||
DEBUG_MSG("Found existing broadcast 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
|
||||
r.rxTimeMsec = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find an existing record, make one
|
||||
BroadcastRecord r;
|
||||
r.id = p->id;
|
||||
r.sender = p->from;
|
||||
r.rxTimeMsec = now;
|
||||
recentBroadcasts.push_back(r);
|
||||
DEBUG_MSG("Adding broadcast record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
|
||||
return false;
|
||||
}
|
||||
83
src/rf95/FloodingRouter.h
Normal file
83
src/rf95/FloodingRouter.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#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)
|
||||
*
|
||||
* Rules for broadcasting (listing here for now, will move elsewhere eventually):
|
||||
|
||||
If to==BROADCAST and id==0, this is a simple broadcast (0 hops). It will be
|
||||
sent only by the current node and other nodes will not attempt to rebroadcast
|
||||
it.
|
||||
|
||||
If to==BROADCAST and id!=0, this is a "naive flooding" broadcast. The initial
|
||||
node will send it on all local interfaces.
|
||||
|
||||
When other nodes receive this message, they will
|
||||
first check if their recentBroadcasts table contains the (from, id) pair that
|
||||
indicates this message. If so, we've already seen it - so we discard it. If
|
||||
not, we add it to the table and then resend this message on all interfaces.
|
||||
When resending we are careful to use the "from" ID of the original sender. Not
|
||||
our own ID. When resending we pick a random delay between 0 and 10 seconds to
|
||||
decrease the chance of collisions with transmitters we can not even hear.
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
*
|
||||
*/
|
||||
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
|
||||
*/
|
||||
virtual ErrorCode send(MeshPacket *p);
|
||||
|
||||
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.
|
||||
*
|
||||
* Note: this method will free the provided packet
|
||||
*/
|
||||
virtual void handleReceived(MeshPacket *p);
|
||||
|
||||
virtual void doTask();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Update recentBroadcasts and return true if we have already seen this packet
|
||||
*/
|
||||
bool wasSeenRecently(const MeshPacket *p);
|
||||
};
|
||||
17
src/rf95/LICENSE
Normal file
17
src/rf95/LICENSE
Normal file
@@ -0,0 +1,17 @@
|
||||
This software is Copyright (C) 2008 Mike McCauley. Use is subject to license
|
||||
conditions. The main licensing options available are GPL V2 or Commercial:
|
||||
|
||||
Open Source Licensing GPL V2
|
||||
|
||||
This is the appropriate option if you want to share the source code of your
|
||||
application with everyone you distribute it to, and you also want to give them
|
||||
the right to share who uses it. If you wish to use this software under Open
|
||||
Source Licensing, you must contribute all your source code to the open source
|
||||
community in accordance with the GPL Version 2 when your application is
|
||||
distributed. See http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
Commercial Licensing
|
||||
|
||||
This is the appropriate option if you are creating proprietary applications
|
||||
and you are not prepared to distribute and share the source code of your
|
||||
application. Contact info@open.com.au for details.
|
||||
@@ -24,13 +24,14 @@ template <class T> class MemoryPool
|
||||
buf = new T[maxElements];
|
||||
|
||||
// prefill dead
|
||||
for (int i = 0; i < maxElements; i++)
|
||||
for (size_t i = 0; i < maxElements; i++)
|
||||
release(&buf[i]);
|
||||
}
|
||||
|
||||
~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);
|
||||
@@ -40,7 +41,7 @@ template <class T> class MemoryPool
|
||||
}
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
|
||||
/// don't want this version)
|
||||
/// don't want this version).
|
||||
T *allocZeroed(TickType_t maxWait)
|
||||
{
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
@@ -65,7 +66,7 @@ template <class T> class MemoryPool
|
||||
{
|
||||
assert(dead.enqueue(p, 0));
|
||||
assert(p >= buf &&
|
||||
(p - buf) <
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ template <class T> class MemoryPool
|
||||
{
|
||||
assert(dead.enqueueFromISR(p, higherPriWoken));
|
||||
assert(p >= buf &&
|
||||
(p - buf) <
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user