Compare commits

..

81 Commits

Author SHA1 Message Date
Kevin Hester
8f5251583f Merge pull request #738 from geeksville/eink
Eink
2021-03-13 09:00:57 +08:00
Kevin Hester
c2122a6859 Merge remote-tracking branch 'root/master' into eink 2021-03-13 08:55:06 +08:00
Kevin Hester
6dd65adebd 1.2.10 2021-03-13 08:54:02 +08:00
Kevin Hester
c227143b53 @mc-hamster, I think storeandforward was accidentally wrong (no worries though) 2021-03-13 08:41:50 +08:00
Kevin Hester
cdd696c1ff todo updates 2021-03-13 08:30:07 +08:00
Kevin Hester
3e6817cd18 fix board reboot due to forwarding packets we don't have keys for 2021-03-13 08:29:58 +08:00
Kevin Hester
a5ed607261 fix memory corruption in storeandforward 2021-03-13 08:29:32 +08:00
Kevin Hester
7118200885 Merge pull request #737 from geeksville/eink
Eink
2021-03-12 20:47:48 +08:00
Kevin Hester
b7f9064f0d turn off linux on CI for now 2021-03-12 20:42:22 +08:00
Kevin Hester
5dc5bce1b2 cleanup memtest 2021-03-12 20:41:18 +08:00
Kevin Hester
bc7fef1d1a 1.2.9 2021-03-12 20:22:45 +08:00
Kevin Hester
1908d131ca turn on access control for admin plugin, only allow remote access over special channel 2021-03-12 20:22:40 +08:00
Kevin Hester
8cd2a00a25 fix nasty bug 2021-03-12 15:47:00 +08:00
Kevin Hester
c097852ab0 SERIOUS BUG: we've been discarding devicestate when we should not 2021-03-12 15:45:28 +08:00
Kevin Hester
b02212009a progress on remote settings 2021-03-12 14:10:36 +08:00
Kevin Hester
9d1971f0fa Merge remote-tracking branch 'root/master' into eink 2021-03-11 18:54:08 +08:00
Kevin Hester
2d6261703a fix null pointer exception in storeandforwardplugin. if null the vtable is busted
cc @mc-hamster.  In some cases storeForwardPluginRadio can be null ;-)

~/development/meshtastic/meshtastic-esp32$ bin/exception_decoder.py -e .pio/build/tbeam/firmware.elf ex
stack:
0x401db467: StoreForwardPluginRadio::sendPayload(unsigned int, bool) at /home/kevinh/development/meshtastic/meshtastic-esp32/src/plugins/esp32/StoreForwardPlugin.cpp:235
0x400e7cbd: StoreForwardPlugin::runOnce() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/plugins/esp32/StoreForwardPlugin.cpp:225
0x400d4cca: concurrency::OSThread::run() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/concurrency/OSThread.cpp:45
0x400f015d: ThreadController::runOrDelay() at /home/kevinh/development/meshtastic/meshtastic-esp32/.pio/libdeps/tbeam/Thread/ThreadController.cpp:153
0x400da070: loop() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/main.cpp:621
0x400ff709: loopTask(void*) at /home/kevinh/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:19

Signed-off-by: Kevin Hester <kevinh@geeksville.com>
2021-03-11 18:53:17 +08:00
Kevin Hester
a97c2ae6eb minimize radioconfig file writes 2021-03-11 18:29:47 +08:00
Kevin Hester
76e2c39c63 completed moving prefs to new files 2021-03-11 17:54:16 +08:00
Kevin Hester
ab9fe42f58 wip - move channels 2021-03-11 13:02:00 +08:00
Kevin Hester
959b540c02 begin restricting admin ops to the admin channel 2021-03-11 10:01:57 +08:00
Kevin Hester
68781492ad don't block on segger console 2021-03-11 10:00:08 +08:00
Kevin Hester
590e147186 todo updates 2021-03-11 09:59:54 +08:00
Kevin Hester
0b358674ff Merge pull request #736 from geeksville/eink
Eink
2021-03-10 15:59:32 +08:00
Kevin Hester
0df01f2586 Merge remote-tracking branch 'root/master' into eink 2021-03-10 15:30:51 +08:00
Kevin Hester
ca23665463 Merge pull request #735 from meshtastic/sachaw-patch-1
Update main.yml
2021-03-10 15:30:40 +08:00
Kevin Hester
f55ac8e9c9 turn off (buggy) custom_fields in tinygps, and used fixed version of lib 2021-03-10 15:29:25 +08:00
Kevin Hester
6e37fe6343 turn radio back on 2021-03-10 15:24:00 +08:00
Kevin Hester
217bd934d7 fix GPS fixme wrt deletion 2021-03-10 15:21:54 +08:00
Kevin Hester
58715f454c add consolePrintf for C style code 2021-03-10 15:21:30 +08:00
Kevin Hester
772f2a15ff check more error codes 2021-03-09 16:45:40 +08:00
Kevin Hester
5b0d8381b9 fxi log formatting 2021-03-09 15:07:26 +08:00
Kevin Hester
d841d86bbc brownout detect 2021-03-09 15:07:16 +08:00
Kevin Hester
ecaae87b79 make screen optional 2021-03-09 15:07:02 +08:00
Kevin Hester
5835abbcf6 fix eink leds 2021-03-09 15:06:28 +08:00
Sacha Weatherstone
2f7c2a2aea Update main.yml 2021-03-09 16:16:41 +11:00
Kevin Hester
87ec7b09aa add crude check for brownout 2021-03-08 18:12:21 +08:00
Kevin Hester
f8ec072093 make debug console stall until host has downloaded 2021-03-08 17:10:48 +08:00
Kevin Hester
781d2f0ad6 fix warnings 2021-03-08 17:09:35 +08:00
Kevin Hester
7bbd2c0e80 make textmessageplugin optional 2021-03-08 15:30:08 +08:00
Jm Casler
315cfe4f2d Merge branch 'master' into master 2021-03-07 23:25:43 -08:00
Kevin Hester
707ed75138 fix warnings found in nrf52 build 2021-03-08 15:20:43 +08:00
Kevin Hester
c0e180759d improve debugging on cortex m 2021-03-08 15:20:29 +08:00
Kevin Hester
6ceb423033 TODO update 2021-03-08 12:03:04 +08:00
Kevin Hester
96c4286e7d Merge pull request #733 from geeksville/dev1.2
1.2.6
2021-03-07 10:00:00 +08:00
Kevin Hester
f320ecbde8 1.2.6 2021-03-07 09:51:51 +08:00
Kevin Hester
d014ae0bff fix ls_sleeps communication to device clients 2021-03-07 09:51:17 +08:00
Kevin Hester
12a7934ca1 add RU 2021-03-07 09:34:35 +08:00
Kevin Hester
64bc791e48 fix docs 2021-03-07 09:34:29 +08:00
Kevin Hester
1f33506962 Add RU region 2021-03-06 21:10:36 +08:00
Kevin Hester
ba9a94d026 fix is_low_power detection 2021-03-06 18:00:20 +08:00
Kevin Hester
6f13966d19 fix missing acks for broadcasts 2021-03-06 17:48:35 +08:00
Kevin Hester
96cfad4e57 less logspam 2021-03-06 14:52:26 +08:00
Kevin Hester
a26ebb1b69 Merge pull request #732 from geeksville/dev1.2
Dev1.2 - fix lora message rx
2021-03-06 14:31:59 +08:00
Kevin Hester
7a764efc10 1.2.5 2021-03-06 14:30:53 +08:00
Kevin Hester
49b1f4c5af oops - fix failed text message rx 2021-03-06 14:21:20 +08:00
Kevin Hester
fbe56531d2 Merge remote-tracking branch 'root/master' into dev1.2 2021-03-06 11:20:08 +08:00
Kevin Hester
aa6b29a4b5 fix from address on naks 2021-03-06 11:19:52 +08:00
Kevin Hester
c88b9732eb REALLY IMPORTANT: fix bug with retransmissions not happening 2021-03-06 11:13:33 +08:00
Kevin Hester
2c29e8b179 make nodeinfo & position plugins optional 2021-03-06 10:36:30 +08:00
Kevin Hester
d2d6b8e12f fix log formatting 2021-03-06 10:27:48 +08:00
Kevin Hester
badfaa8545 make error message clearer for packets that are too big 2021-03-06 10:27:31 +08:00
Kevin Hester
c5b67d821d Merge pull request #730 from geeksville/dev1.2
1.2.4
2021-03-05 13:46:18 +08:00
Kevin Hester
63bf7a29f3 Merge branch 'master' into dev1.2 2021-03-05 13:46:10 +08:00
Kevin Hester
845dd1f9e3 1.2.4 2021-03-05 12:39:39 +08:00
Kevin Hester
c9c44a934d don't generate acks for locally sourced msgs 2021-03-05 12:39:31 +08:00
Kevin Hester
2f6981a27f Merge pull request #729 from geeksville/dev1.2
important fixes for 1.2
2021-03-05 12:07:27 +08:00
Kevin Hester
8739469db3 oops typo 2021-03-05 11:49:37 +08:00
Kevin Hester
0c0c0babba combine acks works 2021-03-05 11:44:45 +08:00
Kevin Hester
950b32232f don't send messages the phone sent us back towards the phone 2021-03-05 10:19:27 +08:00
Kevin Hester
2f6034b067 update todos 2021-03-04 22:10:09 +08:00
Kevin Hester
159f7622e4 Merge pull request #724 from crossan007/improvements_to_env_plugin
Improvements to Environmental Measurement Plugin
2021-03-04 22:00:25 +08:00
Kevin Hester
2cc2fa906a Merge branch 'master' into improvements_to_env_plugin 2021-03-04 20:06:27 +08:00
Charles Crossan
1b71a0f436 refactor ENV to single multi-inheritance class with cleaner user preferences
Fix merge issues

