mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-31 23:21:06 +00:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7118200885 | ||
|
|
b7f9064f0d | ||
|
|
5dc5bce1b2 | ||
|
|
bc7fef1d1a | ||
|
|
1908d131ca | ||
|
|
8cd2a00a25 | ||
|
|
c097852ab0 | ||
|
|
b02212009a | ||
|
|
9d1971f0fa | ||
|
|
2d6261703a | ||
|
|
a97c2ae6eb | ||
|
|
76e2c39c63 | ||
|
|
ab9fe42f58 | ||
|
|
959b540c02 | ||
|
|
68781492ad | ||
|
|
590e147186 | ||
|
|
0b358674ff | ||
|
|
0df01f2586 | ||
|
|
ca23665463 | ||
|
|
f55ac8e9c9 | ||
|
|
6e37fe6343 | ||
|
|
217bd934d7 | ||
|
|
58715f454c | ||
|
|
772f2a15ff | ||
|
|
5b0d8381b9 | ||
|
|
d841d86bbc | ||
|
|
ecaae87b79 | ||
|
|
5835abbcf6 | ||
|
|
2f7c2a2aea | ||
|
|
87ec7b09aa | ||
|
|
f8ec072093 | ||
|
|
781d2f0ad6 | ||
|
|
7bbd2c0e80 | ||
|
|
315cfe4f2d | ||
|
|
707ed75138 | ||
|
|
c0e180759d | ||
|
|
6ceb423033 | ||
|
|
96c4286e7d | ||
|
|
f320ecbde8 | ||
|
|
d014ae0bff | ||
|
|
12a7934ca1 | ||
|
|
64bc791e48 | ||
|
|
1f33506962 | ||
|
|
ba9a94d026 | ||
|
|
6f13966d19 | ||
|
|
96cfad4e57 | ||
|
|
a26ebb1b69 | ||
|
|
7a764efc10 | ||
|
|
49b1f4c5af | ||
|
|
fbe56531d2 | ||
|
|
aa6b29a4b5 | ||
|
|
c88b9732eb | ||
|
|
2c29e8b179 | ||
|
|
d2d6b8e12f | ||
|
|
badfaa8545 | ||
|
|
c5b67d821d | ||
|
|
63bf7a29f3 | ||
|
|
845dd1f9e3 | ||
|
|
c9c44a934d | ||
|
|
2f6981a27f | ||
|
|
8739469db3 | ||
|
|
0c0c0babba | ||
|
|
950b32232f | ||
|
|
2f6034b067 | ||
|
|
159f7622e4 | ||
|
|
2cc2fa906a | ||
|
|
1b71a0f436 | ||
|
|
2af9e1431e | ||
|
|
ee961d01ed | ||
|
|
a3343bc1af | ||
|
|
ee04d57a7f | ||
|
|
2cf704abe0 | ||
|
|
ef32ac5cd4 | ||
|
|
52d85c9a41 |
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@@ -7,11 +7,12 @@ jobs:
|
|||||||
setup:
|
setup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- name: Checkout code
|
||||||
- name: Checkout submodules
|
uses: actions/checkout@v2
|
||||||
uses: textbook/git-checkout-submodule-action@master
|
with:
|
||||||
|
submodules: true
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@master
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
- name: Install Platform IO
|
- name: Install Platform IO
|
||||||
@@ -30,5 +31,6 @@ jobs:
|
|||||||
run: platformio run -e heltec
|
run: platformio run -e heltec
|
||||||
- name: Build for lora-relay-v1
|
- name: Build for lora-relay-v1
|
||||||
run: platformio run -e lora-relay-v1
|
run: platformio run -e lora-relay-v1
|
||||||
- name: Build for linux
|
# Turn off linux for now
|
||||||
run: platformio run -e linux
|
#- name: Build for linux
|
||||||
|
# run: platformio run -e linux
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
|
|||||||
|
|
||||||
nrfjprog --eraseall -f nrf52
|
nrfjprog --eraseall -f nrf52
|
||||||
|
|
||||||
|
# to get tool run "sudo apt-get install srecord"
|
||||||
|
|
||||||
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
|
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
|
||||||
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
|
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
|
||||||
# first 4 bytes should be 0x01 to indicate valid app image
|
# first 4 bytes should be 0x01 to indicate valid app image
|
||||||
@@ -14,7 +16,7 @@ echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
|
|||||||
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
|
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
|
||||||
|
|
||||||
echo Generating merged hex file
|
echo Generating merged hex file
|
||||||
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-125-gf38f8f4-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-213-gf67f592-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
||||||
|
|
||||||
echo Telling bootloader app region is valid and telling CPU to run
|
echo Telling bootloader app region is valid and telling CPU to run
|
||||||
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
|
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
|
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA -SuppressInfoUpdateFW -DisableAutoUpdateFW -rtos GDBServer/RTOSPlugin_FreeRTOS
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ You probably don't care about this section - skip to the next one.
|
|||||||
|
|
||||||
## 1.2 cleanup & multichannel support:
|
## 1.2 cleanup & multichannel support:
|
||||||
|
|
||||||
|
* cdcacm bug on nrf52: emittx thinks it emitted but client sees nothing. works again later
|
||||||
|
* nrf52: segger logs have errors in formatting that should be impossible (because not going through serial, try stalling on segger)
|
||||||
* DONE call RouterPlugin for *all* packets - not just Router packets
|
* DONE call RouterPlugin for *all* packets - not just Router packets
|
||||||
* DONE generate channel hash from the name of the channel+the psk (not just one or the other)
|
* DONE generate channel hash from the name of the channel+the psk (not just one or the other)
|
||||||
* DONE send a hint that can be used to select which channel to try and hash against with each message
|
* DONE send a hint that can be used to select which channel to try and hash against with each message
|
||||||
@@ -21,32 +23,44 @@ You probably don't care about this section - skip to the next one.
|
|||||||
* DONE implement 'get channels' Admin plugin operation
|
* DONE implement 'get channels' Admin plugin operation
|
||||||
* DONE use get-channels from python
|
* DONE use get-channels from python
|
||||||
* DONE use get channels & get settings from android
|
* DONE use get channels & get settings from android
|
||||||
* use set-channel from python
|
* DONE use set-channel from python
|
||||||
* DONE make settings changes from python work
|
* DONE make settings changes from python work
|
||||||
* DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
* DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
||||||
* DONE add check for old devices with new API library
|
* DONE add check for old devices with new API library
|
||||||
* DONE release python api
|
* DONE release python api
|
||||||
* DONE release protobufs
|
* DONE release protobufs
|
||||||
* DONE release to developers
|
* DONE release to developers
|
||||||
|
* DONE fix setch-fast in python tool
|
||||||
|
* turn off fault 8: https://github.com/meshtastic/Meshtastic-device/issues/734
|
||||||
|
* age out pendingrequests in the python API
|
||||||
|
* DONE stress test channel download from python, sometimes it seems like we don't get all replies, bug was due to simultaneous android connection
|
||||||
|
* DONE combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
|
||||||
|
* DONE don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?)
|
||||||
* fix 1.1.50 android debug panel display
|
* fix 1.1.50 android debug panel display
|
||||||
* warn in android app about unset regions
|
* DONE test android channel setting
|
||||||
* use set-channel from android
|
* DONE release to users
|
||||||
|
* DONE warn in android app about unset regions
|
||||||
|
* DONE use set-channel from android
|
||||||
* DONE add gui in android app for setting region
|
* DONE add gui in android app for setting region
|
||||||
* stress test channel download from python, sometimes it seems like we don't get all replies
|
* DONE clean up python channel usage
|
||||||
* investigate @mc-hamster report of heap corruption
|
* DONE use bindToChannel to limit admin access for remote nodes
|
||||||
* DONE use set-user from android
|
* DONE move channels and radio config out of device settings
|
||||||
* combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
|
* test remote info and remote settings changes
|
||||||
* don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?)
|
|
||||||
* use portuino TCP connection to debug with python API
|
|
||||||
* make python tests more exhaustive
|
* make python tests more exhaustive
|
||||||
* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered)
|
* pick default random admin key
|
||||||
* stress test multi channel
|
* exclude admin channels from URL?
|
||||||
|
* make a way to share just secondary channels via URL
|
||||||
* use single byte 'well known' channel names for the four default channel names (longslow etc), and for admin, gpio, etc...
|
* use single byte 'well known' channel names for the four default channel names (longslow etc), and for admin, gpio, etc...
|
||||||
* use presence of gpio channel to enable gpio ops, same for serial etc...
|
* use presence of gpio channel to enable gpio ops, same for serial etc...
|
||||||
* pick default random admin key
|
|
||||||
* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
|
||||||
* add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
|
|
||||||
* restrict gpio & serial & settings operations to the admin channel (unless local to the current node)
|
* restrict gpio & serial & settings operations to the admin channel (unless local to the current node)
|
||||||
|
* add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
|
||||||
|
* stress test multi channel
|
||||||
|
* investigate @mc-hamster report of heap corruption
|
||||||
|
* DONE use set-user from android
|
||||||
|
* generalize the concept of "shortstrings" use it for both PSKs and well known channel names. Possibly use a ShortString class.
|
||||||
|
* use portuino TCP connection to debug with python API
|
||||||
|
* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered)
|
||||||
|
* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
||||||
* DONE warn in python api if we are too new to talk to the device code
|
* DONE warn in python api if we are too new to talk to the device code
|
||||||
* DONE make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning.
|
* DONE make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning.
|
||||||
* DONE "FIXME - move the radioconfig/user/channel READ operations into SettingsMessage as well"
|
* DONE "FIXME - move the radioconfig/user/channel READ operations into SettingsMessage as well"
|
||||||
@@ -58,6 +72,7 @@ You probably don't care about this section - skip to the next one.
|
|||||||
* confirm we are still calling the plugins for messages inbound from the phone (or generated locally)
|
* confirm we are still calling the plugins for messages inbound from the phone (or generated locally)
|
||||||
* confirm we are still multi hop routing flood broadcasts
|
* confirm we are still multi hop routing flood broadcasts
|
||||||
* confirm we are still doing resends on unicast reliable packets
|
* confirm we are still doing resends on unicast reliable packets
|
||||||
|
* add history to routed packets: https://meshtastic.discourse.group/t/packet-source-tracking/2764/2
|
||||||
* add support for full DSR unicast delivery
|
* add support for full DSR unicast delivery
|
||||||
* DONE move acks into routing
|
* DONE move acks into routing
|
||||||
* DONE make all subpackets different versions of data
|
* DONE make all subpackets different versions of data
|
||||||
@@ -72,21 +87,38 @@ You probably don't care about this section - skip to the next one.
|
|||||||
* DONE move setCrypto call into packet send and packet decode code
|
* DONE move setCrypto call into packet send and packet decode code
|
||||||
* implement 'small location diffs' change
|
* implement 'small location diffs' change
|
||||||
* move battery level out of position?
|
* move battery level out of position?
|
||||||
|
* consider "A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
|
||||||
|
are allowed on any channel (this lets the local user do anything)." Probably by adding a "secure_local_interface" settings bool.
|
||||||
* DOUBLE CHECK android app can still upgrade 1.1 and 1.0 loads
|
* DOUBLE CHECK android app can still upgrade 1.1 and 1.0 loads
|
||||||
|
|
||||||
eink:
|
eink:
|
||||||
|
|
||||||
|
* DONE check email of reported issues
|
||||||
|
* DONE turn off vbus driving (in bootloader)
|
||||||
* new battery level sensing
|
* new battery level sensing
|
||||||
* measure current draw
|
* current draw no good
|
||||||
* DONE: fix backlight
|
* DONE: fix backlight
|
||||||
* USB is busted because of power enable mode?
|
* DONE - USB is busted because of power enable mode?
|
||||||
|
* test CPU voltage? something is bad with RAM (removing eink module does not help)
|
||||||
|
* test that board leaves bootloader always
|
||||||
|
* test USB - works in bootloader
|
||||||
|
* test LEDs
|
||||||
|
* Test BME280
|
||||||
|
* test gps
|
||||||
|
* check GPS fast locking
|
||||||
|
* tested! dlora
|
||||||
|
* test eink backlight
|
||||||
|
* tested! eink
|
||||||
|
* test buttons
|
||||||
|
* test battery charging
|
||||||
|
* test serial flash
|
||||||
|
* send updated app and bootloader image
|
||||||
* OHH BME280! THAT IS GREAT!
|
* OHH BME280! THAT IS GREAT!
|
||||||
* make new screen work, ask for datasheet
|
* make new screen work, ask for datasheet
|
||||||
* say I think you could ship this
|
* say I think you could ship this
|
||||||
* leds seem busted
|
* leds seem busted
|
||||||
* usb doesn't stay connected
|
* fix hw_model: "nrf52unknown"
|
||||||
* check GPS works
|
* use larger icon for meshtastic logo
|
||||||
* check GPS fast locking
|
|
||||||
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
|
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
|
||||||
* send PR for bootloader
|
* send PR for bootloader
|
||||||
* fix nrf52 time/date
|
* fix nrf52 time/date
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ Recommended settings for a sender at different radio settings:
|
|||||||
Medium ... range_test_plugin_sender = 15
|
Medium ... range_test_plugin_sender = 15
|
||||||
Short Fast ... range_test_plugin_sender = 15
|
Short Fast ... range_test_plugin_sender = 15
|
||||||
|
|
||||||
|
## Python API Examples
|
||||||
|
|
||||||
|
### Sender
|
||||||
|
|
||||||
|
meshtastic --set range_test_plugin_enabled 1
|
||||||
|
meshtastic --set range_test_plugin_sender 60
|
||||||
|
|
||||||
|
### Receiver
|
||||||
|
|
||||||
|
meshtastic --set range_test_plugin_enabled 1
|
||||||
|
meshtastic --set range_test_plugin_save 1
|
||||||
|
|
||||||
## Other things to keep in mind
|
## Other things to keep in mind
|
||||||
|
|
||||||
Be sure to turn off either the plugin configured as a sender or the device where the plugin setup as sender when not in use. This will use a lot of time on air and will spam your channel.
|
Be sure to turn off either the plugin configured as a sender or the device where the plugin setup as sender when not in use. This will use a lot of time on air and will spam your channel.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ default_envs = tbeam
|
|||||||
;default_envs = tlora-v2
|
;default_envs = tlora-v2
|
||||||
;default_envs = lora-relay-v1 # nrf board
|
;default_envs = lora-relay-v1 # nrf board
|
||||||
;default_envs = eink
|
;default_envs = eink
|
||||||
|
;default_envs = nrf52840dk-geeksville
|
||||||
;default_envs = linux # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
;default_envs = linux # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
@@ -33,6 +34,8 @@ default_envs = tbeam
|
|||||||
extra_scripts = bin/platformio-custom.py
|
extra_scripts = bin/platformio-custom.py
|
||||||
|
|
||||||
; note: we add src to our include search path so that lmic_project_config can override
|
; note: we add src to our include search path so that lmic_project_config can override
|
||||||
|
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
|
||||||
|
; of code is a heap corruption bug!
|
||||||
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
|
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
|
||||||
build_flags = -Wno-missing-field-initializers
|
build_flags = -Wno-missing-field-initializers
|
||||||
-Wno-format
|
-Wno-format
|
||||||
@@ -40,14 +43,7 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DHW_VERSION_${sysenv.COUNTRY}
|
-DHW_VERSION_${sysenv.COUNTRY}
|
||||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||||
-DUSE_THREAD_NAMES
|
-DUSE_THREAD_NAMES
|
||||||
-DTINYGPSPLUS_OPTION_NO_CUSTOM_FIELDS
|
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||||
|
|
||||||
; leave this commented out to avoid breaking Windows
|
|
||||||
;upload_port = /dev/ttyUSB0
|
|
||||||
;monitor_port = /dev/ttyUSB0
|
|
||||||
|
|
||||||
;upload_port = /dev/cu.SLAB_USBtoUART
|
|
||||||
;monitor_port = /dev/cu.SLAB_USBtoUART
|
|
||||||
|
|
||||||
; the default is esptool
|
; the default is esptool
|
||||||
; upload_protocol = esp-prog
|
; upload_protocol = esp-prog
|
||||||
@@ -71,10 +67,10 @@ lib_deps =
|
|||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
|
||||||
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
||||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||||
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
https://github.com/meshtastic/arduino-fsm.git#55c47b6cded91645aff05a27b6e5821d8d0f64be
|
||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||||
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
|
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
|
||||||
https://github.com/meshtastic/TinyGPSPlus.git#9c1d584d2469523381e077b0b9c1bf868d6c0206
|
https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79
|
||||||
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||||
SPI
|
SPI
|
||||||
@@ -115,6 +111,13 @@ lib_ignore = segger_rtt
|
|||||||
platform_packages =
|
platform_packages =
|
||||||
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
|
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
|
||||||
|
|
||||||
|
; leave this commented out to avoid breaking Windows
|
||||||
|
upload_port = /dev/ttyUSB0
|
||||||
|
;monitor_port = /dev/ttyUSB0
|
||||||
|
|
||||||
|
;upload_port = /dev/cu.SLAB_USBtoUART
|
||||||
|
;monitor_port = /dev/cu.SLAB_USBtoUART
|
||||||
|
|
||||||
; customize the partition table
|
; customize the partition table
|
||||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||||
board_build.partitions = partition-table.csv
|
board_build.partitions = partition-table.csv
|
||||||
@@ -184,7 +187,6 @@ src_filter =
|
|||||||
; platform = nordicnrf52
|
; platform = nordicnrf52
|
||||||
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
|
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
|
||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
debug_tool = jlink
|
|
||||||
build_type = debug ; I'm debugging with ICE a lot now
|
build_type = debug ; I'm debugging with ICE a lot now
|
||||||
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||||
build_flags =
|
build_flags =
|
||||||
@@ -198,6 +200,27 @@ lib_ignore =
|
|||||||
BluetoothOTA
|
BluetoothOTA
|
||||||
monitor_port = /dev/ttyACM1
|
monitor_port = /dev/ttyACM1
|
||||||
|
|
||||||
|
# we pass in options to jlink so it can understand freertos (note: we don't use "jlink" as the tool)
|
||||||
|
debug_tool = jlink
|
||||||
|
debug_port = :2331
|
||||||
|
# Note: the ARGUMENTS MUST BE on multiple lines. Otherwise platformio/commands/debug/helpers.py misparses everything into the "executable"
|
||||||
|
# attribute and leaves "arguments" empty
|
||||||
|
# /home/kevinh/.platformio/packages/tool-jlink/JLinkGDBServerCLExe
|
||||||
|
# This doesn't work yet, so not using for now
|
||||||
|
disabled_debug_server =
|
||||||
|
/usr/bin/JLinkGDBServerCLExe
|
||||||
|
-singlerun
|
||||||
|
-if
|
||||||
|
SWD
|
||||||
|
-select
|
||||||
|
USB
|
||||||
|
-device
|
||||||
|
nRF52840_xxAA
|
||||||
|
-port
|
||||||
|
2331
|
||||||
|
-rtos
|
||||||
|
GDBServer/RTOSPlugin_FreeRTOS
|
||||||
|
|
||||||
debug_extra_cmds =
|
debug_extra_cmds =
|
||||||
source gdbinit
|
source gdbinit
|
||||||
|
|
||||||
|
|||||||
2
proto
2
proto
Submodule proto updated: 94bd0aae44...e63f9713f7
@@ -178,10 +178,11 @@ void PowerFSM_setup()
|
|||||||
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
||||||
|
|
||||||
/* To determine if we're externally powered, assumptions
|
/* To determine if we're externally powered, assumptions
|
||||||
1) If we're powered up and there's no battery, we must be getting power externally.
|
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise)
|
||||||
2) If we detect USB power from the power management chip, we must be getting power externally.
|
|
||||||
|
2) If we detect USB power from the power management chip, we must be getting power externally.
|
||||||
*/
|
*/
|
||||||
bool hasPower = (powerStatus && !powerStatus->getHasBattery()) || (!isLowPower && powerStatus && powerStatus->getHasUSB());
|
bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
|
||||||
|
|
||||||
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "RedirectablePrint.h"
|
#include "RedirectablePrint.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "concurrency/OSThread.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A printer that doesn't go anywhere
|
* A printer that doesn't go anywhere
|
||||||
@@ -20,7 +20,7 @@ size_t RedirectablePrint::write(uint8_t c)
|
|||||||
{
|
{
|
||||||
// Always send the characters to our segger JTAG debugger
|
// Always send the characters to our segger JTAG debugger
|
||||||
#ifdef SEGGER_STDOUT_CH
|
#ifdef SEGGER_STDOUT_CH
|
||||||
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
|
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dest->write(c);
|
dest->write(c);
|
||||||
@@ -38,7 +38,7 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
|||||||
va_end(arg);
|
va_end(arg);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
if (len >= printBufLen) {
|
if (len >= (int)printBufLen) {
|
||||||
delete[] printBuf;
|
delete[] printBuf;
|
||||||
printBufLen *= 2;
|
printBufLen *= 2;
|
||||||
printBuf = new char[printBufLen];
|
printBuf = new char[printBufLen];
|
||||||
@@ -55,45 +55,52 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
|||||||
|
|
||||||
size_t RedirectablePrint::logDebug(const char *format, ...)
|
size_t RedirectablePrint::logDebug(const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list arg;
|
|
||||||
va_start(arg, format);
|
|
||||||
|
|
||||||
// Cope with 0 len format strings, but look for new line terminator
|
|
||||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
|
||||||
|
|
||||||
size_t r = 0;
|
size_t r = 0;
|
||||||
|
|
||||||
// If we are the first message on a report, include the header
|
if (!inDebugPrint) {
|
||||||
if (!isContinuationMessage) {
|
inDebugPrint = true;
|
||||||
struct timeval tv;
|
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
|
||||||
long hms = tv.tv_sec % SEC_PER_DAY;
|
|
||||||
//hms += tz.tz_dsttime * SEC_PER_HOUR;
|
|
||||||
//hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
|
||||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
|
||||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
|
||||||
|
|
||||||
// Tear apart hms into h:m:s
|
va_list arg;
|
||||||
int hour = hms / SEC_PER_HOUR;
|
va_start(arg, format);
|
||||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
|
||||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
|
||||||
|
|
||||||
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
// Cope with 0 len format strings, but look for new line terminator
|
||||||
} else
|
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||||
r += printf("??:??:?? %u ", millis() / 1000);
|
|
||||||
|
|
||||||
auto thread = concurrency::OSThread::currentThread;
|
// If we are the first message on a report, include the header
|
||||||
if(thread) {
|
if (!isContinuationMessage) {
|
||||||
print("[");
|
struct timeval tv;
|
||||||
print(thread->ThreadName);
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
print("] ");
|
long hms = tv.tv_sec % SEC_PER_DAY;
|
||||||
|
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||||
|
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||||
|
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||||
|
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||||
|
|
||||||
|
// Tear apart hms into h:m:s
|
||||||
|
int hour = hms / SEC_PER_HOUR;
|
||||||
|
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||||
|
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||||
|
|
||||||
|
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
||||||
|
} else
|
||||||
|
r += printf("??:??:?? %u ", millis() / 1000);
|
||||||
|
|
||||||
|
auto thread = concurrency::OSThread::currentThread;
|
||||||
|
if (thread) {
|
||||||
|
print("[");
|
||||||
|
// printf("%p ", thread);
|
||||||
|
// assert(thread->ThreadName.length());
|
||||||
|
print(thread->ThreadName);
|
||||||
|
print("] ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r += vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
isContinuationMessage = !hasNewline;
|
||||||
|
inDebugPrint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r += vprintf(format, arg);
|
|
||||||
va_end(arg);
|
|
||||||
|
|
||||||
isContinuationMessage = !hasNewline;
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,8 @@ class RedirectablePrint : public Print
|
|||||||
/// Used to allow multiple logDebug messages to appear on a single log line
|
/// Used to allow multiple logDebug messages to appear on a single log line
|
||||||
bool isContinuationMessage = false;
|
bool isContinuationMessage = false;
|
||||||
|
|
||||||
|
volatile bool inDebugPrint = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
#include "SerialConsole.h"
|
#include "SerialConsole.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "NodeDB.h"
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#define Port Serial
|
#define Port Serial
|
||||||
|
|
||||||
SerialConsole console;
|
SerialConsole console;
|
||||||
|
|
||||||
|
void consolePrintf(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
console.vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
|
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
|
||||||
{
|
{
|
||||||
canWrite = false; // We don't send packets to our port until it has talked to us first
|
canWrite = false; // We don't send packets to our port until it has talked to us first
|
||||||
@@ -29,7 +37,7 @@ void SerialConsole::init()
|
|||||||
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||||
{
|
{
|
||||||
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
|
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
|
||||||
if(!radioConfig.preferences.debug_log_enabled)
|
if (!radioConfig.preferences.debug_log_enabled)
|
||||||
setDestination(&noopPrint);
|
setDestination(&noopPrint);
|
||||||
canWrite = true;
|
canWrite = true;
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
|
|||||||
virtual void onConnectionChanged(bool connected);
|
virtual void onConnectionChanged(bool connected);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A simple wrapper to allow non class aware code write to the console
|
||||||
|
void consolePrintf(const char *format, ...);
|
||||||
|
|
||||||
extern SerialConsole console;
|
extern SerialConsole console;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
#ifdef HAS_FREE_RTOS
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ namespace concurrency
|
|||||||
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
|
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
|
||||||
{
|
{
|
||||||
semaphore = xSemaphoreCreateBinary();
|
semaphore = xSemaphoreCreateBinary();
|
||||||
|
assert(semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class ESP32CryptoEngine : public CryptoEngine
|
|||||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||||
size_t nc_off = 0;
|
size_t nc_off = 0;
|
||||||
|
|
||||||
// DEBUG_MSG("ESP32 encrypt!\n");
|
// DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetNum, numBytes);
|
||||||
initNonce(fromNode, packetNum);
|
initNonce(fromNode, packetNum);
|
||||||
assert(numBytes <= MAX_BLOCKSIZE);
|
assert(numBytes <= MAX_BLOCKSIZE);
|
||||||
memcpy(scratch, bytes, numBytes);
|
memcpy(scratch, bytes, numBytes);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ uint8_t GPS::i2cAddress = 0;
|
|||||||
|
|
||||||
GPS *gps;
|
GPS *gps;
|
||||||
|
|
||||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||||
/// only init that port once.
|
/// only init that port once.
|
||||||
static bool didSerialInit;
|
static bool didSerialInit;
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ bool GPS::setupGPS()
|
|||||||
{
|
{
|
||||||
if (_serial_gps && !didSerialInit) {
|
if (_serial_gps && !didSerialInit) {
|
||||||
didSerialInit = true;
|
didSerialInit = true;
|
||||||
|
|
||||||
#ifdef GPS_RX_PIN
|
#ifdef GPS_RX_PIN
|
||||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||||
#else
|
#else
|
||||||
@@ -73,6 +73,13 @@ bool GPS::setup()
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GPS::~GPS()
|
||||||
|
{
|
||||||
|
// we really should unregister our sleep observer
|
||||||
|
notifySleepObserver.unobserve();
|
||||||
|
notifyDeepSleepObserver.unobserve();
|
||||||
|
}
|
||||||
|
|
||||||
// Allow defining the polarity of the WAKE output. default is active high
|
// Allow defining the polarity of the WAKE output. default is active high
|
||||||
#ifndef GPS_WAKE_ACTIVE
|
#ifndef GPS_WAKE_ACTIVE
|
||||||
#define GPS_WAKE_ACTIVE 1
|
#define GPS_WAKE_ACTIVE 1
|
||||||
@@ -86,8 +93,8 @@ void GPS::wake()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPS::sleep()
|
||||||
void GPS::sleep() {
|
{
|
||||||
#ifdef PIN_GPS_WAKE
|
#ifdef PIN_GPS_WAKE
|
||||||
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
|
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
|
||||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||||
@@ -158,7 +165,8 @@ uint32_t GPS::getWakeTime() const
|
|||||||
return t; // already maxint
|
return t; // already maxint
|
||||||
|
|
||||||
if (t == 0)
|
if (t == 0)
|
||||||
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much less if we can find sats) or less if a router
|
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much
|
||||||
|
// less if we can find sats) or less if a router
|
||||||
|
|
||||||
t *= 1000; // msecs
|
t *= 1000; // msecs
|
||||||
|
|
||||||
@@ -179,8 +187,8 @@ uint32_t GPS::getSleepTime() const
|
|||||||
if (t == UINT32_MAX)
|
if (t == UINT32_MAX)
|
||||||
return t; // already maxint
|
return t; // already maxint
|
||||||
|
|
||||||
if (t == 0) // default - unset in preferences
|
if (t == 0) // default - unset in preferences
|
||||||
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
||||||
|
|
||||||
t *= 1000;
|
t *= 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class GPS : private concurrency::OSThread
|
|||||||
|
|
||||||
GPS() : concurrency::OSThread("GPS") {}
|
GPS() : concurrency::OSThread("GPS") {}
|
||||||
|
|
||||||
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
|
virtual ~GPS();
|
||||||
|
|
||||||
/** We will notify this observable anytime GPS state has changed meaningfully */
|
/** We will notify this observable anytime GPS state has changed meaningfully */
|
||||||
Observable<const meshtastic::GPSStatus *> newStatus;
|
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||||
|
|||||||
@@ -143,11 +143,13 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
drawIconScreen(region, display, state, x, y);
|
drawIconScreen(region, display, state, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_EINK
|
||||||
/// Used on eink displays while in deep sleep
|
/// Used on eink displays while in deep sleep
|
||||||
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
drawIconScreen("Sleeping...", display, state, x, y);
|
drawIconScreen("Sleeping...", display, state, x, y);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
@@ -229,7 +231,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
displayedNodeNum = 0; // Not currently showing a node pane
|
displayedNodeNum = 0; // Not currently showing a node pane
|
||||||
|
|
||||||
MeshPacket &mp = devicestate.rx_text_message;
|
MeshPacket &mp = devicestate.rx_text_message;
|
||||||
NodeInfo *node = nodeDB.getNode(mp.from);
|
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||||
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
|
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
|
||||||
// mp.decoded.variant.data.decoded.bytes);
|
// mp.decoded.variant.data.decoded.bytes);
|
||||||
|
|
||||||
@@ -791,7 +793,8 @@ void Screen::setup()
|
|||||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||||
textMessageObserver.observe(textMessagePlugin);
|
if(textMessagePlugin)
|
||||||
|
textMessageObserver.observe(textMessagePlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::forceDisplay()
|
void Screen::forceDisplay()
|
||||||
@@ -849,7 +852,7 @@ int32_t Screen::runOnce()
|
|||||||
free(cmd.print_text);
|
free(cmd.print_text);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DEBUG_MSG("BUG: invalid cmd");
|
DEBUG_MSG("BUG: invalid cmd\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/main.cpp
11
src/main.cpp
@@ -279,7 +279,13 @@ void setup()
|
|||||||
concurrency::hasBeenSetup = true;
|
concurrency::hasBeenSetup = true;
|
||||||
|
|
||||||
#ifdef SEGGER_STDOUT_CH
|
#ifdef SEGGER_STDOUT_CH
|
||||||
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM;
|
||||||
|
#ifdef NRF52840_XXAA
|
||||||
|
auto buflen = 4096; // this board has a fair amount of ram
|
||||||
|
#else
|
||||||
|
auto buflen = 256; // this board has a fair amount of ram
|
||||||
|
#endif
|
||||||
|
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, buflen, mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SEGGER
|
#ifdef USE_SEGGER
|
||||||
@@ -583,6 +589,9 @@ void loop()
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
esp32Loop();
|
esp32Loop();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef NRF52_SERIES
|
||||||
|
nrf52Loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
||||||
|
|||||||
318
src/memtest.cpp
Normal file
318
src/memtest.cpp
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* mtest - Perform a memory test
|
||||||
|
*
|
||||||
|
* (C) Copyright 2000
|
||||||
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||||
|
*
|
||||||
|
* See file CREDITS for list of people who contributed to this
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||||
|
* MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform a memory test. A more complete alternative test can be
|
||||||
|
* configured using CONFIG_CMD_MTEST_ALTERNATIVE. The complete test
|
||||||
|
* loops until interrupted by ctrl-c or by a failure of one of the
|
||||||
|
* sub-tests.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_CMD_MTEST_ALTERNATIVE
|
||||||
|
static int mem_test(uint32_t _start, uint32_t _end, uint32_t pattern_unused)
|
||||||
|
{
|
||||||
|
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||||
|
volatile uint32_t *end = (volatile uint32_t *)_end;
|
||||||
|
volatile uint32_t *addr;
|
||||||
|
uint32_t val;
|
||||||
|
uint32_t readback;
|
||||||
|
vu_long addr_mask;
|
||||||
|
vu_long offset;
|
||||||
|
vu_long test_offset;
|
||||||
|
vu_long pattern;
|
||||||
|
vu_long temp;
|
||||||
|
vu_long anti_pattern;
|
||||||
|
vu_long num_words;
|
||||||
|
#ifdef CFG_MEMTEST_SCRATCH
|
||||||
|
volatile uint32_t *dummy = (vu_long *)CFG_MEMTEST_SCRATCH;
|
||||||
|
#else
|
||||||
|
volatile uint32_t *dummy = start;
|
||||||
|
#endif
|
||||||
|
int j;
|
||||||
|
int iterations = 1;
|
||||||
|
static const uint32_t bitpattern[] = {
|
||||||
|
0x00000001, /* single bit */
|
||||||
|
0x00000003, /* two adjacent bits */
|
||||||
|
0x00000007, /* three adjacent bits */
|
||||||
|
0x0000000F, /* four adjacent bits */
|
||||||
|
0x00000005, /* two non-adjacent bits */
|
||||||
|
0x00000015, /* three non-adjacent bits */
|
||||||
|
0x00000055, /* four non-adjacent bits */
|
||||||
|
0xaaaaaaaa, /* alternating 1/0 */
|
||||||
|
};
|
||||||
|
/* XXX: enforce alignment of start and end? */
|
||||||
|
for (;;) {
|
||||||
|
if (ctrlc()) {
|
||||||
|
putchar('\n');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Iteration: %6d\r", iterations);
|
||||||
|
iterations++;
|
||||||
|
/*
|
||||||
|
* Data line test: write a pattern to the first
|
||||||
|
* location, write the 1's complement to a 'parking'
|
||||||
|
* address (changes the state of the data bus so a
|
||||||
|
* floating bus doen't give a false OK), and then
|
||||||
|
* read the value back. Note that we read it back
|
||||||
|
* into a variable because the next time we read it,
|
||||||
|
* it might be right (been there, tough to explain to
|
||||||
|
* the quality guys why it prints a failure when the
|
||||||
|
* "is" and "should be" are obviously the same in the
|
||||||
|
* error message).
|
||||||
|
*
|
||||||
|
* Rather than exhaustively testing, we test some
|
||||||
|
* patterns by shifting '1' bits through a field of
|
||||||
|
* '0's and '0' bits through a field of '1's (i.e.
|
||||||
|
* pattern and ~pattern).
|
||||||
|
*/
|
||||||
|
addr = start;
|
||||||
|
/* XXX */
|
||||||
|
if (addr == dummy)
|
||||||
|
++addr;
|
||||||
|
for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) {
|
||||||
|
val = bitpattern[j];
|
||||||
|
for (; val != 0; val <<= 1) {
|
||||||
|
*addr = val;
|
||||||
|
*dummy = ~val; /* clear the test data off of the bus */
|
||||||
|
readback = *addr;
|
||||||
|
if (readback != val) {
|
||||||
|
printf("FAILURE (data line): "
|
||||||
|
"expected 0x%08lx, actual 0x%08lx at address 0x%p\n",
|
||||||
|
val, readback, addr);
|
||||||
|
}
|
||||||
|
*addr = ~val;
|
||||||
|
*dummy = val;
|
||||||
|
readback = *addr;
|
||||||
|
if (readback != ~val) {
|
||||||
|
printf("FAILURE (data line): "
|
||||||
|
"Is 0x%08lx, should be 0x%08lx at address 0x%p\n",
|
||||||
|
readback, ~val, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Based on code whose Original Author and Copyright
|
||||||
|
* information follows: Copyright (c) 1998 by Michael
|
||||||
|
* Barr. This software is placed into the public
|
||||||
|
* domain and may be used for any purpose. However,
|
||||||
|
* this notice must not be changed or removed and no
|
||||||
|
* warranty is either expressed or implied by its
|
||||||
|
* publication or distribution.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Address line test
|
||||||
|
*
|
||||||
|
* Description: Test the address bus wiring in a
|
||||||
|
* memory region by performing a walking
|
||||||
|
* 1's test on the relevant bits of the
|
||||||
|
* address and checking for aliasing.
|
||||||
|
* This test will find single-bit
|
||||||
|
* address failures such as stuck -high,
|
||||||
|
* stuck-low, and shorted pins. The base
|
||||||
|
* address and size of the region are
|
||||||
|
* selected by the caller.
|
||||||
|
*
|
||||||
|
* Notes: For best results, the selected base
|
||||||
|
* address should have enough LSB 0's to
|
||||||
|
* guarantee single address bit changes.
|
||||||
|
* For example, to test a 64-Kbyte
|
||||||
|
* region, select a base address on a
|
||||||
|
* 64-Kbyte boundary. Also, select the
|
||||||
|
* region size as a power-of-two if at
|
||||||
|
* all possible.
|
||||||
|
*
|
||||||
|
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||||
|
*
|
||||||
|
* ## NOTE ## Be sure to specify start and end
|
||||||
|
* addresses such that addr_mask has
|
||||||
|
* lots of bits set. For example an
|
||||||
|
* address range of 01000000 02000000 is
|
||||||
|
* bad while a range of 01000000
|
||||||
|
* 01ffffff is perfect.
|
||||||
|
*/
|
||||||
|
addr_mask = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long);
|
||||||
|
pattern = (vu_long)0xaaaaaaaa;
|
||||||
|
anti_pattern = (vu_long)0x55555555;
|
||||||
|
debug("%s:%d: addr mask = 0x%.8lx\n", __FUNCTION__, __LINE__, addr_mask);
|
||||||
|
/*
|
||||||
|
* Write the default pattern at each of the
|
||||||
|
* power-of-two offsets.
|
||||||
|
*/
|
||||||
|
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1)
|
||||||
|
start[offset] = pattern;
|
||||||
|
/*
|
||||||
|
* Check for address bits stuck high.
|
||||||
|
*/
|
||||||
|
test_offset = 0;
|
||||||
|
start[test_offset] = anti_pattern;
|
||||||
|
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||||
|
temp = start[offset];
|
||||||
|
if (temp != pattern) {
|
||||||
|
printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
|
||||||
|
" expected 0x%.8lx, actual 0x%.8lx\n",
|
||||||
|
(uint32_t)&start[offset], pattern, temp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start[test_offset] = pattern;
|
||||||
|
/*
|
||||||
|
* Check for addr bits stuck low or shorted.
|
||||||
|
*/
|
||||||
|
for (test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1) {
|
||||||
|
start[test_offset] = anti_pattern;
|
||||||
|
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||||
|
temp = start[offset];
|
||||||
|
if ((temp != pattern) && (offset != test_offset)) {
|
||||||
|
printf("\nFAILURE: Address bit stuck low or shorted @"
|
||||||
|
" 0x%.8lx: expected 0x%.8lx, actual 0x%.8lx\n",
|
||||||
|
(uint32_t)&start[offset], pattern, temp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start[test_offset] = pattern;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Description: Test the integrity of a physical
|
||||||
|
* memory device by performing an
|
||||||
|
* increment/decrement test over the
|
||||||
|
* entire region. In the process every
|
||||||
|
* storage bit in the device is tested
|
||||||
|
* as a zero and a one. The base address
|
||||||
|
* and the size of the region are
|
||||||
|
* selected by the caller.
|
||||||
|
*
|
||||||
|
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||||
|
*/
|
||||||
|
num_words = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long) + 1;
|
||||||
|
/*
|
||||||
|
* Fill memory with a known pattern.
|
||||||
|
*/
|
||||||
|
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||||
|
start[offset] = pattern;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check each location and invert it for the second pass.
|
||||||
|
*/
|
||||||
|
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||||
|
temp = start[offset];
|
||||||
|
if (temp != pattern) {
|
||||||
|
printf("\nFAILURE (read/write) @ 0x%.8lx:"
|
||||||
|
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||||
|
(uint32_t)&start[offset], pattern, temp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
anti_pattern = ~pattern;
|
||||||
|
start[offset] = anti_pattern;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check each location for the inverted pattern and zero it.
|
||||||
|
*/
|
||||||
|
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||||
|
anti_pattern = ~pattern;
|
||||||
|
temp = start[offset];
|
||||||
|
if (temp != anti_pattern) {
|
||||||
|
printf("\nFAILURE (read/write): @ 0x%.8lx:"
|
||||||
|
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||||
|
(uint32_t)&start[offset], anti_pattern, temp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
start[offset] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int mem_test(uint32_t *_start, size_t len, bool doRead = true, bool doWrite = true)
|
||||||
|
{
|
||||||
|
volatile uint32_t *addr;
|
||||||
|
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||||
|
volatile uint32_t *end = start + len / sizeof(uint32_t);
|
||||||
|
uint32_t pattern = 0;
|
||||||
|
uint32_t val;
|
||||||
|
uint32_t readback;
|
||||||
|
uint32_t incr;
|
||||||
|
int rcode = 0;
|
||||||
|
incr = 1;
|
||||||
|
|
||||||
|
//DEBUG_MSG("memtest read=%d, write=%d\n", doRead, doWrite);
|
||||||
|
|
||||||
|
if (doWrite) {
|
||||||
|
//DEBUG_MSG("writing\n");
|
||||||
|
for (addr = start, val = pattern; addr < end; addr++) {
|
||||||
|
*addr = val;
|
||||||
|
val += incr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doRead) {
|
||||||
|
//DEBUG_MSG("reading\n");
|
||||||
|
for (addr = start, val = pattern; addr < end; addr++) {
|
||||||
|
readback = *addr;
|
||||||
|
if (readback != val) {
|
||||||
|
DEBUG_MSG("Mem error @ 0x%08X: "
|
||||||
|
"found 0x%08lX, expected 0x%08lX\n",
|
||||||
|
addr, readback, val);
|
||||||
|
rcode++;
|
||||||
|
}
|
||||||
|
val += incr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/*
|
||||||
|
* Flip the pattern each time to make lots of zeros and
|
||||||
|
* then, the next time, lots of ones. We decrement
|
||||||
|
* the "negative" patterns and increment the "positive"
|
||||||
|
* patterns to preserve this feature.
|
||||||
|
*/
|
||||||
|
if(pattern & 0x80000000) {
|
||||||
|
pattern = -pattern; /* complement & increment */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern = ~pattern;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return rcode;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TESTBUF_LEN 16384
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void doMemTest()
|
||||||
|
{
|
||||||
|
static uint32_t *testBuf;
|
||||||
|
static int iter;
|
||||||
|
|
||||||
|
if (!testBuf)
|
||||||
|
testBuf = (uint32_t *)malloc(TESTBUF_LEN);
|
||||||
|
|
||||||
|
assert(testBuf);
|
||||||
|
if (mem_test(testBuf, TESTBUF_LEN, iter % 2 == 1, iter % 2 == 0) > 0)
|
||||||
|
assert(0); // FIXME report error better
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ Channels channels;
|
|||||||
uint8_t xorHash(const uint8_t *p, size_t len)
|
uint8_t xorHash(const uint8_t *p, size_t len)
|
||||||
{
|
{
|
||||||
uint8_t code = 0;
|
uint8_t code = 0;
|
||||||
for (int i = 0; i < len; i++)
|
for (size_t i = 0; i < len; i++)
|
||||||
code ^= p[i];
|
code ^= p[i];
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
@@ -161,8 +161,8 @@ int16_t Channels::setCrypto(ChannelIndex chIndex)
|
|||||||
|
|
||||||
void Channels::initDefaults()
|
void Channels::initDefaults()
|
||||||
{
|
{
|
||||||
devicestate.channels_count = MAX_NUM_CHANNELS;
|
channelFile.channels_count = MAX_NUM_CHANNELS;
|
||||||
for (int i = 0; i < devicestate.channels_count; i++)
|
for (int i = 0; i < channelFile.channels_count; i++)
|
||||||
fixupChannel(i);
|
fixupChannel(i);
|
||||||
initDefaultChannel(0);
|
initDefaultChannel(0);
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ void Channels::initDefaults()
|
|||||||
void Channels::onConfigChanged()
|
void Channels::onConfigChanged()
|
||||||
{
|
{
|
||||||
// Make sure the phone hasn't mucked anything up
|
// Make sure the phone hasn't mucked anything up
|
||||||
for (int i = 0; i < devicestate.channels_count; i++) {
|
for (int i = 0; i < channelFile.channels_count; i++) {
|
||||||
Channel &ch = fixupChannel(i);
|
Channel &ch = fixupChannel(i);
|
||||||
|
|
||||||
if (ch.role == Channel_Role_PRIMARY)
|
if (ch.role == Channel_Role_PRIMARY)
|
||||||
@@ -180,8 +180,8 @@ void Channels::onConfigChanged()
|
|||||||
|
|
||||||
Channel &Channels::getByIndex(ChannelIndex chIndex)
|
Channel &Channels::getByIndex(ChannelIndex chIndex)
|
||||||
{
|
{
|
||||||
assert(chIndex < devicestate.channels_count);
|
assert(chIndex < channelFile.channels_count);
|
||||||
Channel *ch = devicestate.channels + chIndex;
|
Channel *ch = channelFile.channels + chIndex;
|
||||||
return *ch;
|
return *ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +192,8 @@ void Channels::setChannel(const Channel &c)
|
|||||||
// if this is the new primary, demote any existing roles
|
// if this is the new primary, demote any existing roles
|
||||||
if (c.role == Channel_Role_PRIMARY)
|
if (c.role == Channel_Role_PRIMARY)
|
||||||
for (int i = 0; i < getNumChannels(); i++)
|
for (int i = 0; i < getNumChannels(); i++)
|
||||||
if (devicestate.channels[i].role == Channel_Role_PRIMARY)
|
if (channelFile.channels[i].role == Channel_Role_PRIMARY)
|
||||||
devicestate.channels[i].role = Channel_Role_SECONDARY;
|
channelFile.channels[i].role = Channel_Role_SECONDARY;
|
||||||
|
|
||||||
old = c; // slam in the new settings/role
|
old = c; // slam in the new settings/role
|
||||||
}
|
}
|
||||||
@@ -275,10 +275,11 @@ const char *Channels::getPrimaryName()
|
|||||||
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
||||||
{
|
{
|
||||||
if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
|
if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
|
||||||
DEBUG_MSG("Skipping channel %d due to invalid hash/index\n", chIndex);
|
// DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), channelHash);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
|
||||||
setCrypto(chIndex);
|
setCrypto(chIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Channels
|
|||||||
/** The index of the primary channel */
|
/** The index of the primary channel */
|
||||||
ChannelIndex getPrimaryIndex() const { return primaryIndex; }
|
ChannelIndex getPrimaryIndex() const { return primaryIndex; }
|
||||||
|
|
||||||
ChannelIndex getNumChannels() { return devicestate.channels_count; }
|
ChannelIndex getNumChannels() { return channelFile.channels_count; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different
|
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
void CryptoEngine::setKey(const CryptoKey &k)
|
void CryptoEngine::setKey(const CryptoKey &k)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Installing AES%d key!\n", k.length * 8);
|
DEBUG_MSG("Installing AES%d key!\n", k.length * 8);
|
||||||
|
/* for(uint8_t i = 0; i < k.length; i++)
|
||||||
|
DEBUG_MSG("%02x ", k.bytes[i]);
|
||||||
|
DEBUG_MSG("\n"); */
|
||||||
|
|
||||||
key = k;
|
key = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
|||||||
// ignore rebroadcasts.
|
// ignore rebroadcasts.
|
||||||
// this will also add records for any ACKs we receive for our messages
|
// this will also add records for any ACKs we receive for our messages
|
||||||
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
|
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
|
||||||
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
|
addRoute(getFrom(p), getFrom(p), 0); // We are adjacent with zero hops
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
|||||||
{
|
{
|
||||||
// If a broadcast, possibly _also_ send copies out into the mesh.
|
// If a broadcast, possibly _also_ send copies out into the mesh.
|
||||||
// (FIXME, do something smarter than naive flooding here)
|
// (FIXME, do something smarter than naive flooding here)
|
||||||
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && p->from != getNodeNum()) {
|
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) {
|
||||||
if (p->id != 0) {
|
if (p->id != 0) {
|
||||||
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||||
|
|
||||||
|
|||||||
@@ -101,25 +101,30 @@ template <class T> class MemoryPool : public Allocator<T>
|
|||||||
/// Return a buffer for use by others
|
/// Return a buffer for use by others
|
||||||
virtual void release(T *p)
|
virtual void release(T *p)
|
||||||
{
|
{
|
||||||
assert(dead.enqueue(p, 0));
|
|
||||||
assert(p >= buf &&
|
assert(p >= buf &&
|
||||||
(size_t)(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
|
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||||
|
assert(dead.enqueue(p, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
#ifdef HAS_FREE_RTOS
|
||||||
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
|
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
|
||||||
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
|
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
|
||||||
{
|
{
|
||||||
assert(dead.enqueueFromISR(p, higherPriWoken));
|
|
||||||
assert(p >= buf &&
|
assert(p >= buf &&
|
||||||
(size_t)(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
|
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||||
|
assert(dead.enqueueFromISR(p, higherPriWoken));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
|
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
|
||||||
/// probably don't want this version).
|
/// probably don't want this version).
|
||||||
virtual T *alloc(TickType_t maxWait) { return dead.dequeuePtr(maxWait); }
|
virtual T *alloc(TickType_t maxWait)
|
||||||
|
{
|
||||||
|
T *p = dead.dequeuePtr(maxWait);
|
||||||
|
assert(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ static PacketId findId;
|
|||||||
|
|
||||||
static bool isMyPacket(MeshPacket *p)
|
static bool isMyPacket(MeshPacket *p)
|
||||||
{
|
{
|
||||||
return p->id == findId && p->from == findFrom;
|
return p->id == findId && getFrom(p) == findFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
#include "MeshPlugin.h"
|
#include "MeshPlugin.h"
|
||||||
#include "NodeDB.h"
|
#include "Channels.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
|
#include "plugins/RoutingPlugin.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
||||||
|
|
||||||
const MeshPacket *MeshPlugin::currentRequest;
|
const MeshPacket *MeshPlugin::currentRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
|
||||||
|
* the RoutingPlugin to avoid sending redundant acks
|
||||||
|
*/
|
||||||
|
MeshPacket *MeshPlugin::currentReply;
|
||||||
|
|
||||||
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
||||||
{
|
{
|
||||||
// Can't trust static initalizer order, so we check each time
|
// Can't trust static initalizer order, so we check each time
|
||||||
if(!plugins)
|
if (!plugins)
|
||||||
plugins = new std::vector<MeshPlugin *>();
|
plugins = new std::vector<MeshPlugin *>();
|
||||||
|
|
||||||
plugins->push_back(this);
|
plugins->push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPlugin::setup() {
|
void MeshPlugin::setup() {}
|
||||||
}
|
|
||||||
|
|
||||||
MeshPlugin::~MeshPlugin()
|
MeshPlugin::~MeshPlugin()
|
||||||
{
|
{
|
||||||
@@ -31,6 +38,8 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
|
|
||||||
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
|
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
|
||||||
|
|
||||||
|
currentReply = NULL; // No reply yet
|
||||||
|
|
||||||
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||||
auto ourNodeNum = nodeDB.getNodeNum();
|
auto ourNodeNum = nodeDB.getNodeNum();
|
||||||
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
||||||
@@ -39,8 +48,15 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
|
|
||||||
pi.currentRequest = ∓
|
pi.currentRequest = ∓
|
||||||
|
|
||||||
// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
|
/// received channel
|
||||||
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
|
auto ch = channels.getByIndex(mp.channel);
|
||||||
|
assert(ch.has_settings);
|
||||||
|
|
||||||
|
/// Is the channel this packet arrived on acceptable? (security check)
|
||||||
|
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
|
||||||
|
|
||||||
|
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
|
||||||
|
bool wantsPacket = rxChannelOk && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
|
||||||
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
|
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
|
||||||
if (wantsPacket) {
|
if (wantsPacket) {
|
||||||
pluginFound = true;
|
pluginFound = true;
|
||||||
@@ -48,15 +64,16 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
bool handled = pi.handleReceived(mp);
|
bool handled = pi.handleReceived(mp);
|
||||||
|
|
||||||
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious sniffing)
|
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious sniffing)
|
||||||
|
// also: we only let the one plugin send a reply, once that happens, remaining plugins are not considered
|
||||||
|
|
||||||
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because currently when the phone
|
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because
|
||||||
// sends things, it sends things using the local node ID as the from address. A better solution (FIXME) would be to let phones
|
// currently when the phone sends things, it sends things using the local node ID as the from address. A better
|
||||||
// have their own distinct addresses and we 'route' to them like any other node.
|
// solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other
|
||||||
if (mp.decoded.want_response && toUs && (mp.from != ourNodeNum || mp.to == ourNodeNum)) {
|
// node.
|
||||||
|
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
||||||
pi.sendResponse(mp);
|
pi.sendResponse(mp);
|
||||||
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
||||||
}
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
@@ -64,11 +81,24 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pi.currentRequest = NULL;
|
pi.currentRequest = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!pluginFound)
|
if (mp.decoded.want_response && toUs) {
|
||||||
|
if (currentReply) {
|
||||||
|
DEBUG_MSG("Sending response\n");
|
||||||
|
service.sendToMesh(currentReply);
|
||||||
|
currentReply = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No one wanted to reply to this requst, tell the requster that happened
|
||||||
|
DEBUG_MSG("No one responded, send a nak\n");
|
||||||
|
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pluginFound)
|
||||||
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
|
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,39 +106,44 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
||||||
* is optional
|
* is optional
|
||||||
*/
|
*/
|
||||||
void MeshPlugin::sendResponse(const MeshPacket &req) {
|
void MeshPlugin::sendResponse(const MeshPacket &req)
|
||||||
|
{
|
||||||
auto r = allocReply();
|
auto r = allocReply();
|
||||||
if(r) {
|
if (r) {
|
||||||
DEBUG_MSG("Sending response\n");
|
|
||||||
setReplyTo(r, req);
|
setReplyTo(r, req);
|
||||||
service.sendToMesh(r);
|
currentReply = r;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
|
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
|
||||||
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||||
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||||
*/
|
*/
|
||||||
void setReplyTo(MeshPacket *p, const MeshPacket &to) {
|
void setReplyTo(MeshPacket *p, const MeshPacket &to)
|
||||||
|
{
|
||||||
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
|
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
|
||||||
p->to = to.from;
|
p->to = getFrom(&to);
|
||||||
p->want_ack = to.want_ack;
|
p->channel = to.channel; // Use the same channel that the request came in on
|
||||||
|
|
||||||
|
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
||||||
|
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
||||||
|
if (p->priority == MeshPacket_Priority_UNSET)
|
||||||
|
p->priority = MeshPacket_Priority_RELIABLE;
|
||||||
p->decoded.request_id = to.id;
|
p->decoded.request_id = to.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames() {
|
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
|
||||||
|
{
|
||||||
|
|
||||||
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
||||||
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
||||||
auto &pi = **i;
|
auto &pi = **i;
|
||||||
if ( pi.wantUIFrame()) {
|
if (pi.wantUIFrame()) {
|
||||||
DEBUG_MSG("Plugin wants a UI Frame\n");
|
DEBUG_MSG("Plugin wants a UI Frame\n");
|
||||||
pluginsWithUIFrames.push_back(&pi);
|
pluginsWithUIFrames.push_back(&pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pluginsWithUIFrames;
|
return pluginsWithUIFrames;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "mesh/MeshTypes.h"
|
#include "mesh/MeshTypes.h"
|
||||||
#include <vector>
|
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
#include <vector>
|
||||||
/** A baseclass for any mesh "plugin".
|
/** A baseclass for any mesh "plugin".
|
||||||
*
|
*
|
||||||
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
|
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
class MeshPlugin
|
class MeshPlugin
|
||||||
{
|
{
|
||||||
static std::vector<MeshPlugin *> *plugins;
|
static std::vector<MeshPlugin *> *plugins;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructor
|
/** Constructor
|
||||||
@@ -37,16 +37,24 @@ class MeshPlugin
|
|||||||
protected:
|
protected:
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific recipient)
|
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
|
||||||
But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those plugins can set this to
|
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
|
||||||
true and their handleReceived() will be called for every packet.
|
plugins can set this to true and their handleReceived() will be called for every packet.
|
||||||
*/
|
*/
|
||||||
bool isPromiscuous = false;
|
bool isPromiscuous = false;
|
||||||
|
|
||||||
|
/** If a bound channel name is set, we will only accept received packets that come in on that channel.
|
||||||
|
* A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
|
||||||
|
* are allowed on any channel (this lets the local user do anything).
|
||||||
|
*
|
||||||
|
* We will send responses on the same channel that the request arrived on.
|
||||||
|
*/
|
||||||
|
const char *boundChannel = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this plugin is currently handling a request currentRequest will be preset
|
* If this plugin is currently handling a request currentRequest will be preset
|
||||||
* to the packet with the request. This is mostly useful for reply handlers.
|
* to the packet with the request. This is mostly useful for reply handlers.
|
||||||
*
|
*
|
||||||
* Note: this can be static because we are guaranteed to be processing only one
|
* Note: this can be static because we are guaranteed to be processing only one
|
||||||
* plugin at a time.
|
* plugin at a time.
|
||||||
*/
|
*/
|
||||||
@@ -78,9 +86,13 @@ class MeshPlugin
|
|||||||
*/
|
*/
|
||||||
virtual bool wantUIFrame() { return false; }
|
virtual bool wantUIFrame() { return false; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
|
||||||
|
* the RoutingPlugin to avoid sending redundant acks
|
||||||
|
*/
|
||||||
|
static MeshPacket *currentReply;
|
||||||
|
friend class ReliableRouter;
|
||||||
|
|
||||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||||
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
|
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
|
||||||
@@ -89,7 +101,7 @@ class MeshPlugin
|
|||||||
void sendResponse(const MeshPacket &req);
|
void sendResponse(const MeshPacket &req);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||||
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||||
*/
|
*/
|
||||||
void setReplyTo(MeshPacket *p, const MeshPacket &to);
|
void setReplyTo(MeshPacket *p, const MeshPacket &to);
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "plugins/PositionPlugin.h"
|
|
||||||
#include "plugins/NodeInfoPlugin.h"
|
#include "plugins/NodeInfoPlugin.h"
|
||||||
|
#include "plugins/PositionPlugin.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -51,8 +51,6 @@ MeshService service;
|
|||||||
|
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||||
{
|
{
|
||||||
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
||||||
@@ -67,7 +65,6 @@ void MeshService::init()
|
|||||||
gpsObserver.observe(&gps->newStatus);
|
gpsObserver.observe(&gps->newStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int MeshService::handleFromRadio(const MeshPacket *mp)
|
int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||||
{
|
{
|
||||||
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
||||||
@@ -107,7 +104,7 @@ bool MeshService::reloadConfig()
|
|||||||
// This will also update the region as needed
|
// This will also update the region as needed
|
||||||
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||||
|
|
||||||
configChanged.notifyObservers(NULL);
|
configChanged.notifyObservers(NULL); // This will cause radio hardware to change freqs etc
|
||||||
nodeDB.saveToDisk();
|
nodeDB.saveToDisk();
|
||||||
|
|
||||||
return didReset;
|
return didReset;
|
||||||
@@ -117,7 +114,8 @@ bool MeshService::reloadConfig()
|
|||||||
void MeshService::reloadOwner()
|
void MeshService::reloadOwner()
|
||||||
{
|
{
|
||||||
assert(nodeInfoPlugin);
|
assert(nodeInfoPlugin);
|
||||||
nodeInfoPlugin->sendOurNodeInfo();
|
if(nodeInfoPlugin)
|
||||||
|
nodeInfoPlugin->sendOurNodeInfo();
|
||||||
nodeDB.saveToDisk();
|
nodeDB.saveToDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +126,12 @@ void MeshService::reloadOwner()
|
|||||||
*/
|
*/
|
||||||
void MeshService::handleToRadio(MeshPacket &p)
|
void MeshService::handleToRadio(MeshPacket &p)
|
||||||
{
|
{
|
||||||
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
|
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
|
||||||
p.from = nodeDB.getNodeNum();
|
DEBUG_MSG("Warning: phone tried to pick a nodenum, we don't allow that.\n");
|
||||||
|
p.from = 0;
|
||||||
|
} else {
|
||||||
|
// p.from = nodeDB.getNodeNum();
|
||||||
|
}
|
||||||
|
|
||||||
if (p.id == 0)
|
if (p.id == 0)
|
||||||
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
||||||
@@ -151,7 +153,8 @@ void MeshService::handleToRadio(MeshPacket &p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
||||||
bool MeshService::cancelSending(PacketId id) {
|
bool MeshService::cancelSending(PacketId id)
|
||||||
|
{
|
||||||
return router->cancelSending(nodeDB.getNodeNum(), id);
|
return router->cancelSending(nodeDB.getNodeNum(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
|||||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
assert(node);
|
assert(node);
|
||||||
|
|
||||||
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
|
if (node->has_position) {
|
||||||
assert(positionPlugin && nodeInfoPlugin);
|
if(positionPlugin) {
|
||||||
if (node->has_position)
|
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
|
||||||
positionPlugin->sendOurPosition(dest, wantReplies);
|
positionPlugin->sendOurPosition(dest, wantReplies);
|
||||||
else
|
}
|
||||||
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
|
}
|
||||||
|
else {
|
||||||
|
if(nodeInfoPlugin) {
|
||||||
|
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
|
||||||
|
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NodeInfo *MeshService::refreshMyNodeInfo()
|
||||||
NodeInfo *MeshService::refreshMyNodeInfo() {
|
{
|
||||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
assert(node);
|
assert(node);
|
||||||
|
|
||||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
// We might not have a position yet for our local node, in that case, at least try to send the time
|
||||||
if(!node->has_position) {
|
if (!node->has_position) {
|
||||||
memset(&node->position, 0, sizeof(node->position));
|
memset(&node->position, 0, sizeof(node->position));
|
||||||
node->has_position = true;
|
node->has_position = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position &position = node->position;
|
Position &position = node->position;
|
||||||
|
|
||||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
position.time = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
position.time =
|
||||||
|
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
||||||
|
|
||||||
position.battery_level = powerStatus->getBatteryChargePercent();
|
position.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
updateBatteryLevel(position.battery_level);
|
updateBatteryLevel(position.battery_level);
|
||||||
@@ -209,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
|||||||
pos.altitude = gps->altitude;
|
pos.altitude = gps->altitude;
|
||||||
pos.latitude_i = gps->latitude;
|
pos.latitude_i = gps->latitude;
|
||||||
pos.longitude_i = gps->longitude;
|
pos.longitude_i = gps->longitude;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||||
// the old position
|
// the old position
|
||||||
if(radioConfig.preferences.fixed_position) {
|
if (radioConfig.preferences.fixed_position) {
|
||||||
DEBUG_MSG("WARNING: Using fixed position\n");
|
DEBUG_MSG("WARNING: Using fixed position\n");
|
||||||
} else {
|
} else {
|
||||||
// throw away old position
|
// throw away old position
|
||||||
|
|||||||
@@ -32,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number
|
|||||||
typedef int ErrorCode;
|
typedef int ErrorCode;
|
||||||
|
|
||||||
/// Alloc and free packets to our global, ISR safe pool
|
/// Alloc and free packets to our global, ISR safe pool
|
||||||
extern Allocator<MeshPacket> &packetPool;
|
extern Allocator<MeshPacket> &packetPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node.
|
||||||
|
* If from is zero this function returns our node number instead
|
||||||
|
*/
|
||||||
|
NodeNum getFrom(const MeshPacket *p);
|
||||||
@@ -30,7 +30,8 @@ NodeDB nodeDB;
|
|||||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||||
EXT_RAM_ATTR DeviceState devicestate;
|
EXT_RAM_ATTR DeviceState devicestate;
|
||||||
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||||
RadioConfig &radioConfig = devicestate.radio;
|
RadioConfig radioConfig;
|
||||||
|
ChannelFile channelFile;
|
||||||
|
|
||||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||||
@@ -66,6 +67,15 @@ NodeNum displayedNodeNum;
|
|||||||
|
|
||||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
||||||
|
* the local node. If from is zero this function returns our node number instead
|
||||||
|
*/
|
||||||
|
NodeNum getFrom(const MeshPacket *p)
|
||||||
|
{
|
||||||
|
return (p->from == 0) ? nodeDB.getNodeNum() : p->from;
|
||||||
|
}
|
||||||
|
|
||||||
bool NodeDB::resetRadioConfig()
|
bool NodeDB::resetRadioConfig()
|
||||||
{
|
{
|
||||||
bool didFactoryReset = false;
|
bool didFactoryReset = false;
|
||||||
@@ -76,7 +86,7 @@ bool NodeDB::resetRadioConfig()
|
|||||||
DEBUG_MSG("Performing factory reset!\n");
|
DEBUG_MSG("Performing factory reset!\n");
|
||||||
installDefaultDeviceState();
|
installDefaultDeviceState();
|
||||||
didFactoryReset = true;
|
didFactoryReset = true;
|
||||||
} else if (devicestate.channels_count == 0) {
|
} else if (channelFile.channels_count == 0) {
|
||||||
DEBUG_MSG("Setting default channel and radio preferences!\n");
|
DEBUG_MSG("Setting default channel and radio preferences!\n");
|
||||||
|
|
||||||
channels.initDefaults();
|
channels.initDefaults();
|
||||||
@@ -109,6 +119,18 @@ bool NodeDB::resetRadioConfig()
|
|||||||
return didFactoryReset;
|
return didFactoryReset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeDB::installDefaultRadioConfig()
|
||||||
|
{
|
||||||
|
memset(&radioConfig, 0, sizeof(radioConfig));
|
||||||
|
radioConfig.has_preferences = true;
|
||||||
|
resetRadioConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeDB::installDefaultChannels()
|
||||||
|
{
|
||||||
|
memset(&channelFile, 0, sizeof(channelFile));
|
||||||
|
}
|
||||||
|
|
||||||
void NodeDB::installDefaultDeviceState()
|
void NodeDB::installDefaultDeviceState()
|
||||||
{
|
{
|
||||||
// We try to preserve the region setting because it will really bum users out if we discard it
|
// We try to preserve the region setting because it will really bum users out if we discard it
|
||||||
@@ -121,14 +143,11 @@ void NodeDB::installDefaultDeviceState()
|
|||||||
|
|
||||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||||
devicestate.has_my_node = true;
|
devicestate.has_my_node = true;
|
||||||
devicestate.has_radio = true;
|
|
||||||
devicestate.has_owner = true;
|
devicestate.has_owner = true;
|
||||||
devicestate.radio.has_preferences = true;
|
|
||||||
devicestate.node_db_count = 0;
|
devicestate.node_db_count = 0;
|
||||||
|
devicestate.version = DEVICESTATE_CUR_VER;
|
||||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||||
|
|
||||||
resetRadioConfig();
|
|
||||||
|
|
||||||
// default to no GPS, until one has been found by probing
|
// default to no GPS, until one has been found by probing
|
||||||
myNodeInfo.has_gps = false;
|
myNodeInfo.has_gps = false;
|
||||||
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
|
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
|
||||||
@@ -150,6 +169,9 @@ void NodeDB::installDefaultDeviceState()
|
|||||||
radioConfig.preferences.region = oldRegionCode;
|
radioConfig.preferences.region = oldRegionCode;
|
||||||
if (oldRegion.length()) // If the old style region was set, try to keep it up-to-date
|
if (oldRegion.length()) // If the old style region was set, try to keep it up-to-date
|
||||||
strcpy(myNodeInfo.region, oldRegion.c_str());
|
strcpy(myNodeInfo.region, oldRegion.c_str());
|
||||||
|
|
||||||
|
installDefaultChannels();
|
||||||
|
installDefaultRadioConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeDB::init()
|
void NodeDB::init()
|
||||||
@@ -178,12 +200,16 @@ void NodeDB::init()
|
|||||||
info->user = owner;
|
info->user = owner;
|
||||||
info->has_user = true;
|
info->has_user = true;
|
||||||
|
|
||||||
|
// removed from 1.2 (though we do use old values if found)
|
||||||
// We set these _after_ loading from disk - because they come from the build and are more trusted than
|
// We set these _after_ loading from disk - because they come from the build and are more trusted than
|
||||||
// what is stored in flash
|
// what is stored in flash
|
||||||
if (xstr(HW_VERSION)[0])
|
//if (xstr(HW_VERSION)[0])
|
||||||
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
// strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
||||||
else
|
// else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
|
||||||
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
|
|
||||||
|
// DEBUG_MSG("legacy region %d\n", devicestate.legacyRadio.preferences.region);
|
||||||
|
if(radioConfig.preferences.region == RegionCode_Unset)
|
||||||
|
radioConfig.preferences.region = devicestate.legacyRadio.preferences.region;
|
||||||
|
|
||||||
// Check for the old style of region code strings, if found, convert to the new enum.
|
// Check for the old style of region code strings, if found, convert to the new enum.
|
||||||
// Those strings will look like "1.0-EU433"
|
// Those strings will look like "1.0-EU433"
|
||||||
@@ -201,8 +227,7 @@ void NodeDB::init()
|
|||||||
|
|
||||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||||
|
|
||||||
DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, radioConfig.preferences.region,
|
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
|
||||||
myNodeInfo.my_node_num, *numNodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We reserve a few nodenums for future use
|
// We reserve a few nodenums for future use
|
||||||
@@ -232,84 +257,134 @@ void NodeDB::pickNewNodeNum()
|
|||||||
myNodeInfo.my_node_num = r;
|
myNodeInfo.my_node_num = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *preffile = "/db.proto";
|
static const char *preffileOld = "/db.proto";
|
||||||
const char *preftmp = "/db.proto.tmp";
|
static const char *preffile = "/prefs/db.proto";
|
||||||
|
static const char *radiofile = "/prefs/radio.proto";
|
||||||
|
static const char *channelfile = "/prefs/channels.proto";
|
||||||
|
// const char *preftmp = "/db.proto.tmp";
|
||||||
|
|
||||||
void NodeDB::loadFromDisk()
|
/** Load a protobuf from a file, return true for success */
|
||||||
|
bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
|
||||||
{
|
{
|
||||||
#ifdef FS
|
#ifdef FS
|
||||||
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||||
|
|
||||||
auto f = FS.open(preffile);
|
auto f = FS.open(filename);
|
||||||
|
|
||||||
|
// FIXME, temporary hack until every node in the universe is 1.2 or later - look for prefs in the old location (so we can
|
||||||
|
// preserve region)
|
||||||
|
if (!f && filename == preffile) {
|
||||||
|
filename = preffileOld;
|
||||||
|
f = FS.open(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool okay = false;
|
||||||
if (f) {
|
if (f) {
|
||||||
DEBUG_MSG("Loading saved preferences\n");
|
DEBUG_MSG("Loading %s\n", filename);
|
||||||
pb_istream_t stream = {&readcb, &f, DeviceState_size};
|
pb_istream_t stream = {&readcb, &f, protoSize};
|
||||||
|
|
||||||
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
|
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
|
||||||
|
|
||||||
memset(&devicestate, 0, sizeof(devicestate));
|
memset(dest_struct, 0, objSize);
|
||||||
if (!pb_decode(&stream, DeviceState_fields, &devicestate)) {
|
if (!pb_decode(&stream, fields, dest_struct)) {
|
||||||
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
|
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
|
||||||
// FIXME - report failure to phone
|
|
||||||
} else {
|
} else {
|
||||||
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
okay = true;
|
||||||
DEBUG_MSG("Warn: devicestate is old, discarding\n");
|
|
||||||
installDefaultDeviceState();
|
|
||||||
} else {
|
|
||||||
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG_MSG("Postload channel name=%s\n", channelSettings.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("No saved preferences found\n");
|
DEBUG_MSG("No %s preferences found\n", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||||
#endif
|
#endif
|
||||||
|
return okay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeDB::loadFromDisk()
|
||||||
|
{
|
||||||
|
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||||
|
if (!loadProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate)) {
|
||||||
|
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
||||||
|
} else {
|
||||||
|
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
||||||
|
DEBUG_MSG("Warn: devicestate %d is old, discarding\n", devicestate.version);
|
||||||
|
installDefaultDeviceState();
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig)) {
|
||||||
|
installDefaultRadioConfig(); // Our in RAM copy might now be corrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile)) {
|
||||||
|
installDefaultChannels(); // Our in RAM copy might now be corrupt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save a protobuf from a file, return true for success */
|
||||||
|
bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, const void *dest_struct)
|
||||||
|
{
|
||||||
|
#ifdef FS
|
||||||
|
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||||
|
String filenameTmp = filename;
|
||||||
|
filenameTmp += ".tmp";
|
||||||
|
auto f = FS.open(filenameTmp.c_str(), FILE_O_WRITE);
|
||||||
|
bool okay = false;
|
||||||
|
if (f) {
|
||||||
|
DEBUG_MSG("Saving %s\n", filename);
|
||||||
|
pb_ostream_t stream = {&writecb, &f, protoSize};
|
||||||
|
|
||||||
|
if (!pb_encode(&stream, fields, dest_struct)) {
|
||||||
|
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
|
} else {
|
||||||
|
okay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// brief window of risk here ;-)
|
||||||
|
if (!FS.remove(filename))
|
||||||
|
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||||
|
if (!FS.rename(filenameTmp.c_str(), filename))
|
||||||
|
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("Can't write prefs\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||||
|
#endif
|
||||||
|
return okay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeDB::saveChannelsToDisk()
|
||||||
|
{
|
||||||
|
if (!devicestate.no_save) {
|
||||||
|
#ifdef FS
|
||||||
|
FS.mkdir("/prefs");
|
||||||
|
#endif
|
||||||
|
saveProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeDB::saveToDisk()
|
void NodeDB::saveToDisk()
|
||||||
{
|
{
|
||||||
#ifdef FS
|
|
||||||
if (!devicestate.no_save) {
|
if (!devicestate.no_save) {
|
||||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
#ifdef FS
|
||||||
if (f) {
|
FS.mkdir("/prefs");
|
||||||
DEBUG_MSG("Writing preferences\n");
|
#endif
|
||||||
|
bool okay = saveProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate);
|
||||||
|
okay &= saveProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig);
|
||||||
|
saveChannelsToDisk();
|
||||||
|
|
||||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
// remove any pre 1.2 pref files, turn on after 1.2 is in beta
|
||||||
|
// if(okay) FS.remove(preffileOld);
|
||||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
|
||||||
|
|
||||||
devicestate.version = DEVICESTATE_CUR_VER;
|
|
||||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
|
||||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
|
||||||
// FIXME - report failure to phone
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
} else {
|
|
||||||
// Success - replace the old file
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
// brief window of risk here ;-)
|
|
||||||
if (!FS.remove(preffile))
|
|
||||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
|
||||||
if (!FS.rename(preftmp, preffile))
|
|
||||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeInfo *NodeDB::readNextInfo()
|
const NodeInfo *NodeDB::readNextInfo()
|
||||||
@@ -362,7 +437,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
|
|||||||
// recorded based on the packet rxTime
|
// recorded based on the packet rxTime
|
||||||
if (!info->position.time && p.time)
|
if (!info->position.time && p.time)
|
||||||
info->position.time = p.time;
|
info->position.time = p.time;
|
||||||
if(p.battery_level)
|
if (p.battery_level)
|
||||||
info->position.battery_level = p.battery_level;
|
info->position.battery_level = p.battery_level;
|
||||||
if (p.latitude_i || p.longitude_i) {
|
if (p.latitude_i || p.longitude_i) {
|
||||||
info->position.latitude_i = p.latitude_i;
|
info->position.latitude_i = p.latitude_i;
|
||||||
@@ -406,7 +481,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||||
|
|
||||||
NodeInfo *info = getOrCreateNode(mp.from);
|
NodeInfo *info = getOrCreateNode(getFrom(&mp));
|
||||||
|
|
||||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
||||||
info->has_position = true; // at least the time is valid
|
info->has_position = true; // at least the time is valid
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
|
||||||
extern DeviceState devicestate;
|
extern DeviceState devicestate;
|
||||||
|
extern ChannelFile channelFile;
|
||||||
extern MyNodeInfo &myNodeInfo;
|
extern MyNodeInfo &myNodeInfo;
|
||||||
extern RadioConfig &radioConfig;
|
extern RadioConfig radioConfig;
|
||||||
extern User &owner;
|
extern User &owner;
|
||||||
|
|
||||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||||
@@ -42,7 +43,7 @@ class NodeDB
|
|||||||
void init();
|
void init();
|
||||||
|
|
||||||
/// write to flash
|
/// write to flash
|
||||||
void saveToDisk();
|
void saveToDisk(), saveChannelsToDisk();
|
||||||
|
|
||||||
/** Reinit radio config if needed, because either:
|
/** Reinit radio config if needed, because either:
|
||||||
* a) sometimes a buggy android app might send us bogus settings or
|
* a) sometimes a buggy android app might send us bogus settings or
|
||||||
@@ -117,7 +118,7 @@ class NodeDB
|
|||||||
void loadFromDisk();
|
void loadFromDisk();
|
||||||
|
|
||||||
/// Reinit device state from scratch (not loading from disk)
|
/// Reinit device state from scratch (not loading from disk)
|
||||||
void installDefaultDeviceState();
|
void installDefaultDeviceState(), installDefaultRadioConfig(), installDefaultChannels();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,3 +184,4 @@ PREF_GET(min_wake_secs, 10)
|
|||||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||||
*/
|
*/
|
||||||
extern uint32_t radioGeneration;
|
extern uint32_t radioGeneration;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
||||||
recentPackets.erase(recentPackets.begin() + i); // delete old record
|
recentPackets.erase(recentPackets.begin() + i); // delete old record
|
||||||
} else {
|
} else {
|
||||||
if (r.id == p->id && r.sender == p->from) {
|
if (r.id == p->id && r.sender == getFrom(p)) {
|
||||||
DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||||
|
|
||||||
// Update the time on this record to now
|
// Update the time on this record to now
|
||||||
@@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
if (withUpdate) {
|
if (withUpdate) {
|
||||||
PacketRecord r;
|
PacketRecord r;
|
||||||
r.id = p->id;
|
r.id = p->id;
|
||||||
r.sender = p->from;
|
r.sender = getFrom(p);
|
||||||
r.rxTimeMsec = now;
|
r.rxTimeMsec = now;
|
||||||
recentPackets.push_back(r);
|
recentPackets.push_back(r);
|
||||||
printPacket("Adding packet record", p);
|
printPacket("Adding packet record", p);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
}
|
}
|
||||||
// return (lastContactMsec != 0) &&
|
// return (lastContactMsec != 0) &&
|
||||||
|
|
||||||
|
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
|
||||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||||
switch (toRadioScratch.which_payloadVariant) {
|
switch (toRadioScratch.which_payloadVariant) {
|
||||||
case ToRadio_packet_tag: {
|
case ToRadio_packet_tag: {
|
||||||
@@ -155,7 +156,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
state = STATE_SEND_PACKETS;
|
state = STATE_SEND_PACKETS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_LEGACY: // Treat as the same as send packets
|
|
||||||
case STATE_SEND_PACKETS:
|
case STATE_SEND_PACKETS:
|
||||||
// Do we have a message from the mesh?
|
// Do we have a message from the mesh?
|
||||||
if (packetForPhone) {
|
if (packetForPhone) {
|
||||||
@@ -178,9 +178,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
// Do we have a message from the mesh?
|
// Do we have a message from the mesh?
|
||||||
if (fromRadioScratch.which_payloadVariant != 0) {
|
if (fromRadioScratch.which_payloadVariant != 0) {
|
||||||
// Encapsulate as a FromRadio packet
|
// Encapsulate as a FromRadio packet
|
||||||
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_payloadVariant);
|
|
||||||
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
|
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
|
||||||
DEBUG_MSG(", %d bytes\n", numbytes);
|
// DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes);
|
||||||
return numbytes;
|
return numbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +207,6 @@ bool PhoneAPI::available()
|
|||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_LEGACY: // Treat as the same as send packets
|
|
||||||
case STATE_SEND_PACKETS: {
|
case STATE_SEND_PACKETS: {
|
||||||
// Try to pull a new packet from the service (if we haven't already)
|
// Try to pull a new packet from the service (if we haven't already)
|
||||||
if (!packetForPhone)
|
if (!packetForPhone)
|
||||||
@@ -236,7 +234,7 @@ int PhoneAPI::onNotify(uint32_t newValue)
|
|||||||
checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this
|
checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this
|
||||||
// from idle)
|
// from idle)
|
||||||
|
|
||||||
if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) {
|
if (state == STATE_SEND_PACKETS) {
|
||||||
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
|
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
|
||||||
onNowHasData(newValue);
|
onNowHasData(newValue);
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class PhoneAPI
|
|||||||
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
||||||
{
|
{
|
||||||
enum State {
|
enum State {
|
||||||
STATE_LEGACY, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API
|
STATE_UNUSED, // (no longer used) old 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_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_MY_INFO, // send our my info record
|
||||||
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet
|
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet
|
||||||
|
|||||||
@@ -58,11 +58,12 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
|
|||||||
// it would be better to update even if the message was destined to others.
|
// it would be better to update even if the message was destined to others.
|
||||||
|
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
|
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size);
|
||||||
|
|
||||||
T scratch;
|
T scratch;
|
||||||
T *decoded = NULL;
|
T *decoded = NULL;
|
||||||
if(mp.decoded.portnum == ourPortNum) {
|
if(mp.decoded.portnum == ourPortNum) {
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
||||||
decoded = &scratch;
|
decoded = &scratch;
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "Channels.h"
|
#include "Channels.h"
|
||||||
#include "MeshRadio.h"
|
#include "MeshRadio.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "configuration.h"
|
#include "Router.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "Channels.h"
|
|
||||||
|
|
||||||
#define RDEF(name, freq, spacing, num_ch, power_limit) \
|
#define RDEF(name, freq, spacing, num_ch, power_limit) \
|
||||||
{ \
|
{ \
|
||||||
@@ -25,9 +25,27 @@ const RegionInfo regions[] = {
|
|||||||
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
|
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
|
||||||
// freq. (921.9f is for download, others are for uplink)
|
// freq. (921.9f is for download, others are for uplink)
|
||||||
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
|
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
|
||||||
|
RDEF(RU, 868.9f, 0.2f, 2, 20), // See notes below
|
||||||
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2):
|
||||||
|
|
||||||
|
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision № 18-46-03-1 (September 11th 2018) https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1
|
||||||
|
We have 3 options for 868 MHz:
|
||||||
|
|
||||||
|
864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports.
|
||||||
|
866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports
|
||||||
|
868,7 - 869,2 MHz ERP 100mW, Duty Cycle 10% or LBT, no resctrictions
|
||||||
|
and according to RP2-1.0.2 LoRaWAN® Regional Parameters RP2-1.0.2 LoRaWAN® Regional Parameters - LoRa Alliance®
|
||||||
|
I propose to use following meshtastic bandplan:
|
||||||
|
1 channel - central frequency 868.9MHz 125kHz band
|
||||||
|
Protective band - 75kHz
|
||||||
|
2 channel - central frequency 869.1MHz 125kHz band
|
||||||
|
|
||||||
|
RDEF(RU, 868.9f, 0.2f, 2, 20)
|
||||||
|
*/
|
||||||
|
|
||||||
const RegionInfo *myRegion;
|
const RegionInfo *myRegion;
|
||||||
|
|
||||||
void initRegion()
|
void initRegion()
|
||||||
@@ -119,11 +137,11 @@ uint32_t RadioInterface::getTxDelayMsec()
|
|||||||
|
|
||||||
void printPacket(const char *prefix, const MeshPacket *p)
|
void printPacket(const char *prefix, const MeshPacket *p)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack,
|
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
||||||
p->hop_limit, p->channel);
|
p->want_ack, p->hop_limit, p->channel);
|
||||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
auto &s = p->decoded;
|
auto &s = p->decoded;
|
||||||
|
|
||||||
DEBUG_MSG(" Portnum=%d", s.portnum);
|
DEBUG_MSG(" Portnum=%d", s.portnum);
|
||||||
|
|
||||||
if (s.want_response)
|
if (s.want_response)
|
||||||
@@ -135,6 +153,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
|||||||
if (s.dest != 0)
|
if (s.dest != 0)
|
||||||
DEBUG_MSG(" dest=%08x", s.dest);
|
DEBUG_MSG(" dest=%08x", s.dest);
|
||||||
|
|
||||||
|
if(s.request_id)
|
||||||
|
DEBUG_MSG(" requestId=%0x", s.request_id);
|
||||||
|
|
||||||
/* now inside Data and therefore kinda opaque
|
/* now inside Data and therefore kinda opaque
|
||||||
if (s.which_ackVariant == SubPacket_success_id_tag)
|
if (s.which_ackVariant == SubPacket_success_id_tag)
|
||||||
DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
|
DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
|
||||||
@@ -332,6 +353,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
|
|||||||
{
|
{
|
||||||
assert(rxDest);
|
assert(rxDest);
|
||||||
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
||||||
|
|
||||||
|
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
|
||||||
|
if (router)
|
||||||
|
router->setReceivedMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "ReliableRouter.h"
|
#include "ReliableRouter.h"
|
||||||
|
#include "MeshPlugin.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
@@ -26,15 +27,18 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
|||||||
|
|
||||||
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||||
{
|
{
|
||||||
|
// Note: do not use getFrom() here, because we want to ignore messages sent from phone
|
||||||
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
||||||
printPacket("Rx someone rebroadcasting for us", p);
|
printPacket("Rx someone rebroadcasting for us", p);
|
||||||
|
|
||||||
// We are seeing someone rebroadcast one of our broadcast attempts.
|
// We are seeing someone rebroadcast one of our broadcast attempts.
|
||||||
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
|
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
|
||||||
// the original sending process.
|
// the original sending process.
|
||||||
if (stopRetransmission(p->from, p->id)) {
|
if (stopRetransmission(getFrom(p), p->id)) {
|
||||||
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
|
DEBUG_MSG("generating implicit ack\n");
|
||||||
sendAckNak(Routing_Error_NONE, p->from, p->id);
|
// NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be
|
||||||
|
// marked as wantAck
|
||||||
|
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,23 +64,26 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
|||||||
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
|
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
|
||||||
// - not DSR routing)
|
// - not DSR routing)
|
||||||
if (p->want_ack) {
|
if (p->want_ack) {
|
||||||
sendAckNak(Routing_Error_NONE, p->from, p->id);
|
if (MeshPlugin::currentReply)
|
||||||
|
DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack\n");
|
||||||
|
else
|
||||||
|
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the payload is valid, look for ack/nak
|
// We consider an ack to be either a !routing packet with a request ID or a routing packet with !error
|
||||||
if (c) {
|
PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_id : 0;
|
||||||
PacketId ackId = c->error_reason == Routing_Error_NONE ? p->decoded.request_id : 0;
|
|
||||||
PacketId nakId = c->error_reason != Routing_Error_NONE ? p->decoded.request_id : 0;
|
|
||||||
|
|
||||||
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
// A nak is a routing packt that has an error code
|
||||||
if (ackId || nakId) {
|
PacketId nakId = (c && c->error_reason != Routing_Error_NONE) ? p->decoded.request_id : 0;
|
||||||
if (ackId) {
|
|
||||||
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
|
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
||||||
stopRetransmission(p->to, ackId);
|
if (ackId || nakId) {
|
||||||
} else {
|
if (ackId) {
|
||||||
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
|
DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId);
|
||||||
stopRetransmission(p->to, nakId);
|
stopRetransmission(p->to, ackId);
|
||||||
}
|
} else {
|
||||||
|
DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
|
||||||
|
stopRetransmission(p->to, nakId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,8 +137,9 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
|||||||
auto id = GlobalPacketId(p);
|
auto id = GlobalPacketId(p);
|
||||||
auto rec = PendingPacket(p);
|
auto rec = PendingPacket(p);
|
||||||
|
|
||||||
|
stopRetransmission(getFrom(p), p->id);
|
||||||
|
|
||||||
setNextTx(&rec);
|
setNextTx(&rec);
|
||||||
stopRetransmission(p->from, p->id);
|
|
||||||
pending[id] = rec;
|
pending[id] = rec;
|
||||||
|
|
||||||
return &pending[id];
|
return &pending[id];
|
||||||
@@ -151,18 +159,21 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
++nextIt; // we use this odd pattern because we might be deleting it...
|
++nextIt; // we use this odd pattern because we might be deleting it...
|
||||||
auto &p = it->second;
|
auto &p = it->second;
|
||||||
|
|
||||||
|
bool stillValid = true; // assume we'll keep this record around
|
||||||
|
|
||||||
// FIXME, handle 51 day rolloever here!!!
|
// FIXME, handle 51 day rolloever here!!!
|
||||||
if (p.nextTxMsec <= now) {
|
if (p.nextTxMsec <= now) {
|
||||||
if (p.numRetransmissions == 0) {
|
if (p.numRetransmissions == 0) {
|
||||||
DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
|
DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to,
|
||||||
p.packet->id);
|
p.packet->id);
|
||||||
sendAckNak(Routing_Error_MAX_RETRANSMIT, p.packet->from, p.packet->id);
|
sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel);
|
||||||
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
|
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
|
||||||
// allows the DSR version to still be able to look at the PendingPacket
|
// allows the DSR version to still be able to look at the PendingPacket
|
||||||
stopRetransmission(it->first);
|
stopRetransmission(it->first);
|
||||||
|
stillValid = false; // just deleted it
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
|
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
|
||||||
p.packet->id, p.numRetransmissions);
|
p.packet->to, p.packet->id, p.numRetransmissions);
|
||||||
|
|
||||||
// Note: we call the superclass version because we don't want to have our version of send() add a new
|
// Note: we call the superclass version because we don't want to have our version of send() add a new
|
||||||
// retransmission record
|
// retransmission record
|
||||||
@@ -172,8 +183,10 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
--p.numRetransmissions;
|
--p.numRetransmissions;
|
||||||
setNextTx(&p);
|
setNextTx(&p);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Not yet time
|
|
||||||
|
if (stillValid) {
|
||||||
|
// Update our desired sleep delay
|
||||||
int32_t t = p.nextTxMsec - now;
|
int32_t t = p.nextTxMsec - now;
|
||||||
|
|
||||||
d = min(t, d);
|
d = min(t, d);
|
||||||
@@ -181,4 +194,14 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReliableRouter::setNextTx(PendingPacket *pending)
|
||||||
|
{
|
||||||
|
assert(iface);
|
||||||
|
auto d = iface->getRetransmissionMsec(pending->packet);
|
||||||
|
pending->nextTxMsec = millis() + d;
|
||||||
|
DEBUG_MSG("Setting next retransmission in %u msecs: ", d);
|
||||||
|
printPacket("", pending->packet);
|
||||||
|
setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ struct GlobalPacketId {
|
|||||||
|
|
||||||
GlobalPacketId(const MeshPacket *p)
|
GlobalPacketId(const MeshPacket *p)
|
||||||
{
|
{
|
||||||
node = p->from;
|
node = getFrom(p);
|
||||||
id = p->id;
|
id = p->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter
|
|||||||
*/
|
*/
|
||||||
int32_t doRetransmissions();
|
int32_t doRetransmissions();
|
||||||
|
|
||||||
void setNextTx(PendingPacket *pending) {
|
void setNextTx(PendingPacket *pending);
|
||||||
assert(iface);
|
|
||||||
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -103,24 +103,29 @@ MeshPacket *Router::allocForSending()
|
|||||||
/**
|
/**
|
||||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||||
*/
|
*/
|
||||||
void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom)
|
void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
|
||||||
{
|
{
|
||||||
routingPlugin->sendAckNak(err, to, idFrom);
|
routingPlugin->sendAckNak(err, to, idFrom, chIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err);
|
DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err);
|
||||||
sendAckNak(Routing_Error_NO_INTERFACE, p->from, p->id);
|
sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel);
|
||||||
packetPool.release(p);
|
packetPool.release(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Router::setReceivedMessage() {
|
||||||
|
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
|
||||||
|
}
|
||||||
|
|
||||||
ErrorCode Router::sendLocal(MeshPacket *p)
|
ErrorCode Router::sendLocal(MeshPacket *p)
|
||||||
{
|
{
|
||||||
// No need to deliver externally if the destination is the local node
|
// No need to deliver externally if the destination is the local node
|
||||||
if (p->to == nodeDB.getNodeNum()) {
|
if (p->to == nodeDB.getNodeNum()) {
|
||||||
printPacket("Enqueuing local", p);
|
printPacket("Enqueuing local", p);
|
||||||
fromRadioQueue.enqueue(p);
|
fromRadioQueue.enqueue(p);
|
||||||
|
setReceivedMessage();
|
||||||
return ERRNO_OK;
|
return ERRNO_OK;
|
||||||
} else if (!iface) {
|
} else if (!iface) {
|
||||||
// We must be sending to remote nodes also, fail if no interface found
|
// We must be sending to remote nodes also, fail if no interface found
|
||||||
@@ -138,6 +143,13 @@ ErrorCode Router::sendLocal(MeshPacket *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printBytes(const char *label, const uint8_t *p, size_t numbytes) {
|
||||||
|
DEBUG_MSG("%s: ", label);
|
||||||
|
for(size_t i = 0; i < numbytes; i++)
|
||||||
|
DEBUG_MSG("%02x ", p[i]);
|
||||||
|
DEBUG_MSG("\n");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a packet on a suitable interface. This routine will
|
* Send a packet on a suitable interface. This routine will
|
||||||
* later free() the packet to pool. This routine is not allowed to stall.
|
* later free() the packet to pool. This routine is not allowed to stall.
|
||||||
@@ -155,6 +167,10 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
if (p->to == NODENUM_BROADCAST)
|
if (p->to == NODENUM_BROADCAST)
|
||||||
p->want_ack = false;
|
p->want_ack = false;
|
||||||
|
|
||||||
|
// Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over
|
||||||
|
// the lora we need to make sure we have replaced it with our local address
|
||||||
|
p->from = getFrom(p);
|
||||||
|
|
||||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||||
|
|
||||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
|
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
|
||||||
@@ -164,6 +180,8 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||||
|
|
||||||
|
// printPacket("pre encrypt", p); // portnum valid here
|
||||||
|
|
||||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
||||||
|
|
||||||
if (numbytes > MAX_RHPACKETLEN) {
|
if (numbytes > MAX_RHPACKETLEN) {
|
||||||
@@ -171,6 +189,8 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
return ERRNO_TOO_LARGE;
|
return ERRNO_TOO_LARGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//printBytes("plaintext", bytes, numbytes);
|
||||||
|
|
||||||
auto hash = channels.setActiveByIndex(p->channel);
|
auto hash = channels.setActiveByIndex(p->channel);
|
||||||
if (hash < 0) {
|
if (hash < 0) {
|
||||||
// No suitable channel could be found for sending
|
// No suitable channel could be found for sending
|
||||||
@@ -180,7 +200,7 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
|
|
||||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||||
p->channel = hash;
|
p->channel = hash;
|
||||||
crypto->encrypt(p->from, p->id, numbytes, bytes);
|
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||||
|
|
||||||
// Copy back into the packet and set the variant type
|
// Copy back into the packet and set the variant type
|
||||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||||
@@ -221,19 +241,26 @@ bool Router::perhapsDecode(MeshPacket *p)
|
|||||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||||
// Try to decrypt the packet if we can
|
// Try to decrypt the packet if we can
|
||||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||||
|
size_t rawSize = p->encrypted.size;
|
||||||
|
assert(rawSize <= sizeof(bytes));
|
||||||
memcpy(bytes, p->encrypted.bytes,
|
memcpy(bytes, p->encrypted.bytes,
|
||||||
p->encrypted
|
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||||
.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||||
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
|
|
||||||
|
//printBytes("plaintext", bytes, p->encrypted.size);
|
||||||
|
|
||||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||||
if (!pb_decode_from_bytes(bytes, p->encrypted.size, Data_fields, &p->decoded)) {
|
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||||
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?!\n");
|
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
|
||||||
} else {
|
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n");
|
||||||
|
} else if(p->decoded.portnum == PortNum_UNKNOWN_APP) {
|
||||||
|
DEBUG_MSG("Invalid portnum (bad psk?)!\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
// parsing was successful
|
// parsing was successful
|
||||||
|
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
|
||||||
p->channel = chIndex; // change to store the index instead of the hash
|
p->channel = chIndex; // change to store the index instead of the hash
|
||||||
// printPacket("decoded message", p);
|
printPacket("decoded message", p);
|
||||||
p->which_payloadVariant = MeshPacket_decoded_tag;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,11 +285,10 @@ void Router::handleReceived(MeshPacket *p)
|
|||||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||||
|
|
||||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||||
bool decoded = perhapsDecode(p);
|
bool decoded = perhapsDecode(p);
|
||||||
printPacket("handleReceived", p);
|
|
||||||
DEBUG_MSG("decoded=%d\n", decoded);
|
|
||||||
if (decoded) {
|
if (decoded) {
|
||||||
// parsing was successful, queue for our recipient
|
// parsing was successful, queue for our recipient
|
||||||
|
printPacket("handleReceived", p);
|
||||||
|
|
||||||
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
|
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
|
||||||
// sniffReceived(p);
|
// sniffReceived(p);
|
||||||
@@ -273,7 +299,7 @@ void Router::handleReceived(MeshPacket *p)
|
|||||||
void Router::perhapsHandleReceived(MeshPacket *p)
|
void Router::perhapsHandleReceived(MeshPacket *p)
|
||||||
{
|
{
|
||||||
assert(radioConfig.has_preferences);
|
assert(radioConfig.has_preferences);
|
||||||
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from);
|
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, getFrom(p));
|
||||||
|
|
||||||
if (ignore)
|
if (ignore)
|
||||||
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
|
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
|
#include "Channels.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mesh aware router that supports multiple interfaces.
|
* A mesh aware router that supports multiple interfaces.
|
||||||
@@ -63,6 +64,11 @@ class Router : protected concurrency::OSThread
|
|||||||
* @return our local nodenum */
|
* @return our local nodenum */
|
||||||
NodeNum getNodeNum();
|
NodeNum getNodeNum();
|
||||||
|
|
||||||
|
/** Wake up the router thread ASAP, because we just queued a message for it.
|
||||||
|
* FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue'
|
||||||
|
*/
|
||||||
|
void setReceivedMessage();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class RoutingPlugin;
|
friend class RoutingPlugin;
|
||||||
|
|
||||||
@@ -101,7 +107,7 @@ class Router : protected concurrency::OSThread
|
|||||||
/**
|
/**
|
||||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||||
*/
|
*/
|
||||||
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom);
|
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -71,12 +71,18 @@ void StreamAPI::writeStream()
|
|||||||
void StreamAPI::emitTxBuffer(size_t len)
|
void StreamAPI::emitTxBuffer(size_t len)
|
||||||
{
|
{
|
||||||
if (len != 0) {
|
if (len != 0) {
|
||||||
|
DEBUG_MSG("emit tx %d\n", len);
|
||||||
txBuf[0] = START1;
|
txBuf[0] = START1;
|
||||||
txBuf[1] = START2;
|
txBuf[1] = START2;
|
||||||
txBuf[2] = (len >> 8) & 0xff;
|
txBuf[2] = (len >> 8) & 0xff;
|
||||||
txBuf[3] = len & 0xff;
|
txBuf[3] = len & 0xff;
|
||||||
|
|
||||||
stream->write(txBuf, len + HEADER_LEN);
|
auto totalLen = len + HEADER_LEN;
|
||||||
|
stream->write(txBuf, totalLen);
|
||||||
|
/* for(size_t i = 0; i < totalLen; i++) {
|
||||||
|
stream->write(txBuf[i]);
|
||||||
|
// stream->flush();
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ typedef struct _AdminMessage {
|
|||||||
RadioConfig get_radio_response;
|
RadioConfig get_radio_response;
|
||||||
uint32_t get_channel_request;
|
uint32_t get_channel_request;
|
||||||
Channel get_channel_response;
|
Channel get_channel_response;
|
||||||
|
bool confirm_set_channel;
|
||||||
|
bool confirm_set_radio;
|
||||||
};
|
};
|
||||||
} AdminMessage;
|
} AdminMessage;
|
||||||
|
|
||||||
@@ -43,6 +45,8 @@ extern "C" {
|
|||||||
#define AdminMessage_get_radio_response_tag 5
|
#define AdminMessage_get_radio_response_tag 5
|
||||||
#define AdminMessage_get_channel_request_tag 6
|
#define AdminMessage_get_channel_request_tag 6
|
||||||
#define AdminMessage_get_channel_response_tag 7
|
#define AdminMessage_get_channel_response_tag 7
|
||||||
|
#define AdminMessage_confirm_set_channel_tag 32
|
||||||
|
#define AdminMessage_confirm_set_radio_tag 33
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define AdminMessage_FIELDLIST(X, a) \
|
#define AdminMessage_FIELDLIST(X, a) \
|
||||||
@@ -52,7 +56,9 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,set_channel,set_channel), 3) \
|
|||||||
X(a, STATIC, ONEOF, BOOL, (variant,get_radio_request,get_radio_request), 4) \
|
X(a, STATIC, ONEOF, BOOL, (variant,get_radio_request,get_radio_request), 4) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (variant,get_radio_response,get_radio_response), 5) \
|
X(a, STATIC, ONEOF, MESSAGE, (variant,get_radio_response,get_radio_response), 5) \
|
||||||
X(a, STATIC, ONEOF, UINT32, (variant,get_channel_request,get_channel_request), 6) \
|
X(a, STATIC, ONEOF, UINT32, (variant,get_channel_request,get_channel_request), 6) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7)
|
X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7) \
|
||||||
|
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_channel), 32) \
|
||||||
|
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33)
|
||||||
#define AdminMessage_CALLBACK NULL
|
#define AdminMessage_CALLBACK NULL
|
||||||
#define AdminMessage_DEFAULT NULL
|
#define AdminMessage_DEFAULT NULL
|
||||||
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig
|
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig
|
||||||
@@ -67,7 +73,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
|
|||||||
#define AdminMessage_fields &AdminMessage_msg
|
#define AdminMessage_fields &AdminMessage_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define AdminMessage_size 338
|
#define AdminMessage_size 351
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ typedef struct _ChannelSettings {
|
|||||||
} ChannelSettings;
|
} ChannelSettings;
|
||||||
|
|
||||||
typedef struct _Channel {
|
typedef struct _Channel {
|
||||||
uint8_t index;
|
int8_t index;
|
||||||
bool has_settings;
|
bool has_settings;
|
||||||
ChannelSettings settings;
|
ChannelSettings settings;
|
||||||
Channel_Role role;
|
Channel_Role role;
|
||||||
@@ -100,7 +100,7 @@ X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 17)
|
|||||||
#define ChannelSettings_DEFAULT NULL
|
#define ChannelSettings_DEFAULT NULL
|
||||||
|
|
||||||
#define Channel_FIELDLIST(X, a) \
|
#define Channel_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, index, 1) \
|
X(a, STATIC, SINGULAR, INT32, index, 1) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, settings, 2) \
|
X(a, STATIC, OPTIONAL, MESSAGE, settings, 2) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, role, 3)
|
X(a, STATIC, SINGULAR, UENUM, role, 3)
|
||||||
#define Channel_CALLBACK NULL
|
#define Channel_CALLBACK NULL
|
||||||
@@ -116,7 +116,7 @@ extern const pb_msgdesc_t Channel_msg;
|
|||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define ChannelSettings_size 87
|
#define ChannelSettings_size 87
|
||||||
#define Channel_size 94
|
#define Channel_size 102
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -6,7 +6,16 @@
|
|||||||
#error Regenerate this file with the current version of nanopb generator.
|
#error Regenerate this file with the current version of nanopb generator.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PB_BIND(LegacyRadioConfig, LegacyRadioConfig, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(LegacyRadioConfig_LegacyPreferences, LegacyRadioConfig_LegacyPreferences, AUTO)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(DeviceState, DeviceState, 2)
|
PB_BIND(DeviceState, DeviceState, 2)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(ChannelFile, ChannelFile, 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,31 @@
|
|||||||
#define PB_DEVICEONLY_PB_H_INCLUDED
|
#define PB_DEVICEONLY_PB_H_INCLUDED
|
||||||
#include <pb.h>
|
#include <pb.h>
|
||||||
#include "mesh.pb.h"
|
#include "mesh.pb.h"
|
||||||
#include "radioconfig.pb.h"
|
|
||||||
#include "channel.pb.h"
|
#include "channel.pb.h"
|
||||||
|
#include "radioconfig.pb.h"
|
||||||
|
|
||||||
#if PB_PROTO_HEADER_VERSION != 40
|
#if PB_PROTO_HEADER_VERSION != 40
|
||||||
#error Regenerate this file with the current version of nanopb generator.
|
#error Regenerate this file with the current version of nanopb generator.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
|
typedef struct _ChannelFile {
|
||||||
|
pb_size_t channels_count;
|
||||||
|
Channel channels[8];
|
||||||
|
} ChannelFile;
|
||||||
|
|
||||||
|
typedef struct _LegacyRadioConfig_LegacyPreferences {
|
||||||
|
RegionCode region;
|
||||||
|
} LegacyRadioConfig_LegacyPreferences;
|
||||||
|
|
||||||
|
typedef struct _LegacyRadioConfig {
|
||||||
|
bool has_preferences;
|
||||||
|
LegacyRadioConfig_LegacyPreferences preferences;
|
||||||
|
} LegacyRadioConfig;
|
||||||
|
|
||||||
typedef struct _DeviceState {
|
typedef struct _DeviceState {
|
||||||
bool has_radio;
|
bool has_legacyRadio;
|
||||||
RadioConfig radio;
|
LegacyRadioConfig legacyRadio;
|
||||||
bool has_my_node;
|
bool has_my_node;
|
||||||
MyNodeInfo my_node;
|
MyNodeInfo my_node;
|
||||||
bool has_owner;
|
bool has_owner;
|
||||||
@@ -29,8 +43,6 @@ typedef struct _DeviceState {
|
|||||||
uint32_t version;
|
uint32_t version;
|
||||||
bool no_save;
|
bool no_save;
|
||||||
bool did_gps_reset;
|
bool did_gps_reset;
|
||||||
pb_size_t channels_count;
|
|
||||||
Channel channels[8];
|
|
||||||
} DeviceState;
|
} DeviceState;
|
||||||
|
|
||||||
|
|
||||||
@@ -39,11 +51,20 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0, 0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}}
|
#define LegacyRadioConfig_init_default {false, LegacyRadioConfig_LegacyPreferences_init_default}
|
||||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0, 0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}}
|
#define LegacyRadioConfig_LegacyPreferences_init_default {_RegionCode_MIN}
|
||||||
|
#define DeviceState_init_default {false, LegacyRadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
|
||||||
|
#define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}}
|
||||||
|
#define LegacyRadioConfig_init_zero {false, LegacyRadioConfig_LegacyPreferences_init_zero}
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_init_zero {_RegionCode_MIN}
|
||||||
|
#define DeviceState_init_zero {false, LegacyRadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
|
||||||
|
#define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define DeviceState_radio_tag 1
|
#define ChannelFile_channels_tag 1
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_region_tag 15
|
||||||
|
#define LegacyRadioConfig_preferences_tag 1
|
||||||
|
#define DeviceState_legacyRadio_tag 1
|
||||||
#define DeviceState_my_node_tag 2
|
#define DeviceState_my_node_tag 2
|
||||||
#define DeviceState_owner_tag 3
|
#define DeviceState_owner_tag 3
|
||||||
#define DeviceState_node_db_tag 4
|
#define DeviceState_node_db_tag 4
|
||||||
@@ -52,11 +73,21 @@ extern "C" {
|
|||||||
#define DeviceState_version_tag 8
|
#define DeviceState_version_tag 8
|
||||||
#define DeviceState_no_save_tag 9
|
#define DeviceState_no_save_tag 9
|
||||||
#define DeviceState_did_gps_reset_tag 11
|
#define DeviceState_did_gps_reset_tag 11
|
||||||
#define DeviceState_channels_tag 13
|
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
|
#define LegacyRadioConfig_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1)
|
||||||
|
#define LegacyRadioConfig_CALLBACK NULL
|
||||||
|
#define LegacyRadioConfig_DEFAULT NULL
|
||||||
|
#define LegacyRadioConfig_preferences_MSGTYPE LegacyRadioConfig_LegacyPreferences
|
||||||
|
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, region, 15)
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_CALLBACK NULL
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_DEFAULT NULL
|
||||||
|
|
||||||
#define DeviceState_FIELDLIST(X, a) \
|
#define DeviceState_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, radio, 1) \
|
X(a, STATIC, OPTIONAL, MESSAGE, legacyRadio, 1) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||||
@@ -64,25 +95,38 @@ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
|||||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
|
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
|
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
|
||||||
X(a, STATIC, REPEATED, MESSAGE, channels, 13)
|
|
||||||
#define DeviceState_CALLBACK NULL
|
#define DeviceState_CALLBACK NULL
|
||||||
#define DeviceState_DEFAULT NULL
|
#define DeviceState_DEFAULT NULL
|
||||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
#define DeviceState_legacyRadio_MSGTYPE LegacyRadioConfig
|
||||||
#define DeviceState_my_node_MSGTYPE MyNodeInfo
|
#define DeviceState_my_node_MSGTYPE MyNodeInfo
|
||||||
#define DeviceState_owner_MSGTYPE User
|
#define DeviceState_owner_MSGTYPE User
|
||||||
#define DeviceState_node_db_MSGTYPE NodeInfo
|
#define DeviceState_node_db_MSGTYPE NodeInfo
|
||||||
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
||||||
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
||||||
#define DeviceState_channels_MSGTYPE Channel
|
|
||||||
|
|
||||||
|
#define ChannelFile_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, REPEATED, MESSAGE, channels, 1)
|
||||||
|
#define ChannelFile_CALLBACK NULL
|
||||||
|
#define ChannelFile_DEFAULT NULL
|
||||||
|
#define ChannelFile_channels_MSGTYPE Channel
|
||||||
|
|
||||||
|
extern const pb_msgdesc_t LegacyRadioConfig_msg;
|
||||||
|
extern const pb_msgdesc_t LegacyRadioConfig_LegacyPreferences_msg;
|
||||||
extern const pb_msgdesc_t DeviceState_msg;
|
extern const pb_msgdesc_t DeviceState_msg;
|
||||||
|
extern const pb_msgdesc_t ChannelFile_msg;
|
||||||
|
|
||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
|
#define LegacyRadioConfig_fields &LegacyRadioConfig_msg
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_fields &LegacyRadioConfig_LegacyPreferences_msg
|
||||||
#define DeviceState_fields &DeviceState_msg
|
#define DeviceState_fields &DeviceState_msg
|
||||||
|
#define ChannelFile_fields &ChannelFile_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define DeviceState_size 6156
|
#define LegacyRadioConfig_size 4
|
||||||
|
#define LegacyRadioConfig_LegacyPreferences_size 2
|
||||||
|
#define DeviceState_size 5056
|
||||||
|
#define ChannelFile_size 832
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ typedef enum _CriticalErrorCode {
|
|||||||
CriticalErrorCode_UBloxInitFailed = 5,
|
CriticalErrorCode_UBloxInitFailed = 5,
|
||||||
CriticalErrorCode_NoAXP192 = 6,
|
CriticalErrorCode_NoAXP192 = 6,
|
||||||
CriticalErrorCode_InvalidRadioSetting = 7,
|
CriticalErrorCode_InvalidRadioSetting = 7,
|
||||||
CriticalErrorCode_TransmitFailed = 8
|
CriticalErrorCode_TransmitFailed = 8,
|
||||||
|
CriticalErrorCode_Brownout = 9
|
||||||
} CriticalErrorCode;
|
} CriticalErrorCode;
|
||||||
|
|
||||||
typedef enum _Routing_Error {
|
typedef enum _Routing_Error {
|
||||||
@@ -36,7 +37,8 @@ typedef enum _Routing_Error {
|
|||||||
Routing_Error_NO_INTERFACE = 4,
|
Routing_Error_NO_INTERFACE = 4,
|
||||||
Routing_Error_MAX_RETRANSMIT = 5,
|
Routing_Error_MAX_RETRANSMIT = 5,
|
||||||
Routing_Error_NO_CHANNEL = 6,
|
Routing_Error_NO_CHANNEL = 6,
|
||||||
Routing_Error_TOO_LARGE = 7
|
Routing_Error_TOO_LARGE = 7,
|
||||||
|
Routing_Error_NO_RESPONSE = 8
|
||||||
} Routing_Error;
|
} Routing_Error;
|
||||||
|
|
||||||
typedef enum _MeshPacket_Priority {
|
typedef enum _MeshPacket_Priority {
|
||||||
@@ -177,12 +179,12 @@ typedef struct _ToRadio {
|
|||||||
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
|
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
|
||||||
|
|
||||||
#define _CriticalErrorCode_MIN CriticalErrorCode_None
|
#define _CriticalErrorCode_MIN CriticalErrorCode_None
|
||||||
#define _CriticalErrorCode_MAX CriticalErrorCode_TransmitFailed
|
#define _CriticalErrorCode_MAX CriticalErrorCode_Brownout
|
||||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1))
|
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1))
|
||||||
|
|
||||||
#define _Routing_Error_MIN Routing_Error_NONE
|
#define _Routing_Error_MIN Routing_Error_NONE
|
||||||
#define _Routing_Error_MAX Routing_Error_TOO_LARGE
|
#define _Routing_Error_MAX Routing_Error_NO_RESPONSE
|
||||||
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_TOO_LARGE+1))
|
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1))
|
||||||
|
|
||||||
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
|
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
|
||||||
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
|
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ typedef enum _PortNum {
|
|||||||
PortNum_ADMIN_APP = 6,
|
PortNum_ADMIN_APP = 6,
|
||||||
PortNum_REPLY_APP = 32,
|
PortNum_REPLY_APP = 32,
|
||||||
PortNum_IP_TUNNEL_APP = 33,
|
PortNum_IP_TUNNEL_APP = 33,
|
||||||
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 34,
|
|
||||||
PortNum_SERIAL_APP = 64,
|
PortNum_SERIAL_APP = 64,
|
||||||
PortNum_STORE_FORWARD_APP = 65,
|
PortNum_STORE_FORWARD_APP = 65,
|
||||||
PortNum_RANGE_TEST_APP = 66,
|
PortNum_RANGE_TEST_APP = 66,
|
||||||
|
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
|
||||||
PortNum_PRIVATE_APP = 256,
|
PortNum_PRIVATE_APP = 256,
|
||||||
PortNum_ATAK_FORWARDER = 257,
|
PortNum_ATAK_FORWARDER = 257,
|
||||||
PortNum_MAX = 511
|
PortNum_MAX = 511
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ typedef enum _RegionCode {
|
|||||||
RegionCode_JP = 5,
|
RegionCode_JP = 5,
|
||||||
RegionCode_ANZ = 6,
|
RegionCode_ANZ = 6,
|
||||||
RegionCode_KR = 7,
|
RegionCode_KR = 7,
|
||||||
RegionCode_TW = 8
|
RegionCode_TW = 8,
|
||||||
|
RegionCode_RU = 9
|
||||||
} RegionCode;
|
} RegionCode;
|
||||||
|
|
||||||
typedef enum _ChargeCurrent {
|
typedef enum _ChargeCurrent {
|
||||||
@@ -56,6 +57,10 @@ typedef enum _LocationSharing {
|
|||||||
LocationSharing_LocDisabled = 2
|
LocationSharing_LocDisabled = 2
|
||||||
} LocationSharing;
|
} LocationSharing;
|
||||||
|
|
||||||
|
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
|
||||||
|
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0
|
||||||
|
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
typedef struct _RadioConfig_UserPreferences {
|
typedef struct _RadioConfig_UserPreferences {
|
||||||
uint32_t position_broadcast_secs;
|
uint32_t position_broadcast_secs;
|
||||||
@@ -106,6 +111,9 @@ typedef struct _RadioConfig_UserPreferences {
|
|||||||
uint32_t environmental_measurement_plugin_read_error_count_threshold;
|
uint32_t environmental_measurement_plugin_read_error_count_threshold;
|
||||||
uint32_t environmental_measurement_plugin_update_interval;
|
uint32_t environmental_measurement_plugin_update_interval;
|
||||||
uint32_t environmental_measurement_plugin_recovery_interval;
|
uint32_t environmental_measurement_plugin_recovery_interval;
|
||||||
|
bool environmental_measurement_plugin_display_farenheit;
|
||||||
|
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type;
|
||||||
|
uint32_t environmental_measurement_plugin_sensor_pin;
|
||||||
} RadioConfig_UserPreferences;
|
} RadioConfig_UserPreferences;
|
||||||
|
|
||||||
typedef struct _RadioConfig {
|
typedef struct _RadioConfig {
|
||||||
@@ -116,8 +124,8 @@ typedef struct _RadioConfig {
|
|||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
#define _RegionCode_MIN RegionCode_Unset
|
#define _RegionCode_MIN RegionCode_Unset
|
||||||
#define _RegionCode_MAX RegionCode_TW
|
#define _RegionCode_MAX RegionCode_RU
|
||||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
|
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1))
|
||||||
|
|
||||||
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
||||||
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
||||||
@@ -131,6 +139,10 @@ typedef struct _RadioConfig {
|
|||||||
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
||||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
||||||
|
|
||||||
|
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||||
|
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||||
|
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1))
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -138,9 +150,9 @@ extern "C" {
|
|||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
|
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
|
||||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0}
|
||||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
|
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
|
||||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
||||||
@@ -190,6 +202,9 @@ extern "C" {
|
|||||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142
|
||||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143
|
||||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144
|
||||||
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_display_farenheit_tag 145
|
||||||
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146
|
||||||
|
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147
|
||||||
#define RadioConfig_preferences_tag 1
|
#define RadioConfig_preferences_tag 1
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
@@ -246,7 +261,10 @@ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_
|
|||||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \
|
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \
|
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \
|
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144)
|
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_farenheit, 145) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147)
|
||||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||||
|
|
||||||
@@ -258,8 +276,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
|||||||
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define RadioConfig_size 335
|
#define RadioConfig_size 348
|
||||||
#define RadioConfig_UserPreferences_size 332
|
#define RadioConfig_UserPreferences_size 345
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
|
|||||||
|
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
|
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
|
||||||
if (!pb_encode(&stream, fields, src_struct)) {
|
if (!pb_encode(&stream, fields, src_struct)) {
|
||||||
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
DEBUG_MSG("Panic: can't encode protobuf %s, did you make a field too large?\n", PB_GET_ERROR(&stream));
|
||||||
assert(0); // FIXME - panic
|
assert(0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options
|
||||||
} else {
|
} else {
|
||||||
return stream.bytes_written;
|
return stream.bytes_written;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
|
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
|
||||||
|
|
||||||
/// Max number of channels allowed
|
/// Max number of channels allowed
|
||||||
#define MAX_NUM_CHANNELS (member_size(DeviceState, channels) / member_size(DeviceState, channels[0]))
|
#define MAX_NUM_CHANNELS (member_size(ChannelFile, channels) / member_size(ChannelFile, channels[0]))
|
||||||
|
|
||||||
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
|
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
|
||||||
/// returns the encoded packet size
|
/// returns the encoded packet size
|
||||||
|
|||||||
@@ -82,8 +82,34 @@ extern "C" void HardFault_Impl(uint32_t stack[])
|
|||||||
// while (1) ;
|
// while (1) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
// This is a generic cortex M entrypoint that doesn't assume freertos
|
||||||
|
|
||||||
extern "C" void HardFault_Handler(void)
|
extern "C" void HardFault_Handler(void)
|
||||||
{
|
{
|
||||||
asm volatile(" mrs r0,msp\n"
|
asm volatile(" mrs r0,msp\n"
|
||||||
" b HardFault_Impl \n");
|
" b HardFault_Impl \n");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* The prototype shows it is a naked function - in effect this is just an
|
||||||
|
assembly function. */
|
||||||
|
extern "C" void HardFault_Handler( void ) __attribute__( ( naked ) );
|
||||||
|
|
||||||
|
/* The fault handler implementation calls a function called
|
||||||
|
prvGetRegistersFromStack(). */
|
||||||
|
extern "C" void HardFault_Handler(void)
|
||||||
|
{
|
||||||
|
__asm volatile
|
||||||
|
(
|
||||||
|
" tst lr, #4 \n"
|
||||||
|
" ite eq \n"
|
||||||
|
" mrseq r0, msp \n"
|
||||||
|
" mrsne r0, psp \n"
|
||||||
|
" ldr r1, [r0, #24] \n"
|
||||||
|
" ldr r2, handler2_address_const \n"
|
||||||
|
" bx r2 \n"
|
||||||
|
" handler2_address_const: .word HardFault_Impl \n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "NRF52Bluetooth.h"
|
#include "NRF52Bluetooth.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "error.h"
|
||||||
#include "graphics/TFTDisplay.h"
|
#include "graphics/TFTDisplay.h"
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
@@ -51,14 +52,14 @@ void getMacAddr(uint8_t *dmac)
|
|||||||
NRF52Bluetooth *nrf52Bluetooth;
|
NRF52Bluetooth *nrf52Bluetooth;
|
||||||
|
|
||||||
static bool bleOn = false;
|
static bool bleOn = false;
|
||||||
static const bool enableBle = false; // Set to false for easier debugging
|
static const bool useSoftDevice = false; // Set to false for easier debugging
|
||||||
|
|
||||||
void setBluetoothEnable(bool on)
|
void setBluetoothEnable(bool on)
|
||||||
{
|
{
|
||||||
if (on != bleOn) {
|
if (on != bleOn) {
|
||||||
if (on) {
|
if (on) {
|
||||||
if (!nrf52Bluetooth) {
|
if (!nrf52Bluetooth) {
|
||||||
if (!enableBle)
|
if (!useSoftDevice)
|
||||||
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
|
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
|
||||||
else {
|
else {
|
||||||
nrf52Bluetooth = new NRF52Bluetooth();
|
nrf52Bluetooth = new NRF52Bluetooth();
|
||||||
@@ -87,6 +88,49 @@ int printf(const char *fmt, ...)
|
|||||||
|
|
||||||
#include "BQ25713.h"
|
#include "BQ25713.h"
|
||||||
|
|
||||||
|
void initBrownout()
|
||||||
|
{
|
||||||
|
auto vccthresh = POWER_POFCON_THRESHOLD_V28;
|
||||||
|
auto vcchthresh = POWER_POFCON_THRESHOLDVDDH_V27;
|
||||||
|
|
||||||
|
if (useSoftDevice) {
|
||||||
|
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
|
||||||
|
assert(err_code == NRF_SUCCESS);
|
||||||
|
|
||||||
|
err_code = sd_power_pof_threshold_set(vccthresh);
|
||||||
|
assert(err_code == NRF_SUCCESS);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NRF_POWER->POFCON = POWER_POFCON_POF_Msk | (vccthresh << POWER_POFCON_THRESHOLD_Pos) | (vcchthresh << POWER_POFCON_THRESHOLDVDDH_Pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSDEvents()
|
||||||
|
{
|
||||||
|
if (useSoftDevice) {
|
||||||
|
uint32_t evt;
|
||||||
|
while (NRF_ERROR_NOT_FOUND == sd_evt_get(&evt)) {
|
||||||
|
switch (evt) {
|
||||||
|
case NRF_EVT_POWER_FAILURE_WARNING:
|
||||||
|
recordCriticalError(CriticalErrorCode_Brownout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_MSG("Unexpected SDevt %d\n", evt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(NRF_POWER->EVENTS_POFWARN)
|
||||||
|
recordCriticalError(CriticalErrorCode_Brownout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf52Loop()
|
||||||
|
{
|
||||||
|
checkSDEvents();
|
||||||
|
}
|
||||||
|
|
||||||
void nrf52Setup()
|
void nrf52Setup()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -112,6 +156,8 @@ void nrf52Setup()
|
|||||||
// randomSeed(r);
|
// randomSeed(r);
|
||||||
DEBUG_MSG("FIXME, call randomSeed\n");
|
DEBUG_MSG("FIXME, call randomSeed\n");
|
||||||
// ::printf("TESTING PRINTF\n");
|
// ::printf("TESTING PRINTF\n");
|
||||||
|
|
||||||
|
initBrownout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpuDeepSleep(uint64_t msecToWake)
|
void cpuDeepSleep(uint64_t msecToWake)
|
||||||
@@ -128,7 +174,7 @@ void cpuDeepSleep(uint64_t msecToWake)
|
|||||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||||
|
|
||||||
auto ok = sd_power_system_off();
|
auto ok = sd_power_system_off();
|
||||||
if(ok != NRF_SUCCESS) {
|
if (ok != NRF_SUCCESS) {
|
||||||
DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
|
DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
|
||||||
NRF_POWER->SYSTEMOFF = 1;
|
NRF_POWER->SYSTEMOFF = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
AdminPlugin *adminPlugin;
|
AdminPlugin *adminPlugin;
|
||||||
|
|
||||||
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex) {
|
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
|
||||||
if (req.decoded.want_response) {
|
{
|
||||||
|
if (req.decoded.want_response) {
|
||||||
// We create the reply here
|
// We create the reply here
|
||||||
AdminMessage r = AdminMessage_init_default;
|
AdminMessage r = AdminMessage_init_default;
|
||||||
r.get_channel_response = channels.getByIndex(channelIndex);
|
r.get_channel_response = channels.getByIndex(channelIndex);
|
||||||
@@ -23,7 +24,13 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req)
|
|||||||
if (req.decoded.want_response) {
|
if (req.decoded.want_response) {
|
||||||
// We create the reply here
|
// We create the reply here
|
||||||
AdminMessage r = AdminMessage_init_default;
|
AdminMessage r = AdminMessage_init_default;
|
||||||
r.get_radio_response = devicestate.radio;
|
r.get_radio_response = radioConfig;
|
||||||
|
|
||||||
|
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||||
|
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||||
|
// using to the app (so that even old phone apps work with new device loads).
|
||||||
|
r.get_radio_response.preferences.ls_secs = getPref_ls_secs();
|
||||||
|
|
||||||
r.which_variant = AdminMessage_get_radio_response_tag;
|
r.which_variant = AdminMessage_get_radio_response_tag;
|
||||||
reply = allocDataProtobuf(r);
|
reply = allocDataProtobuf(r);
|
||||||
}
|
}
|
||||||
@@ -59,6 +66,8 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages
|
||||||
|
DEBUG_MSG("Ignoring nonrelevant admin %d\n", r->which_variant);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
@@ -89,21 +98,22 @@ void AdminPlugin::handleSetChannel(const Channel &cc)
|
|||||||
{
|
{
|
||||||
channels.setChannel(cc);
|
channels.setChannel(cc);
|
||||||
|
|
||||||
bool didReset = service.reloadConfig();
|
// Just update and save the channels - no need to update the radio for ! primary channel changes
|
||||||
/* FIXME - do we need this still?
|
if (cc.index == 0) {
|
||||||
if (didReset) {
|
// FIXME, this updates the user preferences also, which isn't needed - we really just want to notify on configChanged
|
||||||
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
|
service.reloadConfig();
|
||||||
} */
|
}
|
||||||
|
else {
|
||||||
|
channels.onConfigChanged(); // tell the radios about this change
|
||||||
|
nodeDB.saveChannelsToDisk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdminPlugin::handleSetRadio(const RadioConfig &r)
|
void AdminPlugin::handleSetRadio(const RadioConfig &r)
|
||||||
{
|
{
|
||||||
radioConfig = r;
|
radioConfig = r;
|
||||||
|
|
||||||
bool didReset = service.reloadConfig();
|
service.reloadConfig();
|
||||||
/* FIXME - do we need this still? if (didReset) {
|
|
||||||
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshPacket *AdminPlugin::allocReply()
|
MeshPacket *AdminPlugin::allocReply()
|
||||||
@@ -115,5 +125,6 @@ MeshPacket *AdminPlugin::allocReply()
|
|||||||
|
|
||||||
AdminPlugin::AdminPlugin() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
|
AdminPlugin::AdminPlugin() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
|
||||||
{
|
{
|
||||||
// FIXME, restrict to the admin channel for rx
|
// restrict to the admin channel for rx
|
||||||
|
boundChannel = "admin";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ int32_t ExternalNotificationPlugin::runOnce()
|
|||||||
|
|
||||||
return (INT32_MAX);
|
return (INT32_MAX);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return INT32_MAX;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
|
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
|
|
||||||
if (mp.from != nodeDB.getNodeNum()) {
|
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||||
|
|
||||||
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
|
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
|
||||||
// Need to know if and how this could be a problem.
|
// Need to know if and how this could be a problem.
|
||||||
|
|||||||
@@ -12,16 +12,18 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pp
|
|||||||
{
|
{
|
||||||
auto p = *pptr;
|
auto p = *pptr;
|
||||||
|
|
||||||
nodeDB.updateUser(mp.from, p);
|
nodeDB.updateUser(getFrom(&mp), p);
|
||||||
|
|
||||||
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
||||||
|
|
||||||
// Show new nodes on LCD screen
|
// Show new nodes on LCD screen
|
||||||
if (wasBroadcast) {
|
if (wasBroadcast) {
|
||||||
String lcd = String("Joined: ") + p.long_name + "\n";
|
String lcd = String("Joined: ") + p.long_name + "\n";
|
||||||
screen->print(lcd.c_str());
|
if(screen)
|
||||||
|
screen->print(lcd.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUG_MSG("did handleReceived\n");
|
||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +67,7 @@ int32_t NodeInfoPlugin::runOnce()
|
|||||||
currentGeneration = radioGeneration;
|
currentGeneration = radioGeneration;
|
||||||
|
|
||||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||||
assert(nodeInfoPlugin);
|
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||||
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
|
||||||
|
|
||||||
return getPref_position_broadcast_secs() * 1000;
|
return getPref_position_broadcast_secs() * 1000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
void setupPlugins()
|
void setupPlugins()
|
||||||
{
|
{
|
||||||
routingPlugin = new RoutingPlugin();
|
|
||||||
adminPlugin = new AdminPlugin();
|
adminPlugin = new AdminPlugin();
|
||||||
nodeInfoPlugin = new NodeInfoPlugin();
|
nodeInfoPlugin = new NodeInfoPlugin();
|
||||||
positionPlugin = new PositionPlugin();
|
positionPlugin = new PositionPlugin();
|
||||||
@@ -48,4 +47,7 @@ void setupPlugins()
|
|||||||
// new StoreForwardPlugin();
|
// new StoreForwardPlugin();
|
||||||
new EnvironmentalMeasurementPlugin();
|
new EnvironmentalMeasurementPlugin();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// NOTE! This plugin must be added LAST because it likes to check for replies from other plugins and avoid sending extra acks
|
||||||
|
routingPlugin = new RoutingPlugin();
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
|
|||||||
perhapsSetRTC(RTCQualityFromNet, &tv);
|
perhapsSetRTC(RTCQualityFromNet, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeDB.updatePosition(mp.from, p);
|
nodeDB.updatePosition(getFrom(&mp), p);
|
||||||
|
|
||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ RoutingPlugin *routingPlugin;
|
|||||||
|
|
||||||
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
|
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Routing sniffing", &mp);
|
printPacket("Routing sniffing", &mp);
|
||||||
router->sniffReceived(&mp, r);
|
router->sniffReceived(&mp, r);
|
||||||
|
|
||||||
// FIXME - move this to a non promsicious PhoneAPI plugin?
|
// FIXME - move this to a non promsicious PhoneAPI plugin?
|
||||||
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
|
// Note: we are careful not to send back packets that started with the phone back to the phone
|
||||||
|
if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) && (mp.from != 0)) {
|
||||||
printPacket("Delivering rx packet", &mp);
|
printPacket("Delivering rx packet", &mp);
|
||||||
service.handleFromRadio(&mp);
|
service.handleFromRadio(&mp);
|
||||||
}
|
}
|
||||||
@@ -34,7 +35,7 @@ MeshPacket *RoutingPlugin::allocReply()
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom)
|
void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
|
||||||
{
|
{
|
||||||
Routing c = Routing_init_default;
|
Routing c = Routing_init_default;
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom)
|
|||||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||||
p->to = to;
|
p->to = to;
|
||||||
p->decoded.request_id = idFrom;
|
p->decoded.request_id = idFrom;
|
||||||
|
p->channel = chIndex;
|
||||||
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
||||||
|
|
||||||
router->sendLocal(p); // we sometimes send directly to the local node
|
router->sendLocal(p); // we sometimes send directly to the local node
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "ProtobufPlugin.h"
|
#include "ProtobufPlugin.h"
|
||||||
|
#include "Channels.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing plugin for router control messages
|
* Routing plugin for router control messages
|
||||||
@@ -12,6 +13,8 @@ class RoutingPlugin : public ProtobufPlugin<Routing>
|
|||||||
*/
|
*/
|
||||||
RoutingPlugin();
|
RoutingPlugin();
|
||||||
|
|
||||||
|
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Router;
|
friend class Router;
|
||||||
|
|
||||||
@@ -27,8 +30,6 @@ class RoutingPlugin : public ProtobufPlugin<Routing>
|
|||||||
|
|
||||||
/// Override wantPacket to say we want to see all packets, not just those for our port number
|
/// Override wantPacket to say we want to see all packets, not just those for our port number
|
||||||
virtual bool wantPacket(const MeshPacket *p) { return true; }
|
virtual bool wantPacket(const MeshPacket *p) { return true; }
|
||||||
|
|
||||||
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RoutingPlugin *routingPlugin;
|
extern RoutingPlugin *routingPlugin;
|
||||||
@@ -124,7 +124,8 @@ int32_t SerialPlugin::runOnce()
|
|||||||
|
|
||||||
return (INT32_MAX);
|
return (INT32_MAX);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return INT32_MAX;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ bool SerialPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||||
|
|
||||||
if (mp.from == nodeDB.getNodeNum()) {
|
if (getFrom(&mp) == nodeDB.getNodeNum()) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
|
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ TextMessagePlugin *textMessagePlugin;
|
|||||||
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
|
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
|
||||||
{
|
{
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
DEBUG_MSG("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
||||||
|
|
||||||
// We only store/display messages destined for us.
|
// We only store/display messages destined for us.
|
||||||
// Keep a copy of the most recent text message.
|
// Keep a copy of the most recent text message.
|
||||||
|
|||||||
@@ -10,20 +10,10 @@
|
|||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
|
||||||
EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin;
|
|
||||||
EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio;
|
|
||||||
|
|
||||||
EnvironmentalMeasurementPlugin::EnvironmentalMeasurementPlugin() : concurrency::OSThread("EnvironmentalMeasurementPlugin") {}
|
|
||||||
|
|
||||||
uint32_t sensor_read_error_count = 0;
|
|
||||||
|
|
||||||
#define DHT_11_GPIO_PIN 13
|
|
||||||
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts
|
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts
|
||||||
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
||||||
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||||
|
|
||||||
DHT dht(DHT_11_GPIO_PIN,DHT11);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAS_EINK
|
#ifdef HAS_EINK
|
||||||
// The screen is bigger so use bigger fonts
|
// The screen is bigger so use bigger fonts
|
||||||
@@ -49,11 +39,15 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
Uncomment the preferences below if you want to use the plugin
|
Uncomment the preferences below if you want to use the plugin
|
||||||
without having to configure it from the PythonAPI or WebUI.
|
without having to configure it from the PythonAPI or WebUI.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
|
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1;
|
radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5;
|
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_update_interval = 30;
|
radioConfig.preferences.environmental_measurement_plugin_update_interval = 30;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 600;*/
|
radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60;
|
||||||
|
radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true;
|
||||||
|
radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13;
|
||||||
|
radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/
|
||||||
|
|
||||||
if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
||||||
// If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
// If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
||||||
@@ -62,20 +56,32 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
|
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
// This is the first time the OSThread library has called this function, so do some setup
|
// This is the first time the OSThread library has called this function, so do some setup
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
|
|
||||||
environmentalMeasurementPluginRadio = new EnvironmentalMeasurementPluginRadio();
|
|
||||||
firstTime = 0;
|
firstTime = 0;
|
||||||
// begin reading measurements from the sensor
|
|
||||||
// DHT have a max read-rate of 1HZ, so we should wait at least 1 second
|
|
||||||
// after initializing the sensor before we try to read from it.
|
|
||||||
// returning the interval here means that the next time OSThread
|
|
||||||
// calls our plugin, we'll run the other branch of this if statement
|
|
||||||
// and actually do a "sendOurEnvironmentalMeasurement()"
|
|
||||||
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
||||||
{
|
{
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
|
||||||
// it's possible to have this plugin enabled, only for displaying values on the screen.
|
// it's possible to have this plugin enabled, only for displaying values on the screen.
|
||||||
// therefore, we should only enable the sensor loop if measurement is also enabled
|
// therefore, we should only enable the sensor loop if measurement is also enabled
|
||||||
dht.begin();
|
switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
|
||||||
|
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
|
||||||
|
dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11);
|
||||||
|
this->dht->begin();
|
||||||
|
this->dht->read();
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
|
||||||
|
return (INT32_MAX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// begin reading measurements from the sensor
|
||||||
|
// DHT have a max read-rate of 1HZ, so we should wait at least 1 second
|
||||||
|
// after initializing the sensor before we try to read from it.
|
||||||
|
// returning the interval here means that the next time OSThread
|
||||||
|
// calls our plugin, we'll run the other branch of this if statement
|
||||||
|
// and actually do a "sendOurEnvironmentalMeasurement()"
|
||||||
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||||
}
|
}
|
||||||
return (INT32_MAX);
|
return (INT32_MAX);
|
||||||
@@ -96,6 +102,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
"EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n",
|
"EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n",
|
||||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
|
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
|
||||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
||||||
|
sensor_read_error_count = 0;
|
||||||
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
||||||
}
|
}
|
||||||
DEBUG_MSG(
|
DEBUG_MSG(
|
||||||
@@ -110,7 +117,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
sensor_read_error_count,
|
sensor_read_error_count,
|
||||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count);
|
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count);
|
||||||
}
|
}
|
||||||
if (! environmentalMeasurementPluginRadio->sendOurEnvironmentalMeasurement() ){
|
if (!sendOurEnvironmentalMeasurement() ){
|
||||||
// if we failed to read the sensor, then try again
|
// if we failed to read the sensor, then try again
|
||||||
// as soon as we can according to the maximum polling frequency
|
// as soon as we can according to the maximum polling frequency
|
||||||
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||||
@@ -123,25 +130,16 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentalMeasurementPluginRadio::wantUIFrame() {
|
bool EnvironmentalMeasurementPlugin::wantUIFrame() {
|
||||||
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
|
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvironmentalMeasurementPluginRadio::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
||||||
{
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
display->setFont(FONT_MEDIUM);
|
|
||||||
display->drawString(x, y, "Environment");
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": T:"+ String(lastMeasurement.temperature,2) + " H:" + String(lastMeasurement.relative_humidity,2));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
String GetSenderName(const MeshPacket &mp) {
|
String GetSenderName(const MeshPacket &mp) {
|
||||||
String sender;
|
String sender;
|
||||||
|
|
||||||
if (nodeDB.getNode(mp.from)){
|
auto node = nodeDB.getNode(getFrom(&mp));
|
||||||
sender = nodeDB.getNode(mp.from)->user.short_name;
|
if (node){
|
||||||
|
sender = node->user.short_name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sender = "UNK";
|
sender = "UNK";
|
||||||
@@ -149,55 +147,99 @@ String GetSenderName(const MeshPacket &mp) {
|
|||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentalMeasurementPluginRadio::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *pptr)
|
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) {
|
||||||
|
uint32_t now = getTime();
|
||||||
|
|
||||||
|
uint32_t last_seen = mp->rx_time;
|
||||||
|
int delta = (int)(now - last_seen);
|
||||||
|
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
||||||
|
delta = 0;
|
||||||
|
|
||||||
|
return delta;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) {
|
||||||
|
return (c*9)/5 + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
const EnvironmentalMeasurement &p = *pptr;
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
display->setFont(FONT_MEDIUM);
|
||||||
|
display->drawString(x, y, "Environment");
|
||||||
|
if (lastMeasurementPacket == nullptr) {
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
|
||||||
|
//DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnvironmentalMeasurement lastMeasurement;
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
|
||||||
|
String lastSender = GetSenderName(*lastMeasurementPacket);
|
||||||
|
|
||||||
|
auto &p = lastMeasurementPacket->decoded;
|
||||||
|
if (!pb_decode_from_bytes(p.payload.bytes,
|
||||||
|
p.payload.size,
|
||||||
|
EnvironmentalMeasurement_fields,
|
||||||
|
&lastMeasurement)) {
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
String last_temp = String(lastMeasurement.temperature,0) +"°C";
|
||||||
|
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){
|
||||||
|
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";;
|
||||||
|
}
|
||||||
|
|
||||||
|
display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p)
|
||||||
|
{
|
||||||
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
||||||
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
|
||||||
|
|
||||||
String sender = GetSenderName(mp);
|
String sender = GetSenderName(mp);
|
||||||
|
|
||||||
// Show new nodes on LCD screen
|
|
||||||
if (DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN && wasBroadcast) {
|
|
||||||
String lcd = String("Env Measured: ") +sender + "\n" +
|
|
||||||
"T: " + p.temperature + "\n" +
|
|
||||||
"H: " + p.relative_humidity + "\n";
|
|
||||||
screen->print(lcd.c_str());
|
|
||||||
}
|
|
||||||
DEBUG_MSG("-----------------------------------------\n");
|
|
||||||
|
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender);
|
DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender);
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p.relative_humidity);
|
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity);
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature);
|
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p->temperature);
|
||||||
|
|
||||||
|
lastMeasurementPacket = packetPool.allocCopy(mp);
|
||||||
|
|
||||||
lastMeasurement = p;
|
|
||||||
lastSender = sender;
|
|
||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentalMeasurementPluginRadio::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
|
bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
|
||||||
{
|
{
|
||||||
EnvironmentalMeasurement m;
|
EnvironmentalMeasurement m;
|
||||||
|
|
||||||
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
|
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
|
||||||
m.relative_humidity = dht.readHumidity();
|
|
||||||
m.temperature = dht.readTemperature();;
|
|
||||||
|
|
||||||
DEBUG_MSG("-----------------------------------------\n");
|
DEBUG_MSG("-----------------------------------------\n");
|
||||||
|
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
|
if (!this->dht->read(true)){
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
|
|
||||||
|
|
||||||
if (isnan(m.relative_humidity) || isnan(m.temperature) ){
|
|
||||||
sensor_read_error_count++;
|
sensor_read_error_count++;
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
m.relative_humidity = this->dht->readHumidity();
|
||||||
|
m.temperature = this->dht->readTemperature();
|
||||||
|
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
|
||||||
|
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
|
||||||
|
|
||||||
sensor_read_error_count = 0;
|
sensor_read_error_count = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -3,61 +3,32 @@
|
|||||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
#include <DHT.h>
|
||||||
|
|
||||||
|
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
|
||||||
class EnvironmentalMeasurementPlugin : private concurrency::OSThread
|
|
||||||
{
|
{
|
||||||
bool firstTime = 1;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EnvironmentalMeasurementPlugin();
|
EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) {
|
||||||
|
lastMeasurementPacket = nullptr;
|
||||||
|
}
|
||||||
|
virtual bool wantUIFrame();
|
||||||
|
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/** Called to handle a particular incoming message
|
||||||
|
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||||
|
*/
|
||||||
|
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p);
|
||||||
virtual int32_t runOnce();
|
virtual int32_t runOnce();
|
||||||
};
|
|
||||||
|
|
||||||
extern EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EnvironmentalMeasurementPluginRadio plugin for sending/receiving environmental measurements to/from the mesh
|
|
||||||
*/
|
|
||||||
class EnvironmentalMeasurementPluginRadio : public ProtobufPlugin<EnvironmentalMeasurement>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/** Constructor
|
|
||||||
* name is for debugging output
|
|
||||||
*/
|
|
||||||
EnvironmentalMeasurementPluginRadio() : ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) {
|
|
||||||
lastMeasurement.barometric_pressure = nanf("");
|
|
||||||
lastMeasurement.relative_humidity = nanf("");
|
|
||||||
lastMeasurement.temperature = nanf("");
|
|
||||||
lastSender = "N/A";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send our EnvironmentalMeasurement into the mesh
|
* Send our EnvironmentalMeasurement into the mesh
|
||||||
*/
|
*/
|
||||||
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
|
|
||||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/** Called to handle a particular incoming message
|
|
||||||
|
|
||||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
|
||||||
*/
|
|
||||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p);
|
|
||||||
|
|
||||||
virtual bool wantUIFrame();
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
float CelsiusToFarenheit(float c);
|
||||||
EnvironmentalMeasurement lastMeasurement;
|
bool firstTime = 1;
|
||||||
|
DHT* dht;
|
||||||
String lastSender;
|
const MeshPacket *lastMeasurementPacket;
|
||||||
|
uint32_t sensor_read_error_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio;
|
|
||||||
@@ -133,7 +133,7 @@ bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||||
|
|
||||||
if (mp.from != nodeDB.getNodeNum()) {
|
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||||
|
|
||||||
// DEBUG_MSG("* * Message came from the mesh\n");
|
// DEBUG_MSG("* * Message came from the mesh\n");
|
||||||
// Serial2.println("* * Message came from the mesh");
|
// Serial2.println("* * Message came from the mesh");
|
||||||
@@ -144,7 +144,7 @@ bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
NodeInfo *n = nodeDB.getNode(mp.from);
|
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||||
|
|
||||||
if (radioConfig.preferences.range_test_plugin_save) {
|
if (radioConfig.preferences.range_test_plugin_save) {
|
||||||
appendFile(mp);
|
appendFile(mp);
|
||||||
@@ -209,7 +209,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
|||||||
{
|
{
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
|
|
||||||
NodeInfo *n = nodeDB.getNode(mp.from);
|
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||||
/*
|
/*
|
||||||
DEBUG_MSG("-----------------------------------------\n");
|
DEBUG_MSG("-----------------------------------------\n");
|
||||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||||
@@ -290,7 +290,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
|||||||
fileToAppend.printf("??:??:??,"); // Time
|
fileToAppend.printf("??:??:??,"); // Time
|
||||||
}
|
}
|
||||||
|
|
||||||
fileToAppend.printf("%d,", mp.from); // From
|
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||||
fileToAppend.printf("%s,", n->user.long_name); // Long Name
|
fileToAppend.printf("%s,", n->user.long_name); // Long Name
|
||||||
fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat
|
fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat
|
||||||
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
||||||
|
|||||||
@@ -26,26 +26,23 @@ int32_t StoreForwardPlugin::runOnce()
|
|||||||
without having to configure it from the PythonAPI or WebUI.
|
without having to configure it from the PythonAPI or WebUI.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// radioConfig.preferences.store_forward_plugin_enabled = 1;
|
radioConfig.preferences.store_forward_plugin_enabled = 1;
|
||||||
// radioConfig.preferences.is_router = 1;
|
radioConfig.preferences.is_router = 0;
|
||||||
|
|
||||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||||
|
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
|
|
||||||
/*
|
firstTime = 0;
|
||||||
*/
|
|
||||||
|
|
||||||
if (radioConfig.preferences.is_router) {
|
if (radioConfig.preferences.is_router) {
|
||||||
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled\n");
|
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Router\n");
|
||||||
// Router
|
// Router
|
||||||
if (ESP.getPsramSize()) {
|
if (ESP.getPsramSize()) {
|
||||||
if (ESP.getFreePsram() >= 2048 * 1024) {
|
if (ESP.getFreePsram() >= 2048 * 1024) {
|
||||||
// Do the startup here
|
// Do the startup here
|
||||||
storeForwardPluginRadio = new StoreForwardPluginRadio();
|
storeForwardPluginRadio = new StoreForwardPluginRadio();
|
||||||
|
|
||||||
firstTime = 0;
|
|
||||||
|
|
||||||
this->populatePSRAM();
|
this->populatePSRAM();
|
||||||
|
|
||||||
// packetHistory[0].bytes;
|
// packetHistory[0].bytes;
|
||||||
@@ -66,21 +63,31 @@ int32_t StoreForwardPlugin::runOnce()
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled but is_router is not turned on.\n");
|
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Client\n");
|
||||||
DEBUG_MSG(
|
return (5 * 1000);
|
||||||
"Initializing Store & Forward Plugin - If you want to use this plugin, you must also turn on is_router.\n");
|
|
||||||
// Non-Router
|
|
||||||
|
|
||||||
return (30 * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// What do we do if it's not our first time?
|
|
||||||
|
|
||||||
// Maybe some cleanup functions?
|
if (radioConfig.preferences.is_router) {
|
||||||
this->sawNodeReport();
|
// Maybe some cleanup functions?
|
||||||
this->historyReport();
|
this->sawNodeReport();
|
||||||
return (10 * 1000);
|
this->historyReport();
|
||||||
|
return (10 * 1000);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
|
||||||
|
* few minutes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUG_MSG("Store & Forward Plugin - Sending heartbeat\n");
|
||||||
|
|
||||||
|
// storeForwardPluginRadio->sendPayloadHeartbeat();
|
||||||
|
if(storeForwardPluginRadio)
|
||||||
|
storeForwardPluginRadio->sendPayload();
|
||||||
|
|
||||||
|
return (1 * 60 * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -220,15 +227,32 @@ void StoreForwardPlugin::sawNodeReport()
|
|||||||
|
|
||||||
MeshPacket *StoreForwardPluginRadio::allocReply()
|
MeshPacket *StoreForwardPluginRadio::allocReply()
|
||||||
{
|
{
|
||||||
|
//auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
//return reply;
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StoreForwardPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
void StoreForwardPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||||
{
|
{
|
||||||
MeshPacket *p = allocReply();
|
MeshPacket *p = this->allocReply();
|
||||||
|
/*
|
||||||
|
p->to = dest;
|
||||||
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
|
p->want_ack = true;
|
||||||
|
*/
|
||||||
|
// static char heartbeatString[20];
|
||||||
|
// snprintf(heartbeatString, sizeof(heartbeatString), "1");
|
||||||
|
|
||||||
|
// p->decoded.data.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
|
||||||
|
// memcpy(p->decoded.data.payload.bytes, "1", 1);
|
||||||
|
|
||||||
|
// service.sendToMesh(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreForwardPluginRadio::sendPayloadHeartbeat(NodeNum dest, bool wantReplies)
|
||||||
|
{
|
||||||
|
DEBUG_MSG("Sending S&F Heartbeat\n");
|
||||||
|
MeshPacket *p = this->allocReply();
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
@@ -240,12 +264,12 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||||
|
|
||||||
if (mp.from != nodeDB.getNodeNum()) {
|
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||||
// DEBUG_MSG("Store & Forward Plugin -- Print Start ---------- ---------- ---------- ---------- ----------\n\n\n");
|
// DEBUG_MSG("Store & Forward Plugin -- Print Start ---------- ---------- ---------- ---------- ----------\n\n\n");
|
||||||
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
||||||
// p->want_ack, p->hop_limit);
|
// p->want_ack, p->hop_limit);
|
||||||
printPacket("----- PACKET FROM RADIO -----", &mp);
|
printPacket("----- PACKET FROM RADIO -----", &mp);
|
||||||
uint32_t sawTime = storeForwardPlugin->sawNode(mp.from & 0xffffffff);
|
uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff);
|
||||||
DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
|
DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
|
||||||
|
|
||||||
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) {
|
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) {
|
||||||
@@ -282,7 +306,7 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((millis() - sawTime) > STOREFORWARD_SEND_HISTORY_SHORT) {
|
if ((millis() - sawTime) > STOREFORWARD_SEND_HISTORY_SHORT) {
|
||||||
// Node has been away for a while.
|
// Node has been away for a while.
|
||||||
storeForwardPlugin->historySend(sawTime, mp.from);
|
storeForwardPlugin->historySend(sawTime, mp.from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ class StoreForwardPluginRadio : public SinglePortPlugin
|
|||||||
*/
|
*/
|
||||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send our payload into the mesh
|
||||||
|
*/
|
||||||
|
void sendPayloadHeartbeat(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual MeshPacket *allocReply();
|
virtual MeshPacket *allocReply();
|
||||||
|
|
||||||
|
|||||||
@@ -99,16 +99,16 @@ extern "C" {
|
|||||||
#define NUM_ANALOG_OUTPUTS (0)
|
#define NUM_ANALOG_OUTPUTS (0)
|
||||||
|
|
||||||
// LEDs
|
// LEDs
|
||||||
#define PIN_LED1 (0 + 13) // red (confirmed on 1.0 board)
|
#define PIN_LED1 (0 + 14) // 13 red (confirmed on 1.0 board)
|
||||||
#define PIN_LED2 (0 + 14) // blue (seems busted!)
|
#define PIN_LED2 (0 + 15) // 14 blue
|
||||||
#define PIN_LED3 (0 + 15) // green (seems busted!)
|
#define PIN_LED3 (0 + 13) // 15 green
|
||||||
|
|
||||||
#define LED_RED PIN_LED3
|
#define LED_RED PIN_LED3
|
||||||
#define LED_GREEN PIN_LED1
|
#define LED_BLUE PIN_LED1
|
||||||
#define LED_BLUE PIN_LED2
|
#define LED_GREEN PIN_LED2
|
||||||
|
|
||||||
#define LED_BUILTIN LED_GREEN
|
#define LED_BUILTIN LED_BLUE
|
||||||
#define LED_CONN PIN_BLUE
|
#define LED_CONN PIN_GREEN
|
||||||
|
|
||||||
#define LED_STATE_ON 0 // State when LED is lit
|
#define LED_STATE_ON 0 // State when LED is lit
|
||||||
#define LED_INVERTED 1
|
#define LED_INVERTED 1
|
||||||
@@ -192,6 +192,9 @@ External serial flash WP25R1635FZUIL0
|
|||||||
|
|
||||||
// #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
|
// #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
|
||||||
|
|
||||||
|
// #undef SX1262_CS
|
||||||
|
// #define USE_SIM_RADIO // define to not use the lora radio hardware at all
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* eink display pins
|
* eink display pins
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 1
|
major = 1
|
||||||
minor = 2
|
minor = 2
|
||||||
build = 1
|
build = 9
|
||||||
|
|||||||
Reference in New Issue
Block a user