don't log when drawing blank frames

remove useless logging

re-comment stuff

fix comment

unused var
2021-03-03 20:30:20 -05:00
Charles Crossan
2af9e1431e Improvements to EnvironmentalMeasurementPlugin
Instead of holding onto only the last measurement, hold onto a copy of the last MeshPacket containing a measurement

This will make it easier to display the last time received

make DHT sensor reads more reliable

user preference for Farenheit vs Celsius
2021-03-03 08:23:03 -05:00
Jm
ee961d01ed Merge remote-tracking branch 'origin/master' 2021-03-02 20:50:13 -08:00
Jm Casler
a3343bc1af Revert "Merge pull request #73 from meshtastic/master"
This reverts commit ee04d57a7f.
2021-03-02 20:48:34 -08:00
Jm Casler
ee04d57a7f Merge pull request #73 from meshtastic/master
pulling 1.2 into my fork
2021-03-02 20:46:21 -08:00
Jm
2cf704abe0 Crashes after 7 seconds. 2021-02-28 19:35:00 -08:00
Jm
ef32ac5cd4 Update rangetest docs with api example 2021-02-28 11:55:54 -08:00
Jm Casler
52d85c9a41 Partial work from laptop -- non-routers can send a heartbeat 2021-02-27 22:34:53 -08:00
70 changed files with 1407 additions and 513 deletions

View File

@@ -7,11 +7,12 @@ jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Checkout submodules
uses: textbook/git-checkout-submodule-action@master
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: true
- name: Setup Python
uses: actions/setup-python@master
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Install Platform IO
@@ -30,5 +31,6 @@ jobs:
run: platformio run -e heltec
- name: Build for lora-relay-v1
run: platformio run -e lora-relay-v1
- name: Build for linux
run: platformio run -e linux
# Turn off linux for now
#- name: Build for linux
# run: platformio run -e linux

View File

@@ -6,6 +6,8 @@ BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
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
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
# 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
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
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset

View File

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

View File

@@ -4,6 +4,11 @@ You probably don't care about this section - skip to the next one.
## 1.2 cleanup & multichannel support:
* timestamps on oled screen are wrong - don't seem to be updating based on message rx
* luxon bug report - seeing rx acks for nodes that are not on the network
* channel hash suffixes are wrong on android
* 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 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
@@ -21,32 +26,44 @@ You probably don't care about this section - skip to the next one.
* DONE implement 'get channels' Admin plugin operation
* DONE use get-channels from python
* 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 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 release python api
* DONE release protobufs
* 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
* warn in android app about unset regions
* use set-channel from android
* DONE test android channel setting
* 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
* stress test channel download from python, sometimes it seems like we don't get all replies
* investigate @mc-hamster report of heap corruption
* DONE use set-user from android
* combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
* 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
* DONE clean up python channel usage
* DONE use bindToChannel to limit admin access for remote nodes
* DONE move channels and radio config out of device settings
* test remote info and remote settings changes
* 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)
* stress test multi channel
* pick default random admin key
* 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 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)
* 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 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"
@@ -58,6 +75,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 multi hop routing flood broadcasts
* 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
* DONE move acks into routing
* DONE make all subpackets different versions of data
@@ -72,21 +90,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
* implement 'small location diffs' change
* 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
eink:
* DONE check email of reported issues
* DONE turn off vbus driving (in bootloader)
* new battery level sensing
* measure current draw
* current draw no good
* 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!
* make new screen work, ask for datasheet
* say I think you could ship this
* leds seem busted
* usb doesn't stay connected
* check GPS works
* check GPS fast locking
* fix hw_model: "nrf52unknown"
* use larger icon for meshtastic logo
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
* send PR for bootloader
* fix nrf52 time/date

View File

@@ -45,6 +45,18 @@ Recommended settings for a sender at different radio settings:
Medium ... 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
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.

View File

@@ -16,6 +16,7 @@ default_envs = tbeam
;default_envs = tlora-v2
;default_envs = lora-relay-v1 # nrf board
;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
[common]
@@ -33,6 +34,8 @@ default_envs = tbeam
extra_scripts = bin/platformio-custom.py
; 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
build_flags = -Wno-missing-field-initializers
-Wno-format
@@ -40,14 +43,7 @@ build_flags = -Wno-missing-field-initializers
-DHW_VERSION_${sysenv.COUNTRY}
-DHW_VERSION=${sysenv.HW_VERSION}
-DUSE_THREAD_NAMES
-DTINYGPSPLUS_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
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
; the default is esptool
; upload_protocol = esp-prog
@@ -71,10 +67,10 @@ lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
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
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/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
Wire ; explicitly needed here because the AXP202 library forgets to add it
SPI
@@ -115,6 +111,13 @@ lib_ignore = segger_rtt
platform_packages =
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
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
@@ -184,7 +187,6 @@ src_filter =
; platform = nordicnrf52
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
extends = arduino_base
debug_tool = jlink
build_type = debug ; I'm debugging with ICE a lot now
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
build_flags =
@@ -198,6 +200,27 @@ lib_ignore =
BluetoothOTA
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 =
source gdbinit

2
proto

Submodule proto updated: 94bd0aae44...bf8ac6718c

View File

@@ -178,10 +178,11 @@ void PowerFSM_setup()
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
/* 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.
2) If we detect USB power from the power management chip, 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.
*/
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);
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");

View File

@@ -1,9 +1,9 @@
#include "RedirectablePrint.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include "concurrency/OSThread.h"
/**
* 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
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
#endif
dest->write(c);
@@ -38,7 +38,7 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
va_end(arg);
return 0;
};
if (len >= printBufLen) {
if (len >= (int)printBufLen) {
delete[] printBuf;
printBufLen *= 2;
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, ...)
{
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;
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
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;
if (!inDebugPrint) {
inDebugPrint = true;
// 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
va_list arg;
va_start(arg, format);
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
} else
r += printf("??:??:?? %u ", millis() / 1000);
// Cope with 0 len format strings, but look for new line terminator
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
auto thread = concurrency::OSThread::currentThread;
if(thread) {
print("[");
print(thread->ThreadName);
print("] ");
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
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
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;
}

View File

@@ -19,6 +19,8 @@ class RedirectablePrint : public Print
/// Used to allow multiple logDebug messages to appear on a single log line
bool isContinuationMessage = false;
volatile bool inDebugPrint = false;
public:
RedirectablePrint(Print *_dest) : dest(_dest) {}

View File

@@ -1,13 +1,21 @@
#include "SerialConsole.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "NodeDB.h"
#include <Arduino.h>
#define Port Serial
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)
{
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)
{
// 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);
canWrite = true;

View File

@@ -32,4 +32,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
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;

View File

@@ -1,5 +1,6 @@
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#include "configuration.h"
#include <assert.h>
#ifdef HAS_FREE_RTOS
@@ -9,6 +10,7 @@ namespace concurrency
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
{
semaphore = xSemaphoreCreateBinary();
assert(semaphore);
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()

View File

@@ -54,7 +54,7 @@ class ESP32CryptoEngine : public CryptoEngine
static uint8_t scratch[MAX_BLOCKSIZE];
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);
assert(numBytes <= MAX_BLOCKSIZE);
memcpy(scratch, bytes, numBytes);

View File

@@ -25,7 +25,7 @@ uint8_t GPS::i2cAddress = 0;
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.
static bool didSerialInit;
@@ -33,7 +33,7 @@ bool GPS::setupGPS()
{
if (_serial_gps && !didSerialInit) {
didSerialInit = true;
#ifdef GPS_RX_PIN
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
@@ -73,6 +73,13 @@ bool GPS::setup()
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
#ifndef GPS_WAKE_ACTIVE
#define GPS_WAKE_ACTIVE 1
@@ -86,8 +93,8 @@ void GPS::wake()
#endif
}
void GPS::sleep() {
void GPS::sleep()
{
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
@@ -158,7 +165,8 @@ uint32_t GPS::getWakeTime() const
return t; // already maxint
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
@@ -179,8 +187,8 @@ uint32_t GPS::getSleepTime() const
if (t == UINT32_MAX)
return t; // already maxint
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
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 *= 1000;

View File

@@ -47,7 +47,7 @@ class GPS : private concurrency::OSThread
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 */
Observable<const meshtastic::GPSStatus *> newStatus;

View File

@@ -143,11 +143,13 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
drawIconScreen(region, display, state, x, y);
}
#ifdef HAS_EINK
/// Used on eink displays while in deep sleep
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
drawIconScreen("Sleeping...", display, state, x, y);
}
#endif
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
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,
// mp.decoded.variant.data.decoded.bytes);
@@ -791,7 +793,8 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(textMessagePlugin);
if(textMessagePlugin)
textMessageObserver.observe(textMessagePlugin);
}
void Screen::forceDisplay()
@@ -849,7 +852,7 @@ int32_t Screen::runOnce()
free(cmd.print_text);
break;
default:
DEBUG_MSG("BUG: invalid cmd");
DEBUG_MSG("BUG: invalid cmd\n");
}
}

View File

@@ -279,7 +279,13 @@ void setup()
concurrency::hasBeenSetup = true;
#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
#ifdef USE_SEGGER
@@ -583,6 +589,9 @@ void loop()
#ifndef NO_ESP32
esp32Loop();
#endif
#ifdef NRF52_SERIES
nrf52Loop();
#endif
// For debugging
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();

318
src/memtest.cpp Normal file
View 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++;
}

View File

@@ -13,7 +13,7 @@ Channels channels;
uint8_t xorHash(const uint8_t *p, size_t len)
{
uint8_t code = 0;
for (int i = 0; i < len; i++)
for (size_t i = 0; i < len; i++)
code ^= p[i];
return code;
}
@@ -161,8 +161,8 @@ int16_t Channels::setCrypto(ChannelIndex chIndex)
void Channels::initDefaults()
{
devicestate.channels_count = MAX_NUM_CHANNELS;
for (int i = 0; i < devicestate.channels_count; i++)
channelFile.channels_count = MAX_NUM_CHANNELS;
for (int i = 0; i < channelFile.channels_count; i++)
fixupChannel(i);
initDefaultChannel(0);
}
@@ -170,7 +170,7 @@ void Channels::initDefaults()
void Channels::onConfigChanged()
{
// 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);
if (ch.role == Channel_Role_PRIMARY)
@@ -180,8 +180,8 @@ void Channels::onConfigChanged()
Channel &Channels::getByIndex(ChannelIndex chIndex)
{
assert(chIndex < devicestate.channels_count);
Channel *ch = devicestate.channels + chIndex;
assert(chIndex < channelFile.channels_count);
Channel *ch = channelFile.channels + chIndex;
return *ch;
}
@@ -192,8 +192,8 @@ void Channels::setChannel(const Channel &c)
// if this is the new primary, demote any existing roles
if (c.role == Channel_Role_PRIMARY)
for (int i = 0; i < getNumChannels(); i++)
if (devicestate.channels[i].role == Channel_Role_PRIMARY)
devicestate.channels[i].role = Channel_Role_SECONDARY;
if (channelFile.channels[i].role == Channel_Role_PRIMARY)
channelFile.channels[i].role = Channel_Role_SECONDARY;
old = c; // slam in the new settings/role
}
@@ -275,10 +275,11 @@ const char *Channels::getPrimaryName()
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash 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;
}
else {
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
setCrypto(chIndex);
return true;
}

View File

@@ -44,7 +44,7 @@ class Channels
/** The index of the primary channel */
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

View File

@@ -4,6 +4,10 @@
void CryptoEngine::setKey(const CryptoKey &k)
{
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;
}

View File

@@ -69,7 +69,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c)
// ignore rebroadcasts.
// this will also add records for any ACKs we receive for our messages
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
addRoute(getFrom(p), getFrom(p), 0); // We are adjacent with zero hops
}
if (c)

View File

@@ -31,7 +31,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
// If a broadcast, possibly _also_ send copies out into the mesh.
// (FIXME, do something smarter than naive flooding here)
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && p->from != getNodeNum()) {
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) {
if (p->id != 0) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it

View File

@@ -101,25 +101,30 @@ template <class T> class MemoryPool : public Allocator<T>
/// Return a buffer for use by others
virtual void release(T *p)
{
assert(dead.enqueue(p, 0));
assert(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
assert(dead.enqueue(p, 0));
}
#ifdef HAS_FREE_RTOS
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
{
assert(dead.enqueueFromISR(p, higherPriWoken));
assert(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
assert(dead.enqueueFromISR(p, higherPriWoken));
}
#endif
protected:
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
/// probably don't want this version).
virtual T *alloc(TickType_t maxWait) { return dead.dequeuePtr(maxWait); }
virtual T *alloc(TickType_t maxWait)
{
T *p = dead.dequeuePtr(maxWait);
assert(p);
return p;
}
};

View File

@@ -71,7 +71,7 @@ static PacketId findId;
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 */

View File

@@ -1,23 +1,30 @@
#include "MeshPlugin.h"
#include "NodeDB.h"
#include "Channels.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "plugins/RoutingPlugin.h"
#include <assert.h>
std::vector<MeshPlugin *> *MeshPlugin::plugins;
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)
{
// Can't trust static initalizer order, so we check each time
if(!plugins)
if (!plugins)
plugins = new std::vector<MeshPlugin *>();
plugins->push_back(this);
}
void MeshPlugin::setup() {
}
void MeshPlugin::setup() {}
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?
currentReply = NULL; // No reply yet
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
auto ourNodeNum = nodeDB.getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
@@ -39,8 +48,15 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
pi.currentRequest = &mp;
// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
/// received channel
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);
if (wantsPacket) {
pluginFound = true;
@@ -48,15 +64,16 @@ void MeshPlugin::callPlugins(const MeshPacket &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)
// 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
// sends things, it sends things using the local node ID as the from address. A better solution (FIXME) would be to let phones
// have their own distinct addresses and we 'route' to them like any other node.
if (mp.decoded.want_response && toUs && (mp.from != ourNodeNum || mp.to == ourNodeNum)) {
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because
// currently when the phone sends things, it sends things using the local node ID as the from address. A better
// solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other
// node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
pi.sendResponse(mp);
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
}
else {
} else {
DEBUG_MSG("Plugin %s considered\n", pi.name);
}
if (handled) {
@@ -64,11 +81,24 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
break;
}
}
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);
}
@@ -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
* is optional
*/
void MeshPlugin::sendResponse(const MeshPacket &req) {
void MeshPlugin::sendResponse(const MeshPacket &req)
{
auto r = allocReply();
if(r) {
DEBUG_MSG("Sending response\n");
if (r) {
setReplyTo(r, req);
service.sendToMesh(r);
}
else {
currentReply = r;
} else {
// 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");
}
}
/** 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.
*/
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
p->to = to.from;
p->want_ack = to.want_ack;
p->to = getFrom(&to);
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;
}
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) {
auto &pi = **i;
if ( pi.wantUIFrame()) {
if (pi.wantUIFrame()) {
DEBUG_MSG("Plugin wants a UI Frame\n");
pluginsWithUIFrames.push_back(&pi);
}
}
return pluginsWithUIFrames;
}

View File

@@ -1,9 +1,9 @@
#pragma once
#include "mesh/MeshTypes.h"
#include <vector>
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
#include <vector>
/** A baseclass for any mesh "plugin".
*
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
@@ -16,7 +16,7 @@
*/
class MeshPlugin
{
static std::vector<MeshPlugin *> *plugins;
static std::vector<MeshPlugin *> *plugins;
public:
/** Constructor
@@ -37,16 +37,24 @@ class MeshPlugin
protected:
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)
But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those plugins can set this to
true and their handleReceived() will be called for every packet.
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
plugins can set this to true and their handleReceived() will be called for every packet.
*/
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
* 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
* plugin at a time.
*/
@@ -78,9 +86,13 @@ class MeshPlugin
*/
virtual bool wantUIFrame() { return false; }
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
* 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);
};
/** 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.
*/
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to);

View File

@@ -13,8 +13,8 @@
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/PositionPlugin.h"
#include "plugins/NodeInfoPlugin.h"
#include "plugins/PositionPlugin.h"
#include "power.h"
/*
@@ -51,8 +51,6 @@ MeshService service;
#include "Router.h"
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
{
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
@@ -67,7 +65,6 @@ void MeshService::init()
gpsObserver.observe(&gps->newStatus);
}
int MeshService::handleFromRadio(const MeshPacket *mp)
{
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
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();
return didReset;
@@ -117,7 +114,8 @@ bool MeshService::reloadConfig()
void MeshService::reloadOwner()
{
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo();
if(nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk();
}
@@ -128,8 +126,12 @@ void MeshService::reloadOwner()
*/
void MeshService::handleToRadio(MeshPacket &p)
{
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
p.from = nodeDB.getNodeNum();
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
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)
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 */
bool MeshService::cancelSending(PacketId id) {
bool MeshService::cancelSending(PacketId id)
{
return router->cancelSending(nodeDB.getNodeNum(), id);
}
@@ -168,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
assert(positionPlugin && nodeInfoPlugin);
if (node->has_position)
positionPlugin->sendOurPosition(dest, wantReplies);
else
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
if (node->has_position) {
if(positionPlugin) {
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
positionPlugin->sendOurPosition(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());
assert(node);
// 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));
node->has_position = true;
}
Position &position = node->position;
// 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();
updateBatteryLevel(position.battery_level);
@@ -209,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
pos.altitude = gps->altitude;
pos.latitude_i = gps->latitude;
pos.longitude_i = gps->longitude;
}
else {
} else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
if(radioConfig.preferences.fixed_position) {
if (radioConfig.preferences.fixed_position) {
DEBUG_MSG("WARNING: Using fixed position\n");
} else {
// throw away old position

View File

@@ -32,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number
typedef int ErrorCode;
/// 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);

View File

@@ -30,7 +30,8 @@ NodeDB nodeDB;
// we have plenty of ram so statically alloc this tempbuf (for now)
EXT_RAM_ATTR DeviceState devicestate;
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
* 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) {}
/**
* 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 didFactoryReset = false;
@@ -76,7 +86,7 @@ bool NodeDB::resetRadioConfig()
DEBUG_MSG("Performing factory reset!\n");
installDefaultDeviceState();
didFactoryReset = true;
} else if (devicestate.channels_count == 0) {
} else if (channelFile.channels_count == 0) {
DEBUG_MSG("Setting default channel and radio preferences!\n");
channels.initDefaults();
@@ -109,6 +119,18 @@ bool NodeDB::resetRadioConfig()
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()
{
// 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
devicestate.has_my_node = true;
devicestate.has_radio = true;
devicestate.has_owner = true;
devicestate.radio.has_preferences = true;
devicestate.node_db_count = 0;
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
resetRadioConfig();
// default to no GPS, until one has been found by probing
myNodeInfo.has_gps = false;
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
@@ -150,6 +169,9 @@ void NodeDB::installDefaultDeviceState()
radioConfig.preferences.region = oldRegionCode;
if (oldRegion.length()) // If the old style region was set, try to keep it up-to-date
strcpy(myNodeInfo.region, oldRegion.c_str());
installDefaultChannels();
installDefaultRadioConfig();
}
void NodeDB::init()
@@ -178,12 +200,16 @@ void NodeDB::init()
info->user = owner;
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
// what is stored in flash
if (xstr(HW_VERSION)[0])
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
else
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
//if (xstr(HW_VERSION)[0])
// strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
// else 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.
// Those strings will look like "1.0-EU433"
@@ -201,8 +227,7 @@ void NodeDB::init()
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,
myNodeInfo.my_node_num, *numNodes);
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
}
// We reserve a few nodenums for future use
@@ -232,84 +257,134 @@ void NodeDB::pickNewNodeNum()
myNodeInfo.my_node_num = r;
}
const char *preffile = "/db.proto";
const char *preftmp = "/db.proto.tmp";
static const char *preffileOld = "/db.proto";
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
// 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) {
DEBUG_MSG("Loading saved preferences\n");
pb_istream_t stream = {&readcb, &f, DeviceState_size};
DEBUG_MSG("Loading %s\n", filename);
pb_istream_t stream = {&readcb, &f, protoSize};
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
memset(&devicestate, 0, sizeof(devicestate));
if (!pb_decode(&stream, DeviceState_fields, &devicestate)) {
memset(dest_struct, 0, objSize);
if (!pb_decode(&stream, fields, dest_struct)) {
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
// FIXME - report failure to phone
} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
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);
okay = true;
}
f.close();
} else {
DEBUG_MSG("No saved preferences found\n");
DEBUG_MSG("No %s preferences found\n", filename);
}
#else
DEBUG_MSG("ERROR: Filesystem not implemented\n");
#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()
{
#ifdef FS
if (!devicestate.no_save) {
auto f = FS.open(preftmp, FILE_O_WRITE);
if (f) {
DEBUG_MSG("Writing preferences\n");
#ifdef FS
FS.mkdir("/prefs");
#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};
// 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
}
// remove any pre 1.2 pref files, turn on after 1.2 is in beta
// if(okay) FS.remove(preffileOld);
} else {
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()
@@ -362,7 +437,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
// recorded based on the packet rxTime
if (!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;
if (p.latitude_i || p.longitude_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) {
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
info->has_position = true; // at least the time is valid

View File

@@ -9,8 +9,9 @@
#include "mesh-pb-constants.h"
extern DeviceState devicestate;
extern ChannelFile channelFile;
extern MyNodeInfo &myNodeInfo;
extern RadioConfig &radioConfig;
extern RadioConfig radioConfig;
extern User &owner;
/// 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();
/// write to flash
void saveToDisk();
void saveToDisk(), saveChannelsToDisk();
/** Reinit radio config if needed, because either:
* a) sometimes a buggy android app might send us bogus settings or
@@ -117,7 +118,7 @@ class NodeDB
void loadFromDisk();
/// 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.
*/
extern uint32_t radioGeneration;

View File

@@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
recentPackets.erase(recentPackets.begin() + i); // delete old record
} else {
if (r.id == p->id && r.sender == p->from) {
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);
// Update the time on this record to now
@@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
if (withUpdate) {
PacketRecord r;
r.id = p->id;
r.sender = p->from;
r.sender = getFrom(p);
r.rxTimeMsec = now;
recentPackets.push_back(r);
printPacket("Adding packet record", p);

View File

@@ -59,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
}
// return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
switch (toRadioScratch.which_payloadVariant) {
case ToRadio_packet_tag: {
@@ -155,7 +156,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
state = STATE_SEND_PACKETS;
break;
case STATE_LEGACY: // Treat as the same as send packets
case STATE_SEND_PACKETS:
// Do we have a message from the mesh?
if (packetForPhone) {
@@ -178,9 +178,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Do we have a message from the mesh?
if (fromRadioScratch.which_payloadVariant != 0) {
// 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);
DEBUG_MSG(", %d bytes\n", numbytes);
// DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes);
return numbytes;
}
@@ -208,7 +207,6 @@ bool PhoneAPI::available()
case STATE_SEND_COMPLETE_ID:
return true;
case STATE_LEGACY: // Treat as the same as send packets
case STATE_SEND_PACKETS: {
// Try to pull a new packet from the service (if we haven't already)
if (!packetForPhone)
@@ -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
// 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);
onNowHasData(newValue);
} else

View File

@@ -20,7 +20,7 @@ class PhoneAPI
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
{
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_MY_INFO, // send our my info record
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet

View File

@@ -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.
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 *decoded = NULL;
if(mp.decoded.portnum == ourPortNum) {
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
decoded = &scratch;
else

View File

@@ -1,16 +1,16 @@
#include "configuration.h"
#include "RadioInterface.h"
#include "Channels.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "assert.h"
#include "configuration.h"
#include "Router.h"
#include "sleep.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include "Channels.h"
#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
// 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(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
};
/* 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;
void initRegion()
@@ -119,11 +137,11 @@ uint32_t RadioInterface::getTxDelayMsec()
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,
p->hop_limit, p->channel);
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, p->hop_limit, p->channel);
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
auto &s = p->decoded;
DEBUG_MSG(" Portnum=%d", s.portnum);
if (s.want_response)
@@ -135,6 +153,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (s.dest != 0)
DEBUG_MSG(" dest=%08x", s.dest);
if(s.request_id)
DEBUG_MSG(" requestId=%0x", s.request_id);
/* now inside Data and therefore kinda opaque
if (s.which_ackVariant == SubPacket_success_id_tag)
DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
@@ -332,6 +353,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
{
assert(rxDest);
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();
}
/***

View File

@@ -1,4 +1,5 @@
#include "ReliableRouter.h"
#include "MeshPlugin.h"
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
@@ -26,15 +27,26 @@ ErrorCode ReliableRouter::send(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()) {
printPacket("Rx someone rebroadcasting for us", p);
// We are seeing someone rebroadcast one of our broadcast attempts.
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
// the original sending process.
if (stopRetransmission(p->from, p->id)) {
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
sendAckNak(Routing_Error_NONE, p->from, p->id);
// FIXME - we might want to turn off this "optimization", it does save lots of airtime but it assumes that once we've heard one
// one adjacent node hear our packet that a) probably other adjacent nodes heard it and b) we can trust those nodes to reach
// our destination. Both of which might be incorrect.
auto key = GlobalPacketId(getFrom(p), p->id);
auto old = findPendingPacket(key);
if (old) {
DEBUG_MSG("generating implicit ack\n");
// 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, old->packet->channel);
stopRetransmission(key);
}
}
@@ -60,23 +72,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
// - not DSR routing)
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
if (c) {
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 consider an ack to be either a !routing packet with a request ID or a routing packet with !error
PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) {
if (ackId) {
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
// A nak is a routing packt that has an error code
PacketId nakId = (c && 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
if (ackId || nakId) {
if (ackId) {
DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
}
}
@@ -130,8 +145,9 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
auto id = GlobalPacketId(p);
auto rec = PendingPacket(p);
stopRetransmission(getFrom(p), p->id);
setNextTx(&rec);
stopRetransmission(p->from, p->id);
pending[id] = rec;
return &pending[id];
@@ -151,18 +167,21 @@ int32_t ReliableRouter::doRetransmissions()
++nextIt; // we use this odd pattern because we might be deleting it...
auto &p = it->second;
bool stillValid = true; // assume we'll keep this record around
// FIXME, handle 51 day rolloever here!!!
if (p.nextTxMsec <= now) {
if (p.numRetransmissions == 0) {
DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
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);
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
// allows the DSR version to still be able to look at the PendingPacket
stopRetransmission(it->first);
stillValid = false; // just deleted it
} else {
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
p.packet->id, p.numRetransmissions);
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
p.packet->to, p.packet->id, p.numRetransmissions);
// Note: we call the superclass version because we don't want to have our version of send() add a new
// retransmission record
@@ -172,8 +191,10 @@ int32_t ReliableRouter::doRetransmissions()
--p.numRetransmissions;
setNextTx(&p);
}
} else {
// Not yet time
}
if (stillValid) {
// Update our desired sleep delay
int32_t t = p.nextTxMsec - now;
d = min(t, d);
@@ -181,4 +202,14 @@ int32_t ReliableRouter::doRetransmissions()
}
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
}

View File

@@ -15,7 +15,7 @@ struct GlobalPacketId {
GlobalPacketId(const MeshPacket *p)
{
node = p->from;
node = getFrom(p);
id = p->id;
}
@@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter
*/
int32_t doRetransmissions();
void setNextTx(PendingPacket *pending) {
assert(iface);
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
void setNextTx(PendingPacket *pending);
};

View File

@@ -103,24 +103,29 @@ MeshPacket *Router::allocForSending()
/**
* 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)
{
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);
}
void Router::setReceivedMessage() {
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
}
ErrorCode Router::sendLocal(MeshPacket *p)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
printPacket("Enqueuing local", p);
fromRadioQueue.enqueue(p);
setReceivedMessage();
return ERRNO_OK;
} else if (!iface) {
// 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
* 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)
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)
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
@@ -164,6 +180,8 @@ ErrorCode Router::send(MeshPacket *p)
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
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);
if (numbytes > MAX_RHPACKETLEN) {
@@ -171,6 +189,8 @@ ErrorCode Router::send(MeshPacket *p)
return ERRNO_TOO_LARGE;
}
//printBytes("plaintext", bytes, numbytes);
auto hash = channels.setActiveByIndex(p->channel);
if (hash < 0) {
// 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)
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
memcpy(p->encrypted.bytes, bytes, numbytes);
@@ -221,19 +241,26 @@ bool Router::perhapsDecode(MeshPacket *p)
if (channels.decryptForHash(chIndex, p->channel)) {
// Try to decrypt the packet if we can
static uint8_t bytes[MAX_RHPACKETLEN];
size_t rawSize = p->encrypted.size;
assert(rawSize <= sizeof(bytes));
memcpy(bytes, p->encrypted.bytes,
p->encrypted
.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
rawSize); // 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);
//printBytes("plaintext", bytes, p->encrypted.size);
// 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)) {
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?!\n");
} else {
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
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
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
// printPacket("decoded message", p);
p->which_payloadVariant = MeshPacket_decoded_tag;
printPacket("decoded message", p);
return true;
}
}
@@ -258,11 +285,10 @@ void Router::handleReceived(MeshPacket *p)
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
bool decoded = perhapsDecode(p);
printPacket("handleReceived", p);
DEBUG_MSG("decoded=%d\n", decoded);
bool decoded = perhapsDecode(p);
if (decoded) {
// 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
// sniffReceived(p);
@@ -273,7 +299,7 @@ void Router::handleReceived(MeshPacket *p)
void Router::perhapsHandleReceived(MeshPacket *p)
{
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)
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);

View File

@@ -6,6 +6,7 @@
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "concurrency/OSThread.h"
#include "Channels.h"
/**
* A mesh aware router that supports multiple interfaces.
@@ -63,6 +64,11 @@ class Router : protected concurrency::OSThread
* @return our local nodenum */
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:
friend class RoutingPlugin;
@@ -101,7 +107,7 @@ class Router : protected concurrency::OSThread
/**
* 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:
/**

View File

@@ -71,12 +71,18 @@ void StreamAPI::writeStream()
void StreamAPI::emitTxBuffer(size_t len)
{
if (len != 0) {
DEBUG_MSG("emit tx %d\n", len);
txBuf[0] = START1;
txBuf[1] = START2;
txBuf[2] = (len >> 8) & 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();
} */
}
}

View File

@@ -23,6 +23,8 @@ typedef struct _AdminMessage {
RadioConfig get_radio_response;
uint32_t get_channel_request;
Channel get_channel_response;
bool confirm_set_channel;
bool confirm_set_radio;
};
} AdminMessage;
@@ -43,6 +45,8 @@ extern "C" {
#define AdminMessage_get_radio_response_tag 5
#define AdminMessage_get_channel_request_tag 6
#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 */
#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, 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, 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_DEFAULT NULL
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig
@@ -67,7 +73,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */
#define AdminMessage_size 338
#define AdminMessage_size 351
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -40,7 +40,7 @@ typedef struct _ChannelSettings {
} ChannelSettings;
typedef struct _Channel {
uint8_t index;
int8_t index;
bool has_settings;
ChannelSettings settings;
Channel_Role role;
@@ -100,7 +100,7 @@ X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 17)
#define ChannelSettings_DEFAULT NULL
#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, SINGULAR, UENUM, role, 3)
#define Channel_CALLBACK NULL
@@ -116,7 +116,7 @@ extern const pb_msgdesc_t Channel_msg;
/* Maximum encoded size of messages (where known) */
#define ChannelSettings_size 87
#define Channel_size 94
#define Channel_size 102
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -6,7 +6,16 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(LegacyRadioConfig, LegacyRadioConfig, AUTO)
PB_BIND(LegacyRadioConfig_LegacyPreferences, LegacyRadioConfig_LegacyPreferences, AUTO)
PB_BIND(DeviceState, DeviceState, 2)
PB_BIND(ChannelFile, ChannelFile, 2)

View File

@@ -5,17 +5,31 @@
#define PB_DEVICEONLY_PB_H_INCLUDED
#include <pb.h>
#include "mesh.pb.h"
#include "radioconfig.pb.h"
#include "channel.pb.h"
#include "radioconfig.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* 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 {
bool has_radio;
RadioConfig radio;
bool has_legacyRadio;
LegacyRadioConfig legacyRadio;
bool has_my_node;
MyNodeInfo my_node;
bool has_owner;
@@ -29,8 +43,6 @@ typedef struct _DeviceState {
uint32_t version;
bool no_save;
bool did_gps_reset;
pb_size_t channels_count;
Channel channels[8];
} DeviceState;
@@ -39,11 +51,20 @@ extern "C" {
#endif
/* 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 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_init_default {false, LegacyRadioConfig_LegacyPreferences_init_default}
#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) */
#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_owner_tag 3
#define DeviceState_node_db_tag 4
@@ -52,11 +73,21 @@ extern "C" {
#define DeviceState_version_tag 8
#define DeviceState_no_save_tag 9
#define DeviceState_did_gps_reset_tag 11
#define DeviceState_channels_tag 13
/* 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) \
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, owner, 3) \
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, SINGULAR, UINT32, version, 8) \
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
X(a, STATIC, REPEATED, MESSAGE, channels, 13)
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
#define DeviceState_CALLBACK NULL
#define DeviceState_DEFAULT NULL
#define DeviceState_radio_MSGTYPE RadioConfig
#define DeviceState_legacyRadio_MSGTYPE LegacyRadioConfig
#define DeviceState_my_node_MSGTYPE MyNodeInfo
#define DeviceState_owner_MSGTYPE User
#define DeviceState_node_db_MSGTYPE NodeInfo
#define DeviceState_receive_queue_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 ChannelFile_msg;
/* 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 ChannelFile_fields &ChannelFile_msg
/* 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
} /* extern "C" */

View File

@@ -25,7 +25,8 @@ typedef enum _CriticalErrorCode {
CriticalErrorCode_UBloxInitFailed = 5,
CriticalErrorCode_NoAXP192 = 6,
CriticalErrorCode_InvalidRadioSetting = 7,
CriticalErrorCode_TransmitFailed = 8
CriticalErrorCode_TransmitFailed = 8,
CriticalErrorCode_Brownout = 9
} CriticalErrorCode;
typedef enum _Routing_Error {
@@ -36,7 +37,8 @@ typedef enum _Routing_Error {
Routing_Error_NO_INTERFACE = 4,
Routing_Error_MAX_RETRANSMIT = 5,
Routing_Error_NO_CHANNEL = 6,
Routing_Error_TOO_LARGE = 7
Routing_Error_TOO_LARGE = 7,
Routing_Error_NO_RESPONSE = 8
} Routing_Error;
typedef enum _MeshPacket_Priority {
@@ -177,12 +179,12 @@ typedef struct _ToRadio {
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
#define _CriticalErrorCode_MIN CriticalErrorCode_None
#define _CriticalErrorCode_MAX CriticalErrorCode_TransmitFailed
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1))
#define _CriticalErrorCode_MAX CriticalErrorCode_Brownout
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1))
#define _Routing_Error_MIN Routing_Error_NONE
#define _Routing_Error_MAX Routing_Error_TOO_LARGE
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_TOO_LARGE+1))
#define _Routing_Error_MAX Routing_Error_NO_RESPONSE
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1))
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX

View File

@@ -20,10 +20,10 @@ typedef enum _PortNum {
PortNum_ADMIN_APP = 6,
PortNum_REPLY_APP = 32,
PortNum_IP_TUNNEL_APP = 33,
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 34,
PortNum_SERIAL_APP = 64,
PortNum_STORE_FORWARD_APP = 65,
PortNum_RANGE_TEST_APP = 66,
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
PortNum_PRIVATE_APP = 256,
PortNum_ATAK_FORWARDER = 257,
PortNum_MAX = 511

View File

@@ -17,3 +17,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)

View File

@@ -19,7 +19,8 @@ typedef enum _RegionCode {
RegionCode_JP = 5,
RegionCode_ANZ = 6,
RegionCode_KR = 7,
RegionCode_TW = 8
RegionCode_TW = 8,
RegionCode_RU = 9
} RegionCode;
typedef enum _ChargeCurrent {
@@ -56,6 +57,10 @@ typedef enum _LocationSharing {
LocationSharing_LocDisabled = 2
} LocationSharing;
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
/* Struct definitions */
typedef struct _RadioConfig_UserPreferences {
uint32_t position_broadcast_secs;
@@ -99,13 +104,16 @@ typedef struct _RadioConfig_UserPreferences {
bool range_test_plugin_enabled;
uint32_t range_test_plugin_sender;
bool range_test_plugin_save;
bool store_forward_plugin_enabled;
uint32_t store_forward_plugin_records;
bool environmental_measurement_plugin_measurement_enabled;
bool environmental_measurement_plugin_screen_enabled;
uint32_t environmental_measurement_plugin_read_error_count_threshold;
uint32_t environmental_measurement_plugin_update_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;
bool store_forward_plugin_enabled;
} RadioConfig_UserPreferences;
typedef struct _RadioConfig {
@@ -116,8 +124,8 @@ typedef struct _RadioConfig {
/* Helper constants for enums */
#define _RegionCode_MIN RegionCode_Unset
#define _RegionCode_MAX RegionCode_TW
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
#define _RegionCode_MAX RegionCode_RU
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1))
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
@@ -131,6 +139,10 @@ typedef struct _RadioConfig {
#define _LocationSharing_MAX LocationSharing_LocDisabled
#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
extern "C" {
@@ -138,9 +150,9 @@ extern "C" {
/* Initializer values for message structs */
#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, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
#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, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
@@ -183,13 +195,16 @@ extern "C" {
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 136
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
#define RadioConfig_UserPreferences_environmental_measurement_plugin_measurement_enabled_tag 140
#define RadioConfig_UserPreferences_environmental_measurement_plugin_screen_enabled_tag 141
#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_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_UserPreferences_store_forward_plugin_enabled_tag 148
#define RadioConfig_preferences_tag 1
/* Struct field encoding specification for nanopb */
@@ -240,13 +255,16 @@ X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 136) \
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137) \
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_enabled, 140) \
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_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) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148)
#define RadioConfig_UserPreferences_CALLBACK 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
/* Maximum encoded size of messages (where known) */
#define RadioConfig_size 335
#define RadioConfig_UserPreferences_size 332
#define RadioConfig_size 348
#define RadioConfig_UserPreferences_size 345
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -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);
if (!pb_encode(&stream, fields, src_struct)) {
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
assert(0); // FIXME - panic
DEBUG_MSG("Panic: can't encode protobuf %s, did you make a field too large?\n", PB_GET_ERROR(&stream));
assert(0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options
} else {
return stream.bytes_written;
}

View File

@@ -18,7 +18,7 @@
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
/// 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
/// returns the encoded packet size

View File

@@ -82,8 +82,34 @@ extern "C" void HardFault_Impl(uint32_t stack[])
// while (1) ;
}
#ifndef INC_FREERTOS_H
// This is a generic cortex M entrypoint that doesn't assume freertos
extern "C" void HardFault_Handler(void)
{
asm volatile(" mrs r0,msp\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

View File

@@ -1,5 +1,6 @@
#include "NRF52Bluetooth.h"
#include "configuration.h"
#include "error.h"
#include "graphics/TFTDisplay.h"
#include <SPI.h>
#include <Wire.h>
@@ -51,14 +52,14 @@ void getMacAddr(uint8_t *dmac)
NRF52Bluetooth *nrf52Bluetooth;
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)
{
if (on != bleOn) {
if (on) {
if (!nrf52Bluetooth) {
if (!enableBle)
if (!useSoftDevice)
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
else {
nrf52Bluetooth = new NRF52Bluetooth();
@@ -87,6 +88,49 @@ int printf(const char *fmt, ...)
#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()
{
@@ -112,6 +156,8 @@ void nrf52Setup()
// randomSeed(r);
DEBUG_MSG("FIXME, call randomSeed\n");
// ::printf("TESTING PRINTF\n");
initBrownout();
}
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
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");
NRF_POWER->SYSTEMOFF = 1;
}

View File

@@ -8,8 +8,9 @@
AdminPlugin *adminPlugin;
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex) {
if (req.decoded.want_response) {
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
{
if (req.decoded.want_response) {
// We create the reply here
AdminMessage r = AdminMessage_init_default;
r.get_channel_response = channels.getByIndex(channelIndex);
@@ -23,7 +24,13 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req)
if (req.decoded.want_response) {
// We create the reply here
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;
reply = allocDataProtobuf(r);
}
@@ -59,6 +66,8 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
break;
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;
}
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);
bool didReset = 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
} */
// Just update and save the channels - no need to update the radio for ! primary channel changes
if (cc.index == 0) {
// FIXME, this updates the user preferences also, which isn't needed - we really just want to notify on configChanged
service.reloadConfig();
}
else {
channels.onConfigChanged(); // tell the radios about this change
nodeDB.saveChannelsToDisk();
}
}
void AdminPlugin::handleSetRadio(const RadioConfig &r)
{
radioConfig = r;
bool didReset = 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
} */
service.reloadConfig();
}
MeshPacket *AdminPlugin::allocReply()
@@ -115,5 +125,6 @@ MeshPacket *AdminPlugin::allocReply()
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";
}

View File

@@ -114,7 +114,8 @@ int32_t ExternalNotificationPlugin::runOnce()
return (INT32_MAX);
}
#else
return INT32_MAX;
#endif
}
@@ -147,7 +148,7 @@ bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
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.
// Need to know if and how this could be a problem.

View File

@@ -12,16 +12,18 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pp
{
auto p = *pptr;
nodeDB.updateUser(mp.from, p);
nodeDB.updateUser(getFrom(&mp), p);
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
// Show new nodes on LCD screen
if (wasBroadcast) {
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
}
@@ -65,8 +67,7 @@ int32_t NodeInfoPlugin::runOnce()
currentGeneration = radioGeneration;
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
return getPref_position_broadcast_secs() * 1000;
}

View File

@@ -20,7 +20,6 @@
*/
void setupPlugins()
{
routingPlugin = new RoutingPlugin();
adminPlugin = new AdminPlugin();
nodeInfoPlugin = new NodeInfoPlugin();
positionPlugin = new PositionPlugin();
@@ -48,4 +47,7 @@ void setupPlugins()
// new StoreForwardPlugin();
new EnvironmentalMeasurementPlugin();
#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();
}

View File

@@ -30,7 +30,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
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
}

View File

@@ -9,11 +9,12 @@ RoutingPlugin *routingPlugin;
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
{
DEBUG_MSG("Routing sniffing", &mp);
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
// 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);
service.handleFromRadio(&mp);
}
@@ -34,7 +35,7 @@ MeshPacket *RoutingPlugin::allocReply()
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;
@@ -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->to = to;
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);
router->sendLocal(p); // we sometimes send directly to the local node

View File

@@ -1,5 +1,6 @@
#pragma once
#include "ProtobufPlugin.h"
#include "Channels.h"
/**
* Routing plugin for router control messages
@@ -12,6 +13,8 @@ class RoutingPlugin : public ProtobufPlugin<Routing>
*/
RoutingPlugin();
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
protected:
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
virtual bool wantPacket(const MeshPacket *p) { return true; }
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom);
};
extern RoutingPlugin *routingPlugin;

View File

@@ -124,7 +124,8 @@ int32_t SerialPlugin::runOnce()
return (INT32_MAX);
}
#else
return INT32_MAX;
#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",
// 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

View File

@@ -8,7 +8,7 @@ TextMessagePlugin *textMessagePlugin;
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
{
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.
// Keep a copy of the most recent text message.

View File

@@ -10,20 +10,10 @@
#include <OLEDDisplay.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 FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
DHT dht(DHT_11_GPIO_PIN,DHT11);
#ifdef HAS_EINK
// 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
without having to configure it from the PythonAPI or WebUI.
*/
/*radioConfig.preferences.environmental_measurement_plugin_measurement_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_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 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) {
// 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;
// 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)
{
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
// 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
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 (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",
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
sensor_read_error_count = 0;
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
}
DEBUG_MSG(
@@ -110,7 +117,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
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
// as soon as we can according to the maximum polling frequency
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
@@ -123,25 +130,16 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
#endif
}
bool EnvironmentalMeasurementPluginRadio::wantUIFrame() {
bool EnvironmentalMeasurementPlugin::wantUIFrame() {
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 sender;
if (nodeDB.getNode(mp.from)){
sender = nodeDB.getNode(mp.from)->user.short_name;
auto node = nodeDB.getNode(getFrom(&mp));
if (node){
sender = node->user.short_name;
}
else {
sender = "UNK";
@@ -149,55 +147,99 @@ String GetSenderName(const MeshPacket &mp) {
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 this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
return false;
}
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
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->relative_humidity: %f\n", p.relative_humidity);
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature);
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity);
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
}
bool EnvironmentalMeasurementPluginRadio::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
{
EnvironmentalMeasurement m;
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
m.relative_humidity = dht.readHumidity();
m.temperature = dht.readTemperature();;
DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
if (isnan(m.relative_humidity) || isnan(m.temperature) ){
if (!this->dht->read(true)){
sensor_read_error_count++;
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
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;

View File

@@ -3,61 +3,32 @@
#include "../mesh/generated/environmental_measurement.pb.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
#include <DHT.h>
class EnvironmentalMeasurementPlugin : private concurrency::OSThread
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
{
bool firstTime = 1;
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:
/** 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();
};
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
*/
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:
EnvironmentalMeasurement lastMeasurement;
String lastSender;
};
extern EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio;
float CelsiusToFarenheit(float c);
bool firstTime = 1;
DHT* dht;
const MeshPacket *lastMeasurementPacket;
uint32_t sensor_read_error_count = 0;
};

View File

@@ -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",
// 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");
// 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) {
appendFile(mp);
@@ -209,7 +209,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
{
auto &p = mp.decoded;
NodeInfo *n = nodeDB.getNode(mp.from);
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
/*
DEBUG_MSG("-----------------------------------------\n");
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("%d,", mp.from); // From
fileToAppend.printf("%d,", getFrom(&mp)); // From
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.longitude_i * 1e-7); // Sender Long

View File

@@ -24,28 +24,28 @@ int32_t StoreForwardPlugin::runOnce()
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.store_forward_plugin_enabled = 1;
// radioConfig.preferences.is_router = 1;
attn @mc-hamster I moved this back inside the comment because I don't think it was intended to checkin. It was forcing all
nodes to be running this and turning off is_router.
radioConfig.preferences.store_forward_plugin_enabled = 1;
radioConfig.preferences.is_router = 0;
*/
if (radioConfig.preferences.store_forward_plugin_enabled) {
if (firstTime) {
/*
*/
firstTime = 0;
if (radioConfig.preferences.is_router) {
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled\n");
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Router\n");
// Router
if (ESP.getPsramSize()) {
if (ESP.getFreePsram() >= 2048 * 1024) {
// Do the startup here
storeForwardPluginRadio = new StoreForwardPluginRadio();
firstTime = 0;
this->populatePSRAM();
// packetHistory[0].bytes;
@@ -66,21 +66,31 @@ int32_t StoreForwardPlugin::runOnce()
}
} else {
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled but is_router is not turned on.\n");
DEBUG_MSG(
"Initializing Store & Forward Plugin - If you want to use this plugin, you must also turn on is_router.\n");
// Non-Router
return (30 * 1000);
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Client\n");
return (5 * 1000);
}
} else {
// What do we do if it's not our first time?
// Maybe some cleanup functions?
this->sawNodeReport();
this->historyReport();
return (10 * 1000);
if (radioConfig.preferences.is_router) {
// Maybe some cleanup functions?
this->sawNodeReport();
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 {
@@ -220,15 +230,32 @@ void StoreForwardPlugin::sawNodeReport()
MeshPacket *StoreForwardPluginRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
return reply;
return reply; // attn @mc-hamster this code was commented out and was causing memory corruption
}
void StoreForwardPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
/*
MeshPacket *p = this->allocReply(); // attn @mc-hamster, I moved inside the commented block to prevent leaking memory
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->decoded.want_response = wantReplies;
@@ -240,12 +267,12 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
#ifndef NO_ESP32
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("%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);
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);
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) {
@@ -282,7 +309,7 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
}
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);
}
}

View File

@@ -63,6 +63,11 @@ class StoreForwardPluginRadio : public SinglePortPlugin
*/
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:
virtual MeshPacket *allocReply();

View File

@@ -99,16 +99,16 @@ extern "C" {
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (0 + 13) // red (confirmed on 1.0 board)
#define PIN_LED2 (0 + 14) // blue (seems busted!)
#define PIN_LED3 (0 + 15) // green (seems busted!)
#define PIN_LED1 (0 + 14) // 13 red (confirmed on 1.0 board)
#define PIN_LED2 (0 + 15) // 14 blue
#define PIN_LED3 (0 + 13) // 15 green
#define LED_RED PIN_LED3
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_BLUE PIN_LED1
#define LED_GREEN PIN_LED2
#define LED_BUILTIN LED_GREEN
#define LED_CONN PIN_BLUE
#define LED_BUILTIN LED_BLUE
#define LED_CONN PIN_GREEN
#define LED_STATE_ON 0 // State when LED is lit
#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...)
// #undef SX1262_CS
// #define USE_SIM_RADIO // define to not use the lora radio hardware at all
/*
* eink display pins
*/

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 1
minor = 2
build = 1
build = 10