Compare commits

...

125 Commits

Author SHA1 Message Date
Ben Meadors
700397d3ed ugh 2024-08-27 07:32:05 -05:00
Ben Meadors
cd65497ac4 Update to move setup to its own module 2024-08-27 07:02:49 -05:00
Ben Meadors
3c90a65a66 WIP 2024-08-27 06:24:36 -05:00
Ben Meadors
9e8f0814ab Stuff 2024-08-26 16:34:28 -05:00
Ben Meadors
36cadd03ee wip 2024-08-25 15:27:59 -05:00
Ben Meadors
15ee827efd Hello world 2024-08-25 13:41:58 -05:00
Ben Meadors
d6dac1737a Userlite mem comparison (#4552) 2024-08-24 12:19:31 -05:00
github-actions[bot]
23844389ac [create-pull-request] automated change (#4544)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-23 20:44:14 -05:00
John Hollowell
8847945734 Add devcontainer (#4491)
devcontainers can be used by IDEs/editors like VS Code to create a standardized development environment in a container
2024-08-23 20:25:16 -05:00
Nestpebble
5514aab007 add a .yml to setup a Gitpod instance quickly (#4551)
* Create .gitpod.yml

* Update .gitpod.yml
2024-08-23 20:24:23 -05:00
geeksville
aa54335e21 remove deprecated serial/bt logging options and unify in the new (#4516)
security option.  Per discussion in https://github.com/meshtastic/firmware/issues/4375
no need to preserve the old options when changing to this new simpler
single boolean because they were newish, rarely used and only for 'advanced'
developers.
2024-08-23 20:18:36 -05:00
Ben Meadors
2a279c7f3d Dum dum zero comparision 2024-08-23 07:07:28 -05:00
Ben Meadors
7abc194ef5 Found more places to set explicit has_optional on position (#4542) 2024-08-23 07:04:34 -05:00
Ian McEwen
f99b81acf7 Use the '+' wildcard for MQTT rather than '#', to subscribe only to topics one nesting level deep (#4528) 2024-08-23 07:03:29 -05:00
Michael Gjelsø
0850ad6c8d Initial support for RadioMaster Bandit. (#4523)
* Initial support for RadioMaster Bandit.

* Different lighting can be made for Button 1 & 2 on the Bandit.
Changes to AmbientLighting will turn off af shutdown().

* Trunk

* Trunk again.
2024-08-23 06:28:23 -05:00
Jonathan Bennett
00ea9182a4 Fix copyPasta in NodeDB (#4538) 2024-08-23 06:26:19 -05:00
Jonathan Bennett
601ae29fe9 Adds has_x bools to position packet. (#4540) 2024-08-23 06:25:40 -05:00
Ben Meadors
ff500bc5a9 Save nodedb after favoriting (or removing) (#4537) 2024-08-22 20:57:03 -05:00
Mark Trevor Birss
734f36589d Update variant.h (#4534) 2024-08-22 10:44:49 -05:00
Mark Trevor Birss
1e655052fc Fixes for ME25LS01_4Y10TD and ESP32-PICO (#4522)
* Update platformio.ini

* Update variant.h

* Update architecture.h

* Update variant.h
2024-08-22 07:00:19 -05:00
Ben Meadors
6ddee795d6 Poetry 2024-08-21 17:24:56 -05:00
Mark Trevor Birss
d556ae762c Update ScanI2CTwoWire.cpp (#4520) 2024-08-21 06:04:03 -05:00
geeksville
48e0fd7ed0 fix #4448 (by seeing there is actually no problem) (#4517)
Print directory names when listing directories
2024-08-20 17:38:39 -05:00
Jonathan Bennett
ab7de7f6a0 Add handling for sessionkey config (#4513)
* Add handling for sessionkey config

* Protos

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-20 13:36:24 -05:00
Mictronics
6ee30043c3 Fix array out of bounds read. (#4514)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

* Fix deprecated macros.

* Set RP2040 in dormant mode when deep sleep is triggered.

* Fix array out of bounds read.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-20 13:36:10 -05:00
Ben Meadors
314009a10f Version 2.5 bump 2024-08-20 07:35:47 -05:00
Ben Meadors
2d9126f873 Try cwd 2024-08-20 07:17:39 -05:00
Ben Meadors
d404a49336 Trunk 2024-08-20 07:08:42 -05:00
Nestpebble
ee9e46ec92 Make it possible to define TCXO and XTAL radio modules within one variant (#4492)
* Update main.cpp

Add in TCXO_OPTIONAL variable for tcxoVoltage and a double-check for working in both modes.

* Update SX126xInterface.cpp

Make a change to the tcxoVoltage setting so that TCXO_OPTIONAL works if defined.

* Update variant.h

Added define for TCXO_OPTIONAL and the tcxoVoltage variable.

Added detail on the compatible boards.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-20 06:54:18 -05:00
Ben Meadors
929b3e4f88 Add platformio tests 2024-08-20 06:49:54 -05:00
Jorropo
2043ad3bd0 bin: remove unused imports from readprops.py (#3907)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-20 06:38:16 -05:00
Tom Fifield
2472c7cdc7 JP frequency - 20mW limit, change freqs to avoid duty cycle (#4446)
Thanks to user Goyath on Discord, we discovered that in Japan the
250mW radio level requires licensing, and 20mW is the practical
limit. We also discovered that a duty cycle of 10% is needed on
most frequencies.

    CH 24-38 920.5-923.5 20mW no airtime restrictions
    CH 39-61 923.5-928.1 20mW 10% airtime
2024-08-20 06:20:01 -05:00
todd-herbert
058e9769d6 Add heartbeat LED for HT-VME290 and HT-VME213 (#4511)
* Add heartbeat LED for HT-VME290 and HT-VME213
Not populated on original board, however revisions are now shipping which do have the LED

* Update outdated commenting

* Trunk strikes again
2024-08-20 06:19:29 -05:00
Tom Fifield
3b2c37c47f Remove heltec-specific gps code from main.cpp (#4508)
After the recent GPS power work we have an clear set of
definitions for turning GPS on and off. Rather than manipulating
specific heltec tracker-related pins in main setu, the relevant
power management code in the GPS module will turn things
on/off later as needed.
2024-08-20 06:19:02 -05:00
Mictronics
ef5279e85e Set RP2040 in dormant mode when deep sleep is triggered. (#4510)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

* Fix deprecated macros.

* Set RP2040 in dormant mode when deep sleep is triggered.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-20 06:04:39 -05:00
Ben Meadors
bd21a0455b & 2024-08-19 19:19:16 -05:00
Ben Meadors
9b4ad68f43 Add simulator back as a separate step 2024-08-19 18:39:26 -05:00
Jonathan Bennett
e20d57f3ad Merge pull request #4431 from meshtastic/2.5-changes
2.5 Changes
2024-08-19 18:18:51 -05:00
Jonathan Bennett
48dc222b75 Merge branch 'master' into 2.5-changes 2024-08-19 11:28:20 -05:00
Jonathan Bennett
ab9268cba9 Admin session key debugging messages 2024-08-19 11:12:42 -05:00
Mictronics
6de3ca4301 Fix deprecated macros. (#4505)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

* Fix deprecated macros.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-19 07:09:09 -05:00
Talie5in
e65e79c6c9 We need Millseconds not... rapid fire NeighbourInfo! (#4504) 2024-08-19 07:05:28 -05:00
Ben Meadors
14146d6ff5 Ms! 2024-08-19 07:04:48 -05:00
Jonathan Bennett
273beef148 Re-set the extra-nonce value 2024-08-18 22:25:08 -05:00
Jonathan Bennett
94d5ee9fe6 Deal with adminModule session_time of 0 2024-08-18 22:22:21 -05:00
Jonathan Bennett
7b64c4a5bf Merge branch 'master' into 2.5-changes 2024-08-18 19:14:21 -05:00
Jonathan Bennett
ecb4fb72db Don't break EXCLUDE_PKI 2024-08-18 15:51:43 -05:00
Jonathan Bennett
f439081674 Don't segfault on PKI from unknown nodes 2024-08-18 14:11:39 -05:00
Jonathan Bennett
bfbc4bf93a Set the private_key in crypto when changed by admin 2024-08-18 14:11:17 -05:00
Jonathan Bennett
22e129e716 bluetooth != security; security = security 2024-08-18 13:00:52 -05:00
GUVWAF
a85df199a5 Only accept PKI messages for MQTT downlink if we know transmitter and receiver (#4498) 2024-08-18 12:15:50 -05:00
GUVWAF
7a65c8838d Fall back to default modem preset if requested bandwidth is too large (#4497) 2024-08-18 09:14:23 -05:00
Andre K
e3e36e23f9 add admin getter for SECURITY_CONFIG (#4499) 2024-08-18 09:13:53 -05:00
Rafael Cortês
7129cee944 feature: default to fuzzy GPS location on the Default Channel (#4467)
* feature: default to fuzzy GPS location on the Default Channel

* Default to 13

* 13 default

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-18 07:38:54 -05:00
Jonathan Bennett
23e3e6db92 Add 4 bytes of random nonce to PKI (#4493) 2024-08-18 07:23:56 -05:00
Jonathan Bennett
c7c620ac69 Merge branch 'master' into 2.5-changes 2024-08-17 17:38:39 -05:00
GUVWAF
33ced7e87a Add two-way traceroute result with SNR per hop (#4485)
* Add two-way traceroute result with SNR per hop

* Update protos
2024-08-17 17:34:32 -05:00
Jonathan Bennett
578ac6711b Merge branch 'master' into 2.5-changes 2024-08-17 15:39:41 -05:00
Jonathan Bennett
daddaf7146 Merge branch 'master' into 2.5-changes 2024-08-17 12:24:19 -05:00
Jonathan Bennett
0b010b4fd8 Get STM32 building again by disabling admin module 2024-08-17 11:06:00 -05:00
Jonathan Bennett
f86dde3c40 AdminModule session_passkey (#4478)
* Protobuf

* Adds session_passkey for remote admin changes
2024-08-17 08:41:12 -05:00
Ben Meadors
fdaaf71366 Merge branch 'master' into 2.5-changes 2024-08-17 08:01:15 -05:00
Ben Meadors
9dad62e3c4 Set time-only admin command (#4479) 2024-08-17 05:52:36 -05:00
Jonathan Bennett
7cbae56e6c Merge branch 'master' into 2.5-changes 2024-08-16 19:39:10 -05:00
Jonathan Bennett
6eabbaf432 Add PKI logiv to KNOWN_ONLY and LOCAL_ONLY routing modes. 2024-08-16 19:37:28 -05:00
Jonathan Bennett
cec8233cd1 Don't attempt PKI decryption on broadcast packets 2024-08-16 19:33:06 -05:00
Ben Meadors
e61bd84116 Send local stats telemetry to phone every 15 minutes (#4475)
* Send local stats telemetry to phone every 10 minutes

* Add debug log and bump to 15 minutes

* Tronk

* Explicit has_ optional
2024-08-16 17:15:51 -05:00
Jonathan Bennett
b0c1b7b7b5 MQTT PKI fixes 2024-08-16 10:10:08 -05:00
Ben Meadors
eefe9efa9f Adds ASCII log option needed by portudino (#4443) (#4474)
* Adds ASCII logs useful to portudino

Activates ASCII log option when stdout is not a terminal.  This is
generally the right thing to do; if not, the behavior can be
overridden in config.yaml using AsciiLogs under Logging.  The result
is reasonable system logs for portudino when running under systemd or
the like.

Signed-off-by: Christopher Hoover <ch@murgatroid.com>
Co-authored-by: Christopher Hoover <ch@murgatroid.com>
2024-08-16 07:42:19 -05:00
Ben Meadors
390de724ba Update 2.5 protos 2024-08-16 06:09:02 -05:00
Ben Meadors
6f1dae1b1b Re-compute correct timeslot on applyModemConfig (#4469)
* Re-compute correct timeslot on applyModemConfig

* Cap contention window max at 7
2024-08-15 15:05:38 -05:00
Jonathan Bennett
ef56fae976 Merge branch 'master' into 2.5-changes 2024-08-15 11:21:25 -05:00
Ben Meadors
96cf78aadd Short turbo preset (#4465) 2024-08-14 21:16:21 -05:00
Jonathan Bennett
ced87596cb Add PKI channel for MQTT (#4464)
* Add PKI channel for MQTT
2024-08-14 19:32:45 -05:00
Jonathan Bennett
1be635a797 Merge remote-tracking branch 'origin/master' into 2.5-changes 2024-08-14 19:01:06 -05:00
Jonathan Bennett
36f1a62b0b Merge remote-tracking branch 'origin/2.5-changes' into 2.5-changes 2024-08-14 18:56:41 -05:00
Jonathan Bennett
8ef72a5c08 Shorter nodeinfo timeout redux (#4458)
* Add shorterTimeout bool to sendOurNodeInfo

* Respond to likely PKI decode errors with a quick nodeinfo

* Protbufs

* Move to PKI_UNKNOWN_PUBKEY for PKI decode error
2024-08-14 17:17:53 -05:00
Jonathan Bennett
8ce1c07c4e Check for blank key coming from client 2024-08-13 22:34:21 -05:00
Jonathan Bennett
2661fc694f sync protobufs 2024-08-13 20:06:36 -05:00
Jonathan Bennett
b528290fde Failure returns PKI_FAILED message if client requested PKI 2024-08-13 18:45:40 -05:00
Jonathan Bennett
ff89dca5b3 Add PKI indicator to printPacket 2024-08-13 18:45:40 -05:00
Jonathan Bennett
80fd121d87 Add meshtastic_Routing_Error_NO_CHANNEL 2024-08-13 18:45:40 -05:00
Jonathan Bennett
f3fa8daedf Revert "Add Routing_Error_NONE"
This reverts commit e1985fa0f9.
2024-08-13 18:45:40 -05:00
Jonathan Bennett
bcd77c4523 Cleanup public_keys (#4450) 2024-08-13 18:45:40 -05:00
Jonathan Bennett
308c0a6bb8 Add Routing_Error_NONE 2024-08-13 18:45:40 -05:00
Jonathan Bennett
754db3f2bc Finish fixing config migrate 2024-08-13 18:45:40 -05:00
Jonathan Bennett
c16f20de21 Make "Alloc an error" a LOG_WARN 2024-08-13 18:45:40 -05:00
Jonathan Bennett
b4cbea1b3d Move security migrate to if has_security 2024-08-13 18:45:40 -05:00
Ben Meadors
0e7253d309 Protos 2024-08-13 18:45:38 -05:00
Jonathan Bennett
b91d66b436 Don't forget public_key.size in converting back 2024-08-13 18:44:27 -05:00
Jonathan Bennett
7537b55586 Ungoober oldestBoring 2024-08-13 18:44:27 -05:00
Jonathan Bennett
2d18130235 Don't goober public_key in Userlite conversion 2024-08-13 18:44:27 -05:00
Jonathan Bennett
67ddae2851 Add logic to nodeDB to prefer evicting boring nodes (#4441) 2024-08-13 18:44:27 -05:00
Ben Meadors
884bc529f0 protos 2024-08-13 18:44:25 -05:00
Ben Meadors
8f3614d66c User to UserLite in NodeDB (#4438)
* User to UserLite in the nodedb

* Tronkdor the burninator
2024-08-13 18:43:37 -05:00
Jonathan Bennett
e7dfabc20f Exclude position packets from PKI (at least for now) 2024-08-13 18:43:19 -05:00
Jonathan Bennett
185eb318ad Manual protobuf update 2024-08-13 18:43:19 -05:00
Jonathan Bennett
c86a3200f0 Add missed function rename. (Thanks VSCode) 2024-08-13 18:43:19 -05:00
Jonathan Bennett
c3aa56ef30 Refactor platform cryptography, add tests 2024-08-13 18:43:19 -05:00
Jonathan Bennett
192af05a25 Fix compile on STM32 2024-08-13 18:43:19 -05:00
Jonathan Bennett
26d0b2b477 Add DH25519 unit test 2024-08-13 18:43:19 -05:00
Jonathan Bennett
b726792efd Re-implement PKI from #1509 (#4379)
* Re-implement PKI from #1509
co-authored-by: edinnen <ethanjdinnen@protonmail.com>

* Set the key lengnth to actually make PKI work.

* Remove unused variable and initialize keys to null

* move printBytes() to meshUtils

* Don't reset PKI key son reboot unless needed.

* Remove double encryption for PKI messages

* Cleanup encrypt logic

* Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now.

* Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32

* Fix a crash when node is null

* Don't send PKI encrypted packets while licensed

* use chIndex 8 for PKI

* Don't be so clever, that you corrupt incoming packets

* Pass on channel 8 for now

* Typo

* Lock keys once non-zero

* We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf.

* Lighter approach to retaining known key

* Attach the public key to PKI decrypted packets in device memory

* Turn PKI back off for STM32 :(

* Don't just memcp over a protobuf

* Don't PKI encrypt nodeinfo packets

* Add a bit more memory logging around nodeDB

* Use the proper macro to refer to NODENUM_BROADCAST

* Typo fix

* Don't PKI encrypt ROUTING (naks and acks)

* Adds SecurityConfig protobuf

* Add admin messages over PKI

* Disable PKI for the WIO-e5

* Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k

* Add missed "has_security"

* Add the admin_channel_enabled option

* STM32 again

* add missed configuration.h at the top of files

* Add EXCLUDE_TZ and RTC

* Enable PKI build on STM32 once again

* Attempt 1 at moving PKI to aes-ccm

* Fix buffers for encrypt/decrypt

* Eliminate unused aes variable

* Add debugging lines

* Set hash to 0 for PKI

* Fix debug lines so they don't print pointers.

* logic fix and more debug

* Rather important typo

* Check for short packets before attempting decrypt

* Don't forget to give cryptoEngine the keys!

* Use the right scratch buffer

* Cleanup

* moar cleanups

* Minor hardening

* Remove some in-progress stuff

* Turn PKI back off on STM32

* Return false

* 2.5 protos

* Sync up protos

* Add initial cryptography test vector tests

* re-add MINIMUM_SAFE_FREE_HEAP

* Housekeeping and comment fixes

* Add explanatory comment about weak dh25519 keys

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-13 18:43:19 -05:00
Ben Meadors
c451db3a3f Get in the trunk! 2024-08-13 18:43:19 -05:00
Ben Meadors
95682c9095 Add ClientNotification hello world 2024-08-13 18:43:19 -05:00
Ben Meadors
da53b8152d Protos 2024-08-13 18:43:17 -05:00
Ben Meadors
8d1a34a4bf Protobufs 2024-08-13 18:42:40 -05:00
Jonathan Bennett
9bd293a941 Don't forget public_key.size in converting back 2024-08-12 16:20:07 -05:00
Jonathan Bennett
bc69621c3e Ungoober oldestBoring 2024-08-12 11:37:50 -05:00
Jonathan Bennett
2ee53d1500 Don't goober public_key in Userlite conversion 2024-08-12 11:27:05 -05:00
Jonathan Bennett
bee959150b Add logic to nodeDB to prefer evicting boring nodes (#4441) 2024-08-12 06:43:54 -05:00
Ben Meadors
48eee747da protos 2024-08-11 18:25:32 -05:00
Ben Meadors
a28f10e0c2 User to UserLite in NodeDB (#4438)
* User to UserLite in the nodedb

* Tronkdor the burninator
2024-08-11 17:22:11 -05:00
Jonathan Bennett
0bd17e6da6 Merge branch 'master' into 2.5-changes 2024-08-11 16:28:43 -05:00
Jonathan Bennett
9bc2224164 Exclude position packets from PKI (at least for now) 2024-08-11 14:18:33 -05:00
Jonathan Bennett
e1b4b226c9 Manual protobuf update 2024-08-11 14:12:20 -05:00
Jonathan Bennett
54a2e14e35 Add missed function rename. (Thanks VSCode) 2024-08-10 23:11:04 -05:00
Jonathan Bennett
1cfd5d12d2 Refactor platform cryptography, add tests 2024-08-10 22:38:05 -05:00
Jonathan Bennett
b573e0eacc Fix compile on STM32 2024-08-10 20:04:38 -05:00
Jonathan Bennett
8ca884bafd Add DH25519 unit test 2024-08-10 15:45:29 -05:00
Ben Meadors
864b793ce0 Merge branch 'master' into 2.5-changes 2024-08-10 13:45:52 -05:00
Jonathan Bennett
74afd13171 Re-implement PKI from #1509 (#4379)
* Re-implement PKI from #1509
co-authored-by: edinnen <ethanjdinnen@protonmail.com>

* Set the key lengnth to actually make PKI work.

* Remove unused variable and initialize keys to null

* move printBytes() to meshUtils

* Don't reset PKI key son reboot unless needed.

* Remove double encryption for PKI messages

* Cleanup encrypt logic

* Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now.

* Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32

* Fix a crash when node is null

* Don't send PKI encrypted packets while licensed

* use chIndex 8 for PKI

* Don't be so clever, that you corrupt incoming packets

* Pass on channel 8 for now

* Typo

* Lock keys once non-zero

* We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf.

* Lighter approach to retaining known key

* Attach the public key to PKI decrypted packets in device memory

* Turn PKI back off for STM32 :(

* Don't just memcp over a protobuf

* Don't PKI encrypt nodeinfo packets

* Add a bit more memory logging around nodeDB

* Use the proper macro to refer to NODENUM_BROADCAST

* Typo fix

* Don't PKI encrypt ROUTING (naks and acks)

* Adds SecurityConfig protobuf

* Add admin messages over PKI

* Disable PKI for the WIO-e5

* Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k

* Add missed "has_security"

* Add the admin_channel_enabled option

* STM32 again

* add missed configuration.h at the top of files

* Add EXCLUDE_TZ and RTC

* Enable PKI build on STM32 once again

* Attempt 1 at moving PKI to aes-ccm

* Fix buffers for encrypt/decrypt

* Eliminate unused aes variable

* Add debugging lines

* Set hash to 0 for PKI

* Fix debug lines so they don't print pointers.

* logic fix and more debug

* Rather important typo

* Check for short packets before attempting decrypt

* Don't forget to give cryptoEngine the keys!

* Use the right scratch buffer

* Cleanup

* moar cleanups

* Minor hardening

* Remove some in-progress stuff

* Turn PKI back off on STM32

* Return false

* 2.5 protos

* Sync up protos

* Add initial cryptography test vector tests

* re-add MINIMUM_SAFE_FREE_HEAP

* Housekeeping and comment fixes

* Add explanatory comment about weak dh25519 keys

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-10 13:45:41 -05:00
Ben Meadors
a767997cea Get in the trunk! 2024-08-10 08:57:37 -05:00
Ben Meadors
861f0b6769 Add ClientNotification hello world 2024-08-10 08:33:42 -05:00
Ben Meadors
2012a0ae1c Protos 2024-08-10 07:51:59 -05:00
Ben Meadors
3513d88794 Protobufs 2024-08-10 07:25:05 -05:00
114 changed files with 2742 additions and 711 deletions

24
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12
# [Optional] Uncomment this section to install additional packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends \
ca-certificates \
g++ \
git \
libbluetooth-dev \
libgpiod-dev \
liborcania-dev \
libssl-dev \
libulfius-dev \
libyaml-cpp-dev \
pkg-config \
python3 \
python3-pip \
python3-venv \
python3-wheel \
wget \
zip \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir -U platformio==6.1.15

View File

@@ -0,0 +1,28 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "Meshtastic Firmware Dev",
"build": {
"dockerfile": "Dockerfile"
},
"features": {
"ghcr.io/devcontainers/features/python:1": {
"installTools": true,
"version": "latest"
}
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"platformio.platformio-ide",
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [ 4403 ],
// Run commands to prepare the container for use
"postCreateCommand": ".devcontainer/setup.sh",
}

3
.devcontainer/setup.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env sh
git submodule update --init

View File

@@ -35,6 +35,7 @@ runs:
python -m pip install --upgrade pip
pip install -U --no-build-isolation --no-cache-dir "setuptools<72"
pip install -U platformio adafruit-nrfutil --no-build-isolation
pip install -U poetry --no-build-isolation
pip install -U meshtastic --pre --no-build-isolation
- name: Upgrade platformio

57
.github/workflows/test_simulator.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Test Simulator
on:
schedule:
- cron: "0 0 * * *" # Run every day at midnight
workflow_dispatch: {}
jobs:
test-simulator:
runs-on: ubuntu-latest
steps:
- name: Install libbluetooth
shell: bash
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Build Native
run: bin/build-native.sh
# We now run integration test before other build steps (to quickly see runtime failures)
- name: Build for native
run: platformio run -e native
- name: Integration test
run: |
.pio/build/native/program & sleep 10 # 5 seconds was not enough
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
- name: PlatformIO Tests
run: platformio test -e native --junit-output-path testreport.xml
- name: Test Report
uses: dorny/test-reporter@v1.9.1
if: success() || failure() # run this step even if previous step failed
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit

2
.gitpod.yml Normal file
View File

@@ -0,0 +1,2 @@
tasks:
- init: pip install platformio && pip install --upgrade pip

View File

@@ -48,6 +48,7 @@ lib_deps =
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
rweather/Crypto@^0.4.0
lib_ignore =
segger_rtt

View File

@@ -20,6 +20,7 @@ build_src_filter =
lib_deps=
${arduino_base.lib_deps}
rweather/Crypto@^0.4.0
lib_ignore =
BluetoothOTA

View File

@@ -1,9 +1,5 @@
import subprocess
import configparser
import traceback
import sys
import subprocess
def readProps(prefsLoc):
@@ -11,27 +7,36 @@ def readProps(prefsLoc):
config = configparser.RawConfigParser()
config.read(prefsLoc)
version = dict(config.items('VERSION'))
verObj = dict(short = "{}.{}.{}".format(version["major"], version["minor"], version["build"]),
long = "unset")
version = dict(config.items("VERSION"))
verObj = dict(
short="{}.{}.{}".format(version["major"], version["minor"], version["build"]),
long="unset",
)
# Try to find current build SHA if if the workspace is clean. This could fail if git is not installed
try:
sha = subprocess.check_output(
['git', 'rev-parse', '--short', 'HEAD']).decode("utf-8").strip()
isDirty = subprocess.check_output(
['git', 'diff', 'HEAD']).decode("utf-8").strip()
sha = (
subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
.decode("utf-8")
.strip()
)
isDirty = (
subprocess.check_output(["git", "diff", "HEAD"]).decode("utf-8").strip()
)
suffix = sha
# if isDirty:
# # short for 'dirty', we want to keep our verstrings source for protobuf reasons
# suffix = sha + "-d"
verObj['long'] = "{}.{}.{}.{}".format(
version["major"], version["minor"], version["build"], suffix)
verObj["long"] = "{}.{}.{}.{}".format(
version["major"], version["minor"], version["build"], suffix
)
except:
# print("Unexpected error:", sys.exc_info()[0])
# traceback.print_exc()
verObj['long'] = verObj['short']
verObj["long"] = verObj["short"]
# print("firmware version " + verStr)
return verObj
# print("path is" + ','.join(sys.path))
# print("path is" + ','.join(sys.path))

View File

@@ -34,6 +34,7 @@ default_envs = tbeam
;default_envs = wio-e5
;default_envs = radiomaster_900_bandit_nano
;default_envs = radiomaster_900_bandit_micro
;default_envs = radiomaster_900_bandit
;default_envs = heltec_capsule_sensor_v3
;default_envs = heltec_vision_master_t190
;default_envs = heltec_vision_master_e213
@@ -45,6 +46,7 @@ extra_configs =
variants/*/platformio.ini
[env]
test_build_src = true
extra_scripts = bin/platformio-custom.py
; note: we add src to our include search path so that lmic_project_config can override

View File

@@ -1,3 +1,4 @@
#include "Observer.h"
#include "configuration.h"
#ifdef HAS_NCP5623
@@ -22,10 +23,18 @@ class AmbientLightingThread : public concurrency::OSThread
public:
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread")
{
notifyDeepSleepObserver.observe(&notifyDeepSleep); // Let us know when shutdown() is issued.
// Enables Ambient Lighting by default if conditions are meet.
#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
#ifdef ENABLE_AMBIENTLIGHTING
moduleConfig.ambient_lighting.led_state = true;
#endif
#endif
// Uncomment to test module
// moduleConfig.ambient_lighting.led_state = true;
// moduleConfig.ambient_lighting.current = 10;
// // Default to a color based on our node number
// Default to a color based on our node number
// moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
// moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
// moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
@@ -82,9 +91,46 @@ class AmbientLightingThread : public concurrency::OSThread
return disable();
}
// When shutdown() is issued, setLightingOff will be called.
CallbackObserver<AmbientLightingThread, void *> notifyDeepSleepObserver =
CallbackObserver<AmbientLightingThread, void *>(this, &AmbientLightingThread::setLightingOff);
private:
ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
// Turn RGB lighting off, is used in junction to shutdown()
int setLightingOff(void *unused)
{
#ifdef HAS_NCP5623
rgb.setCurrent(0);
rgb.setRed(0);
rgb.setGreen(0);
rgb.setBlue(0);
LOG_INFO("Turn Off NCP5623 Ambient lighting.\n");
#endif
#ifdef HAS_NEOPIXEL
pixels.clear();
pixels.show();
LOG_INFO("Turn Off NeoPixel Ambient lighting.\n");
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255 - 0);
analogWrite(RGBLED_GREEN, 255 - 0);
analogWrite(RGBLED_BLUE, 255 - 0);
LOG_INFO("Turn Off Ambient lighting RGB Common Anode.\n");
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, 0);
analogWrite(RGBLED_GREEN, 0);
analogWrite(RGBLED_BLUE, 0);
LOG_INFO("Turn Off Ambient lighting RGB Common Cathode.\n");
#endif
#ifdef UNPHONE
unphone.rgb(0, 0, 0);
LOG_INFO("Turn Off unPhone Ambient lighting.\n");
#endif
return 0;
}
void setLighting()
{
#ifdef HAS_NCP5623
@@ -100,6 +146,17 @@ class AmbientLightingThread : public concurrency::OSThread
pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
moduleConfig.ambient_lighting.blue),
0, NEOPIXEL_COUNT);
// RadioMaster Bandit has addressable LED at the two buttons
// this allow us to set different lighting for them in variant.h file.
#ifdef RADIOMASTER_900_BANDIT
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
#endif
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
pixels.fill(BUTTON2_COLOR, BUTTON1_COLOR_INDEX, 1);
#endif
#endif
pixels.show();
LOG_DEBUG("Initializing NeoPixel Ambient lighting w/ brightness(current)=%d, red=%d, green=%d, blue=%d\n",
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,

View File

@@ -45,7 +45,7 @@
#define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING)
#define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__)
#define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__)

View File

@@ -3,6 +3,9 @@
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
{
switch (preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
return useShortName ? "ShortT" : "ShortTurbo";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
return useShortName ? "ShortS" : "ShortSlow";
break;

View File

@@ -249,6 +249,7 @@ void listDir(const char *dirname, uint8_t levels, bool del)
file.close();
}
#else
LOG_DEBUG(" %s (directory)\n", file.name());
listDir(file.name(), levels - 1, del);
file.close();
#endif
@@ -275,7 +276,7 @@ void listDir(const char *dirname, uint8_t levels, bool del)
file.close();
}
#else
LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size());
LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size());
file.close();
#endif
}

View File

@@ -38,8 +38,9 @@ size_t RedirectablePrint::write(uint8_t c)
#ifdef USE_SEGGER
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
#endif
if (!config.has_lora || config.device.serial_enabled)
// Account for legacy config transition
bool serialEnabled = config.has_security ? config.security.serial_enabled : config.device.serial_enabled;
if (!config.has_lora || serialEnabled)
dest->write(c);
return 1; // We always claim one was written, rather than trusting what the
@@ -212,7 +213,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format,
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg)
{
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) {
bool isBleConnected = false;
#ifdef ARCH_ESP32
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();

View File

@@ -83,7 +83,7 @@ bool SerialConsole::checkIsConnected()
bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
// only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled.
if (config.has_lora && config.device.serial_enabled) {
if (config.has_lora && config.security.serial_enabled) {
// Switch to protobufs for log messages
usingProtobufs = true;
canWrite = true;
@@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
{
if (usingProtobufs && config.device.debug_log_enabled) {
if (usingProtobufs && config.security.debug_log_api_enabled) {
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
switch (logLevel[0]) {
case 'D':

View File

@@ -193,6 +193,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DEFAULT_SHUTDOWN_SECONDS 2
#endif
#ifndef MINIMUM_SAFE_FREE_HEAP
#define MINIMUM_SAFE_FREE_HEAP 1500
#endif
/* Step #3: mop up with disabled values for HAS_ options not handled by the above two */
#ifndef HAS_WIFI
@@ -256,6 +260,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_MQTT 1
#define MESHTASTIC_EXCLUDE_POWERMON 1
#define MESHTASTIC_EXCLUDE_I2C 1
#define MESHTASTIC_EXCLUDE_PKI 1
#define MESHTASTIC_EXCLUDE_POWER_FSM 1
#define MESHTASTIC_EXCLUDE_TZ 1
#endif
@@ -280,6 +285,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
#define MESHTASTIC_EXCLUDE_SERIAL 1
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
#define MESHTASTIC_EXCLUDE_ADMIN 1
#endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)

View File

@@ -315,7 +315,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case SHT31_4x_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da) {
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) {
type = SHT4X;
LOG_INFO("SHT4X sensor found\n");
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
@@ -404,4 +404,4 @@ size_t ScanI2CTwoWire::countDevices() const
{
return foundDevices.size();
}
#endif
#endif

View File

@@ -112,6 +112,10 @@ AccelerometerThread *accelerometerThread = nullptr;
AudioThread *audioThread = nullptr;
#endif
#if defined(TCXO_OPTIONAL)
float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this here so it can be changed further down.
#endif
using namespace concurrency;
// We always create a screen object, but we only init it if we find the hardware
@@ -227,7 +231,7 @@ void printInfo()
{
LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION));
}
#ifndef PIO_UNIT_TESTING
void setup()
{
concurrency::hasBeenSetup = true;
@@ -294,21 +298,11 @@ void setup()
digitalWrite(VEXT_ENABLE, 0); // turn on the display power
#endif
#if defined(VGNSS_CTRL_V03)
pinMode(VGNSS_CTRL_V03, OUTPUT);
digitalWrite(VGNSS_CTRL_V03, LOW);
#endif
#if defined(VTFT_CTRL_V03)
pinMode(VTFT_CTRL_V03, OUTPUT);
digitalWrite(VTFT_CTRL_V03, LOW);
#endif
#if defined(VGNSS_CTRL)
pinMode(VGNSS_CTRL, OUTPUT);
digitalWrite(VGNSS_CTRL, LOW);
#endif
#if defined(VTFT_CTRL)
pinMode(VTFT_CTRL, OUTPUT);
digitalWrite(VTFT_CTRL, LOW);
@@ -900,7 +894,7 @@ void setup()
}
#endif
#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO)
#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL)
if (!rIf) {
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
@@ -914,6 +908,40 @@ void setup()
}
#endif
#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL)
if (!rIf) {
// Try using the specified TCXO voltage
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
LOG_WARN("Failed to find SX1262 radio with TCXO using DIO3 reference voltage at %f V\n", tcxoVoltage);
delete rIf;
rIf = NULL;
tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt
} else {
LOG_INFO("SX1262 Radio init succeeded, using ");
LOG_WARN("SX1262 Radio with TCXO");
LOG_INFO(", reference voltage at %f V\n", tcxoVoltage);
radioType = SX1262_RADIO;
}
}
if (!rIf) {
// If specified TCXO voltage fails, attempt to use DIO3 as a reference instea
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
LOG_WARN("Failed to find SX1262 radio with XTAL using DIO3 reference voltage at %f V\n", tcxoVoltage);
delete rIf;
rIf = NULL;
tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search
} else {
LOG_INFO("SX1262 Radio init succeeded, using ");
LOG_WARN("SX1262 Radio with XTAL");
LOG_INFO(", reference voltage at %f V\n", tcxoVoltage);
radioType = SX1262_RADIO;
}
}
#endif
#if defined(USE_SX1268)
if (!rIf) {
rIf = new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
@@ -1052,7 +1080,7 @@ void setup()
powerFSMthread = new PowerFSMThread();
setCPUFast(false); // 80MHz is fine for our slow peripherals
}
#endif
uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client)
@@ -1075,7 +1103,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
return deviceMetadata;
}
#ifndef PIO_UNIT_TESTING
void loop()
{
runASAP = false;
@@ -1120,4 +1148,5 @@ void loop()
mainDelay.delay(delayMsec);
}
// if (didWake) LOG_DEBUG("wake!\n");
}
}
#endif

View File

@@ -97,7 +97,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
channelSettings.psk.bytes[0] = defaultpskIndex;
channelSettings.psk.size = 1;
strncpy(channelSettings.name, "", sizeof(channelSettings.name));
channelSettings.module_settings.position_precision = 32; // default to sending location on the primary channel
channelSettings.module_settings.position_precision = 13; // default to sending location on the primary channel
channelSettings.has_module_settings = true;
ch.has_settings = true;
@@ -363,4 +363,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
int16_t Channels::setActiveByIndex(ChannelIndex channelIndex)
{
return setCrypto(channelIndex);
}
}

View File

@@ -1,6 +1,191 @@
#include "CryptoEngine.h"
#include "NodeDB.h"
#include "RadioInterface.h"
#include "architecture.h"
#include "configuration.h"
#if !(MESHTASTIC_EXCLUDE_PKI)
#include "aes-ccm.h"
#include "meshUtils.h"
#include <Crypto.h>
#include <Curve25519.h>
#include <SHA256.h>
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
/**
* Create a public/private key pair with Curve25519.
*
* @param pubKey The destination for the public key.
* @param privKey The destination for the private key.
*/
void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey)
{
LOG_DEBUG("Generating Curve25519 key pair...\n");
Curve25519::dh1(public_key, private_key);
memcpy(pubKey, public_key, sizeof(public_key));
memcpy(privKey, private_key, sizeof(private_key));
}
#endif
void CryptoEngine::clearKeys()
{
memset(public_key, 0, sizeof(public_key));
memset(private_key, 0, sizeof(private_key));
}
/**
* Encrypt a packet's payload using a key generated with Curve25519 and SHA256
* for a specific node.
*
* @param bytes is updated in place
*/
bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes,
uint8_t *bytesOut)
{
uint8_t *auth;
uint32_t *extraNonce;
long extraNonceTmp = random();
auth = bytesOut + numBytes;
extraNonce = (uint32_t *)(auth + 8);
*extraNonce = extraNonceTmp;
LOG_INFO("Random nonce value: %d\n", *extraNonce);
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode);
if (node->num < 1 || node->user.public_key.size == 0) {
LOG_DEBUG("Node %d or their public_key not found\n", toNode);
return false;
}
if (!crypto->setDHKey(toNode)) {
return false;
}
initNonce(fromNode, packetNum, *extraNonce);
// Calculate the shared secret with the destination node and encrypt
printBytes("Attempting encrypt using nonce: ", nonce, 13);
printBytes("Attempting encrypt using shared_key: ", shared_key, 32);
aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut,
auth); // this can write up to 15 bytes longer than numbytes past bytesOut
*extraNonce = extraNonceTmp;
return true;
}
/**
* Decrypt a packet's payload using a key generated with Curve25519 and SHA256
* for a specific node.
*
* @param bytes is updated in place
*/
bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut)
{
uint8_t *auth; // set to last 8 bytes of text?
uint32_t *extraNonce;
auth = bytes + numBytes - 12;
extraNonce = (uint32_t *)(auth + 8);
LOG_INFO("Random nonce value: %d\n", *extraNonce);
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode);
if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) {
LOG_DEBUG("Node or its public key not found in database\n");
return false;
}
// Calculate the shared secret with the sending node and decrypt
if (!crypto->setDHKey(fromNode)) {
return false;
}
initNonce(fromNode, packetNum, *extraNonce);
printBytes("Attempting decrypt using nonce: ", nonce, 13);
printBytes("Attempting decrypt using shared_key: ", shared_key, 32);
return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut);
}
void CryptoEngine::setDHPrivateKey(uint8_t *_private_key)
{
memcpy(private_key, _private_key, 32);
}
/**
* Set the PKI key used for encrypt, decrypt.
*
* @param nodeNum the node number of the node who's public key we want to use
*/
bool CryptoEngine::setDHKey(uint32_t nodeNum)
{
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
if (node->num < 1 || node->user.public_key.size == 0) {
LOG_DEBUG("Node %d or their public_key not found\n", nodeNum);
return false;
}
if (!setDHPublicKey(node->user.public_key.bytes))
return false;
printBytes("DH Output: ", shared_key, 32);
/**
* D.J. Bernstein reccomends hashing the shared key. We want to do this because there are
* at least 128 bits of entropy in the 256-bit output of the DH key exchange, but we don't
* really know where. If you extract, for instance, the first 128 bits with basic truncation,
* then you don't know if you got all of your 128 entropy bits, or less, possibly much less.
*
* No exploitable bias is really known at that point, but we know enough to be wary.
* Hashing the DH output is a simple and safe way to gather all the entropy and spread
* it around as needed.
*/
crypto->hash(shared_key, 32);
return true;
}
/**
* Hash arbitrary data using SHA256.
*
* @param bytes
* @param numBytes
*/
void CryptoEngine::hash(uint8_t *bytes, size_t numBytes)
{
SHA256 hash;
size_t posn, len;
uint8_t size = numBytes;
uint8_t inc = 16;
hash.reset();
for (posn = 0; posn < size; posn += inc) {
len = size - posn;
if (len > inc)
len = inc;
hash.update(bytes + posn, len);
}
hash.finalize(bytes, 32);
}
void CryptoEngine::aesSetKey(const uint8_t *key_bytes, size_t key_len)
{
if (aes) {
delete aes;
aes = nullptr;
}
if (key_len != 0) {
aes = new AESSmall256();
aes->setKey(key_bytes, key_len);
}
}
void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out)
{
aes->encryptBlock(out, in);
}
bool CryptoEngine::setDHPublicKey(uint8_t *pubKey)
{
uint8_t local_priv[32];
memcpy(shared_key, pubKey, 32);
memcpy(local_priv, private_key, 32);
// Calculate the shared secret with the specified node's public key and our private key
// This includes an internal weak key check, which among other things looks for an all 0 public key and shared key.
if (!Curve25519::dh2(shared_key, local_priv)) {
LOG_WARN("Curve25519DH step 2 failed!\n");
return false;
}
return true;
}
#endif
concurrency::Lock *cryptLock;
void CryptoEngine::setKey(const CryptoKey &k)
@@ -14,24 +199,59 @@ void CryptoEngine::setKey(const CryptoKey &k)
*
* @param bytes is updated in place
*/
void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes)
void CryptoEngine::encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes)
{
LOG_WARN("noop encryption!\n");
if (key.length > 0) {
initNonce(fromNode, packetId);
if (numBytes <= MAX_BLOCKSIZE) {
encryptAESCtr(key, nonce, numBytes, bytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes)
{
LOG_WARN("noop decryption!\n");
// For CTR, the implementation is the same
encryptPacket(fromNode, packetId, numBytes, bytes);
}
// Generic implementation of AES-CTR encryption.
void CryptoEngine::encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes)
{
if (ctr) {
delete ctr;
ctr = nullptr;
}
if (_key.length == 16)
ctr = new CTR<AES128>();
else
ctr = new CTR<AES256>();
ctr->setKey(_key.bytes, _key.length);
static uint8_t scratch[MAX_BLOCKSIZE];
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
ctr->setIV(_nonce, 16);
ctr->setCounterSize(4);
ctr->encrypt(bytes, scratch, numBytes);
}
/**
* Init our 128 bit nonce for a new packet
*/
void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId)
void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce)
{
memset(nonce, 0, sizeof(nonce));
// use memcpy to avoid breaking strict-aliasing
memcpy(nonce, &packetId, sizeof(uint64_t));
memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t));
}
if (extraNonce)
memcpy(nonce + sizeof(uint32_t), &extraNonce, sizeof(uint32_t));
}
#ifndef HAS_CUSTOM_CRYPTO_ENGINE
CryptoEngine *crypto = new CryptoEngine;
#endif

View File

@@ -1,6 +1,9 @@
#pragma once
#include "AES.h"
#include "CTR.h"
#include "concurrency/LockGuard.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include <Arduino.h>
extern concurrency::Lock *cryptLock;
@@ -21,14 +24,31 @@ struct CryptoKey {
class CryptoEngine
{
protected:
/** Our per packet nonce */
uint8_t nonce[16] = {0};
CryptoKey key = {};
public:
#if !(MESHTASTIC_EXCLUDE_PKI)
uint8_t public_key[32] = {0};
#endif
virtual ~CryptoEngine() {}
#if !(MESHTASTIC_EXCLUDE_PKI)
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
#endif
void clearKeys();
void setDHPrivateKey(uint8_t *_private_key);
virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes,
uint8_t *bytesOut);
virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut);
bool setDHKey(uint32_t nodeNum);
virtual bool setDHPublicKey(uint8_t *publicKey);
virtual void hash(uint8_t *bytes, size_t numBytes);
virtual void aesSetKey(const uint8_t *key, size_t key_len);
virtual void aesEncrypt(uint8_t *in, uint8_t *out);
AESSmall256 *aes = NULL;
#endif
/**
* Set the key used for encrypt, decrypt.
@@ -46,10 +66,20 @@ class CryptoEngine
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes);
virtual void encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes);
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes);
virtual void encryptAESCtr(CryptoKey key, uint8_t *nonce, size_t numBytes, uint8_t *bytes);
#ifndef PIO_UNIT_TESTING
protected:
#endif
/** Our per packet nonce */
uint8_t nonce[16] = {0};
CryptoKey key = {};
CTRCommon *ctr = NULL;
#if !(MESHTASTIC_EXCLUDE_PKI)
uint8_t shared_key[32] = {0};
uint8_t private_key[32] = {0};
#endif
/**
* Init our 128 bit nonce for a new packet
*
@@ -58,7 +88,7 @@ class CryptoEngine
* a 32 bit sending node number (stored in little endian order)
* a 32 bit block counter (starts at zero)
*/
void initNonce(uint32_t fromNode, uint64_t packetId);
void initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce = 0);
};
extern CryptoEngine *crypto;
extern CryptoEngine *crypto;

View File

@@ -55,7 +55,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
p->decoded.request_id = idFrom;
p->channel = chIndex;
if (err != meshtastic_Routing_Error_NONE)
LOG_ERROR("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
return p;
}

View File

@@ -48,14 +48,19 @@ static MemoryDynamic<meshtastic_MqttClientProxyMessage> staticMqttClientProxyMes
static MemoryDynamic<meshtastic_QueueStatus> staticQueueStatusPool;
static MemoryDynamic<meshtastic_ClientNotification> staticClientNotificationPool;
Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool;
Allocator<meshtastic_ClientNotification> &clientNotificationPool = staticClientNotificationPool;
Allocator<meshtastic_QueueStatus> &queueStatusPool = staticQueueStatusPool;
#include "Router.h"
MeshService::MeshService()
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE)
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE),
toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2)
{
lastQueueStatus = {0, 0, 16, 0};
}
@@ -324,6 +329,20 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage
fromNum++;
}
void MeshService::sendClientNotification(meshtastic_ClientNotification *n)
{
LOG_DEBUG("Sending client notification to phone\n");
if (toPhoneClientNotificationQueue.numFree() == 0) {
LOG_WARN("ClientNotification queue is full, discarding oldest\n");
meshtastic_ClientNotification *d = toPhoneClientNotificationQueue.dequeuePtr(0);
if (d)
releaseClientNotificationToPool(d);
}
assert(toPhoneClientNotificationQueue.enqueue(n, 0));
fromNum++;
}
meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
{
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());

View File

@@ -21,6 +21,7 @@
extern Allocator<meshtastic_QueueStatus> &queueStatusPool;
extern Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool;
extern Allocator<meshtastic_ClientNotification> &clientNotificationPool;
/**
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
@@ -44,6 +45,9 @@ class MeshService
// keep list of MqttClientProxyMessages to be send to the client for delivery
PointerQueue<meshtastic_MqttClientProxyMessage> toPhoneMqttProxyQueue;
// keep list of ClientNotifications to be send to the client (phone)
PointerQueue<meshtastic_ClientNotification> toPhoneClientNotificationQueue;
// This holds the last QueueStatus send
meshtastic_QueueStatus lastQueueStatus;
@@ -97,6 +101,9 @@ class MeshService
// Release MqttClientProxyMessage packet to pool
void releaseMqttClientProxyMessageToPool(meshtastic_MqttClientProxyMessage *p) { mqttClientProxyMessagePool.release(p); }
/// Release the next ClientNotification packet to pool.
void releaseClientNotificationToPool(meshtastic_ClientNotification *p) { clientNotificationPool.release(p); }
/**
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep
@@ -134,6 +141,9 @@ class MeshService
/// Send an MQTT message to the phone for client proxying
void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m);
/// Send a ClientNotification to the phone
void sendClientNotification(meshtastic_ClientNotification *cn);
bool isToPhoneQueueEmpty();
ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);

View File

@@ -19,6 +19,7 @@
#include "error.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshUtils.h"
#include "modules/NeighborInfoModule.h"
#include <ErriezCRC32.h>
#include <algorithm>
@@ -123,7 +124,37 @@ NodeDB::NodeDB()
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
info->user = owner;
if (!config.has_security) {
config.has_security = true;
config.security.serial_enabled = config.device.serial_enabled;
config.security.is_managed = config.device.is_managed;
}
#if !(MESHTASTIC_EXCLUDE_PKI)
// Calculate Curve25519 public and private keys
printBytes("Old Pubkey", config.security.public_key.bytes, 32);
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
LOG_INFO("Using saved PKI keys\n");
owner.public_key.size = config.security.public_key.size;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
crypto->setDHPrivateKey(config.security.private_key.bytes);
} else {
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
LOG_INFO("Generating new PKI keys\n");
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
config.security.public_key.size = 32;
config.security.private_key.size = 32;
printBytes("New Pubkey", config.security.public_key.bytes, 32);
owner.public_key.size = 32;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
#else
LOG_INFO("No PKI keys set, and generation disabled!\n");
#endif
}
#endif
info->user = TypeConversions::ConvertToUserLite(owner);
info->has_user = true;
#ifdef ARCH_ESP32
@@ -237,6 +268,7 @@ void NodeDB::installDefaultConfig()
config.has_power = true;
config.has_network = true;
config.has_bluetooth = (HAS_BLUETOOTH ? true : false);
config.has_security = true;
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
config.lora.sx126x_rx_boosted_gain = true;
@@ -259,6 +291,14 @@ void NodeDB::installDefaultConfig()
#else
config.lora.ignore_mqtt = false;
#endif
#ifdef ADMIN_KEY_USERPREFS
memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32);
config.security.admin_key.size = 32;
#else
config.security.admin_key.size = 0;
#endif
config.security.public_key.size = 0;
config.security.private_key.size = 0;
#ifdef PIN_GPS_EN
config.position.gps_en_gpio = PIN_GPS_EN;
#endif
@@ -282,7 +322,8 @@ void NodeDB::installDefaultConfig()
config.position.broadcast_smart_minimum_interval_secs = 30;
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER)
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
config.device.serial_enabled = true;
config.security.serial_enabled = true;
config.security.admin_channel_enabled = false;
resetRadioConfig();
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
// FIXME: Default to bluetooth capability of platform as default
@@ -512,10 +553,16 @@ void NodeDB::cleanupMeshDB()
{
int newPos = 0, removed = 0;
for (int i = 0; i < numMeshNodes; i++) {
if (meshNodes->at(i).has_user)
if (meshNodes->at(i).has_user) {
if (meshNodes->at(i).user.public_key.size > 0) {
if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) {
meshNodes->at(i).user.public_key.size = 0;
}
}
meshNodes->at(newPos++) = meshNodes->at(i);
else
} else {
removed++;
}
}
numMeshNodes -= removed;
std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + removed,
@@ -778,6 +825,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
config.has_power = true;
config.has_network = true;
config.has_bluetooth = true;
config.has_security = true;
success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
}
@@ -957,23 +1005,39 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
/** Update user info and channel for this node based on received user data
*/
bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex)
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
{
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
if (!info) {
return false;
}
LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel);
LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel);
#if !(MESHTASTIC_EXCLUDE_PKI)
if (p.public_key.size > 0) {
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one
LOG_INFO("Public Key set for node, not updateing!\n");
// we copy the key into the incoming packet, to prevent overwrite
memcpy(p.public_key.bytes, info->user.public_key.bytes, 32);
} else {
LOG_INFO("Updating Node Pubkey!\n");
}
}
#endif
// Both of info->user and p start as filled with zero so I think this is okay
bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex);
auto lite = TypeConversions::ConvertToUserLite(p);
bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex);
info->user = p;
info->user = lite;
if (info->user.public_key.size == 32) {
printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32);
}
if (nodeId != getNodeNum())
info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel)
LOG_DEBUG("updating changed=%d user %s/%s/%s, channel=%d\n", changed, info->user.id, info->user.long_name,
info->user.short_name, info->channel);
LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name,
info->channel);
info->has_user = true;
if (changed) {
@@ -1042,19 +1106,32 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
meshtastic_NodeInfoLite *lite = getMeshNode(n);
if (!lite) {
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) {
if (screen)
screen->print("Warn: node database full!\nErasing oldest entry\n");
LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes,
memGet.getFreeHeap());
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
uint32_t oldestBoring = UINT32_MAX;
int oldestIndex = -1;
int oldestBoringIndex = -1;
for (int i = 1; i < numMeshNodes; i++) {
// Simply the oldest non-favorite node
if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) {
oldest = meshNodes->at(i).last_heard;
oldestIndex = i;
}
// The oldest "boring" node
if (!meshNodes->at(i).is_favorite && meshNodes->at(i).user.public_key.size == 0 &&
meshNodes->at(i).last_heard < oldestBoring) {
oldestBoring = meshNodes->at(i).last_heard;
oldestBoringIndex = i;
}
}
// if we found a "boring" node, evict it
if (oldestBoringIndex != -1) {
oldestIndex = oldestBoringIndex;
}
// Shove the remaining nodes down the chain
for (int i = oldestIndex; i < numMeshNodes - 1; i++) {
@@ -1068,6 +1145,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
// everything is missing except the nodenum
memset(lite, 0, sizeof(*lite));
lite->num = n;
LOG_INFO("Adding node to database with %i nodes and %i bytes free!\n", numMeshNodes, memGet.getFreeHeap());
}
return lite;

View File

@@ -98,7 +98,7 @@ class NodeDB
/** Update user info and channel for this node based on received user data
*/
bool updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex = 0);
bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0);
/// @return our node number
NodeNum getNodeNum() { return myNodeInfo.my_node_num; }

View File

@@ -255,6 +255,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag;
fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth;
break;
case meshtastic_Config_security_tag:
fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag;
fromRadioScratch.config.payload_variant.security = config.security;
break;
default:
LOG_ERROR("Unknown config type %d\n", config_state);
}

View File

@@ -66,6 +66,9 @@ class PhoneAPI
// Keep MqttClientProxyMessage packet just as packetForPhone
meshtastic_MqttClientProxyMessage *mqttClientProxyMessageForPhone = NULL;
// Keep ClientNotification packet just as packetForPhone
meshtastic_ClientNotification *clientNotification = NULL;
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default;

View File

@@ -16,7 +16,7 @@
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
// if you set power to something higher than 17 or 20 you might fry your board.
#ifdef RADIOMASTER_900_BANDIT_NANO
#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT)
// Structure to hold DAC and DB values
typedef struct {
uint8_t dac;
@@ -40,12 +40,23 @@ DACDB getDACandDB(uint8_t dbm)
static const struct {
uint8_t dbm;
DACDB values;
} dbmToDACDB[] = {
}
#ifdef RADIOMASTER_900_BANDIT_NANO
dbmToDACDB[] = {
{20, {168, 2}}, // 100mW
{24, {148, 6}}, // 250mW
{27, {128, 9}}, // 500mW
{30, {90, 12}} // 1000mW
};
#endif
#ifdef RADIOMASTER_900_BANDIT
dbmToDACDB[] = {
{20, {165, 2}}, // 100mW
{24, {155, 6}}, // 250mW
{27, {142, 9}}, // 500mW
{30, {110, 10}} // 1000mW
};
#endif
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
// Find the interval dbm falls within and interpolate
@@ -56,7 +67,12 @@ DACDB getDACandDB(uint8_t dbm)
}
// Return a default value if no match is found and default to 100mW
#ifdef RADIOMASTER_900_BANDIT_NANO
DACDB defaultValue = {168, 2};
#endif
#ifdef RADIOMASTER_900_BANDIT
DACDB defaultValue = {165, 2};
#endif
return defaultValue;
}
#endif
@@ -95,7 +111,7 @@ bool RF95Interface::init()
{
RadioLibInterface::init();
#ifdef RADIOMASTER_900_BANDIT_NANO
#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT)
// DAC and DB values based on dBm using interpolation
DACDB dacDbValues = getDACandDB(power);
int8_t powerDAC = dacDbValues.dac;
@@ -117,7 +133,7 @@ bool RF95Interface::init()
// enable PA
#ifdef RF95_PA_EN
#if defined(RF95_PA_DAC_EN)
#ifdef RADIOMASTER_900_BANDIT_NANO
#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT)
// Use calculated DAC value
dacWrite(RF95_PA_EN, powerDAC);
#else
@@ -163,7 +179,7 @@ bool RF95Interface::init()
LOG_INFO("Frequency set to %f\n", getFreq());
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
#ifdef RADIOMASTER_900_BANDIT_NANO
#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT)
LOG_INFO("DAC output set to %d\n", powerDAC);
#endif

View File

@@ -53,8 +53,10 @@ const RegionInfo regions[] = {
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
https://www.arib.or.jp/english/html/overview/doc/5-STD-T108v1_5-E1.pdf
https://qiita.com/ammo0613/items/d952154f1195b64dc29f
*/
RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false, false),
RDEF(JP, 920.5f, 923.5f, 100, 0, 13, true, false, false),
/*
https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf
@@ -283,6 +285,9 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
if (s.want_response)
out += DEBUG_PORT.mt_sprintf(" WANTRESP");
if (p->pki_encrypted)
out += DEBUG_PORT.mt_sprintf(" PKI");
if (s.source != 0)
out += DEBUG_PORT.mt_sprintf(" source=%08x", s.source);
@@ -409,67 +414,93 @@ void RadioInterface::applyModemConfig()
// Set up default configuration
// No Sync Words in LORA mode
meshtastic_Config_LoRaConfig &loraConfig = config.lora;
if (loraConfig.use_preset) {
bool validConfig = false; // We need to check for a valid configuration
while (!validConfig) {
if (loraConfig.use_preset) {
switch (loraConfig.modem_preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 7;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 8;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 9;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 10;
break;
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 11;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
bw = (myRegion->wideLora) ? 406.25 : 125;
cr = 8;
sf = 11;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
bw = (myRegion->wideLora) ? 406.25 : 125;
cr = 8;
sf = 12;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
bw = (myRegion->wideLora) ? 203.125 : 62.5;
cr = 8;
sf = 12;
break;
switch (loraConfig.modem_preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
bw = (myRegion->wideLora) ? 812.5 : 500;
cr = 5;
sf = 7;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 7;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 8;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 9;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 10;
break;
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5;
sf = 11;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
bw = (myRegion->wideLora) ? 406.25 : 125;
cr = 8;
sf = 11;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
bw = (myRegion->wideLora) ? 406.25 : 125;
cr = 8;
sf = 12;
break;
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
bw = (myRegion->wideLora) ? 203.125 : 62.5;
cr = 8;
sf = 12;
break;
}
} else {
sf = loraConfig.spread_factor;
cr = loraConfig.coding_rate;
bw = loraConfig.bandwidth;
if (bw == 31) // This parameter is not an integer
bw = 31.25;
if (bw == 62) // Fix for 62.5Khz bandwidth
bw = 62.5;
if (bw == 200)
bw = 203.125;
if (bw == 400)
bw = 406.25;
if (bw == 800)
bw = 812.5;
if (bw == 1600)
bw = 1625.0;
}
} else {
sf = loraConfig.spread_factor;
cr = loraConfig.coding_rate;
bw = loraConfig.bandwidth;
if (bw == 31) // This parameter is not an integer
bw = 31.25;
if (bw == 62) // Fix for 62.5Khz bandwidth
bw = 62.5;
if (bw == 200)
bw = 203.125;
if (bw == 400)
bw = 406.25;
if (bw == 800)
bw = 812.5;
if (bw == 1600)
bw = 1625.0;
if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
static const char *err_string =
"Regional frequency range is smaller than bandwidth. Falling back to default preset.\n";
LOG_ERROR(err_string);
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_ERROR;
sprintf(cn->message, err_string);
service->sendClientNotification(cn);
// Set to default modem preset
loraConfig.use_preset = true;
loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
} else {
validConfig = true;
}
}
power = loraConfig.tx_power;
@@ -512,6 +543,7 @@ void RadioInterface::applyModemConfig()
saveChannelNum(channel_num);
saveFreq(freq + loraConfig.frequency_offset);
slotTimeMsec = computeSlotTimeMsec(bw, sf);
preambleTimeMsec = getPacketTime((uint32_t)0);
maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader));
@@ -585,4 +617,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
}
}

View File

@@ -5,6 +5,7 @@
#include "Observer.h"
#include "PointerQueue.h"
#include "airtime.h"
#include "error.h"
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
@@ -71,18 +72,20 @@ class RadioInterface
- roundtrip air propagation time (assuming max. 30km between nodes);
- Tx/Rx turnaround time (maximum of SX126x and SX127x);
- MAC processing time (measured on T-beam) */
uint32_t slotTimeMsec = 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7;
uint32_t slotTimeMsec = computeSlotTimeMsec(bw, sf);
uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving
uint32_t preambleTimeMsec = 165; // calculated on startup, this is the default for LongFast
uint32_t maxPacketTimeMsec = 3246; // calculated on startup, this is the default for LongFast
const uint32_t PROCESSING_TIME_MSEC =
4500; // time to construct, process and construct a packet again (empirically determined)
const uint8_t CWmin = 2; // minimum CWsize
const uint8_t CWmax = 8; // maximum CWsize
const uint8_t CWmax = 7; // maximum CWsize
meshtastic_MeshPacket *sendingPacket = NULL; // The packet we are currently sending
uint32_t lastTxStart = 0L;
uint32_t computeSlotTimeMsec(float bw, float sf) { return 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7; }
/**
* A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need
* */

View File

@@ -61,11 +61,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
static void isrTxLevel0(), isrLevel0Common(PendingISR code);
/**
* Debugging counts
*/
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
protected:
@@ -109,6 +104,11 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
virtual void enableInterrupt(void (*)()) = 0;
/**
* Debugging counts
*/
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
public:
RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy, PhysicalLayer *iface = NULL);

View File

@@ -4,6 +4,7 @@
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "modules/NodeInfoModule.h"
// ReliableRouter::ReliableRouter() {}
@@ -109,13 +110,24 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n");
} else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit);
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 &&
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
// This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
p->hop_start, p->hop_limit);
} else {
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start,
p->hop_limit);
}
}
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c &&
c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) {
if (owner.public_key.size == 32) {
LOG_INFO("This seems like a remote PKI decrypt failure, so send a NodeInfo");
nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true);
}
}
// 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 == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0;

View File

@@ -2,6 +2,7 @@
#include "Channels.h"
#include "CryptoEngine.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
@@ -36,6 +37,7 @@ static MemoryDynamic<meshtastic_MeshPacket> staticPool;
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
static uint8_t bytes[MAX_RHPACKETLEN];
static uint8_t ScratchEncrypted[MAX_RHPACKETLEN];
/**
* Constructor
@@ -209,6 +211,13 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
#ifdef DEBUG_PORT
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->has_reply_id = true;
cn->reply_id = p->id;
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d minutes.", silentMinutes);
service->sendClientNotification(cn);
#endif
meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT;
if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh
@@ -299,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
return true; // If packet was already decoded just return
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
size_t rawSize = p->encrypted.size;
if (rawSize > sizeof(bytes)) {
LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize);
return false;
}
bool decrypted = false;
ChannelIndex chIndex = 0;
memcpy(bytes, p->encrypted.bytes,
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
#if !(MESHTASTIC_EXCLUDE_PKI)
// Attempt PKI decryption first
if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && p->to != NODENUM_BROADCAST &&
nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 &&
nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 12) {
LOG_DEBUG("Attempting PKI decryption\n");
// Try to find a channel that works with this hash
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
// Try to use this hash/channel pair
if (channels.decryptForHash(chIndex, p->channel)) {
// Try to decrypt the packet if we can
size_t rawSize = p->encrypted.size;
if (rawSize > sizeof(bytes)) {
LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize);
return false;
}
memcpy(bytes, p->encrypted.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 (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) {
LOG_INFO("PKI Decryption worked!\n");
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n");
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
LOG_ERROR("Invalid portnum (bad psk?)!\n");
rawSize -= 12;
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) &&
p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) {
decrypted = true;
LOG_INFO("Packet decrypted using PKI!\n");
p->pki_encrypted = true;
memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32);
p->public_key.size = 32;
// memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers
// chIndex = 8;
} else {
// parsing was successful
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
/* Not actually ever used.
// Decompress if needed. jm
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
// Decompress the payload
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
int decompressed_len;
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
} */
printPacket("decoded message", p);
#if ENABLE_JSON_LOGGING
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
#elif ARCH_PORTDUINO
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
}
#endif
return true;
return false;
}
}
}
#endif
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
return false;
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
if (!decrypted) {
// Try to find a channel that works with this hash
for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
// Try to use this hash/channel pair
if (channels.decryptForHash(chIndex, p->channel)) {
// Try to decrypt the packet if we can
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
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id);
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
LOG_ERROR("Invalid portnum (bad psk?)!\n");
} else {
decrypted = true;
break;
}
}
}
}
if (decrypted) {
// parsing was successful
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
/* Not actually ever used.
// Decompress if needed. jm
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
// Decompress the payload
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
int decompressed_len;
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
} */
printPacket("decoded message", p);
#if ENABLE_JSON_LOGGING
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
#elif ARCH_PORTDUINO
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
}
#endif
return true;
} else {
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
return false;
}
}
/** Return 0 for success or a Routing_Errror code for failure
@@ -371,12 +415,13 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
{
concurrency::LockGuard g(cryptLock);
int16_t hash;
// If the packet is not yet encrypted, do so now
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
/* Not actually used, so save the cycles
// Only allow encryption on the text message app.
// TODO: Allow modules to opt into compression.
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) {
@@ -417,17 +462,61 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
// printBytes("plaintext", bytes, numbytes);
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
auto hash = channels.setActiveByIndex(chIndex);
if (hash < 0)
// No suitable channel could be found for sending
return meshtastic_Routing_Error_NO_CHANNEL;
#if !(MESHTASTIC_EXCLUDE_PKI)
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
if (!owner.is_licensed && config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr &&
node->user.public_key.size > 0 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP &&
p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP &&
p->decoded.portnum != meshtastic_PortNum_POSITION_APP) {
LOG_DEBUG("Using PKI!\n");
if (numbytes + 12 > MAX_RHPACKETLEN)
return meshtastic_Routing_Error_TOO_LARGE;
if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) &&
memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) {
LOG_WARN("Client public key for client differs from requested! Requested 0x%02x, but stored key begins 0x%02x\n",
*p->public_key.bytes, *node->user.public_key.bytes);
return meshtastic_Routing_Error_PKI_FAILED;
}
crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted);
numbytes += 12;
memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes);
p->channel = 0;
p->pki_encrypted = true;
} else {
if (p->pki_encrypted == true) {
// Client specifically requested PKI encryption
return meshtastic_Routing_Error_PKI_FAILED;
}
hash = channels.setActiveByIndex(chIndex);
// Now that we are encrypting the packet channel should be the hash (no longer the index)
p->channel = hash;
if (hash < 0) {
// No suitable channel could be found for sending
return meshtastic_Routing_Error_NO_CHANNEL;
}
crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes);
memcpy(p->encrypted.bytes, bytes, numbytes);
}
#else
if (p->pki_encrypted == true) {
// Client specifically requested PKI encryption
return meshtastic_Routing_Error_PKI_FAILED;
}
hash = channels.setActiveByIndex(chIndex);
// Now that we are encrypting the packet channel should be the hash (no longer the index)
p->channel = hash;
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
if (hash < 0) {
// No suitable channel could be found for sending
return meshtastic_Routing_Error_NO_CHANNEL;
}
crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes);
memcpy(p->encrypted.bytes, bytes, numbytes);
#endif
// Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes);
p->encrypted.size = numbytes;
p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
}
@@ -539,4 +628,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
// cache/learn of the existence of nodes (i.e. FloodRouter) that they should not
handleReceived(p);
packetPool.release(p);
}
}

View File

@@ -40,7 +40,7 @@ template <typename T> bool SX126xInterface<T>::init()
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.h#L471C26-L471C104
// (DIO3 is free to be used as an IRQ)
#else
#elif !defined(TCXO_OPTIONAL)
float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE;
// (DIO3 is not free to be used as an IRQ)
#endif
@@ -345,4 +345,4 @@ template <typename T> bool SX126xInterface<T>::sleep()
#endif
return true;
}
}

View File

@@ -16,15 +16,21 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
if (lite->has_position) {
info.has_position = true;
if (lite->position.latitude_i != 0)
info.position.has_latitude_i = true;
info.position.latitude_i = lite->position.latitude_i;
if (lite->position.longitude_i != 0)
info.position.has_longitude_i = true;
info.position.longitude_i = lite->position.longitude_i;
if (lite->position.altitude != 0)
info.position.has_altitude = true;
info.position.altitude = lite->position.altitude;
info.position.location_source = lite->position.location_source;
info.position.time = lite->position.time;
}
if (lite->has_user) {
info.has_user = true;
info.user = lite->user;
info.user = ConvertToUser(lite->num, lite->user);
}
if (lite->has_device_metrics) {
info.has_device_metrics = true;
@@ -48,11 +54,49 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi
meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite)
{
meshtastic_Position position = meshtastic_Position_init_default;
if (lite.latitude_i != 0)
position.has_latitude_i = true;
position.latitude_i = lite.latitude_i;
if (lite.longitude_i != 0)
position.has_longitude_i = true;
position.longitude_i = lite.longitude_i;
if (lite.altitude != 0)
position.has_altitude = true;
position.altitude = lite.altitude;
position.location_source = lite.location_source;
position.time = lite.time;
return position;
}
meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user)
{
meshtastic_UserLite lite = meshtastic_UserLite_init_default;
strncpy(lite.long_name, user.long_name, sizeof(lite.long_name));
strncpy(lite.short_name, user.short_name, sizeof(lite.short_name));
lite.hw_model = user.hw_model;
lite.role = user.role;
lite.is_licensed = user.is_licensed;
memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr));
memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes));
lite.public_key.size = user.public_key.size;
return lite;
}
meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite)
{
meshtastic_User user = meshtastic_User_init_default;
snprintf(user.id, sizeof(user.id), "!%08x", nodeNum);
strncpy(user.long_name, lite.long_name, sizeof(user.long_name));
strncpy(user.short_name, lite.short_name, sizeof(user.short_name));
user.hw_model = lite.hw_model;
user.role = lite.role;
user.is_licensed = lite.is_licensed;
memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr));
memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes));
user.public_key.size = lite.public_key.size;
return user;
}

View File

@@ -10,4 +10,6 @@ class TypeConversions
static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite);
static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position);
static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite);
static meshtastic_UserLite ConvertToUserLite(meshtastic_User user);
static meshtastic_User ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite);
};

157
src/mesh/aes-ccm.cpp Normal file
View File

@@ -0,0 +1,157 @@
/*
* Counter with CBC-MAC (CCM) with AES
*
* Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#define AES_BLOCK_SIZE 16
#include "aes-ccm.h"
#if !MESHTASTIC_EXCLUDE_PKI
static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val)
{
a[0] = val >> 8;
a[1] = val & 0xff;
}
static void xor_aes_block(uint8_t *dst, const uint8_t *src)
{
uint32_t *d = (uint32_t *)dst;
uint32_t *s = (uint32_t *)src;
*d++ ^= *s++;
*d++ ^= *s++;
*d++ ^= *s++;
*d++ ^= *s++;
}
static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len,
uint8_t *x)
{
uint8_t aad_buf[2 * AES_BLOCK_SIZE];
uint8_t b[AES_BLOCK_SIZE];
/* Authentication */
/* B_0: Flags | Nonce N | l(m) */
b[0] = aad_len ? 0x40 : 0 /* Adata */;
b[0] |= (((M - 2) / 2) /* M' */ << 3);
b[0] |= (L - 1) /* L' */;
memcpy(&b[1], nonce, 15 - L);
WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len);
crypto->aesEncrypt(b, x); /* X_1 = E(K, B_0) */
if (!aad_len)
return;
WPA_PUT_BE16(aad_buf, aad_len);
memcpy(aad_buf + 2, aad, aad_len);
memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len);
xor_aes_block(aad_buf, x);
crypto->aesEncrypt(aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */
if (aad_len > AES_BLOCK_SIZE - 2) {
xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x);
/* X_3 = E(K, X_2 XOR B_2) */
crypto->aesEncrypt(&aad_buf[AES_BLOCK_SIZE], x);
}
}
static void aes_ccm_auth(const uint8_t *data, size_t len, uint8_t *x)
{
size_t last = len % AES_BLOCK_SIZE;
size_t i;
for (i = 0; i < len / AES_BLOCK_SIZE; i++) {
/* X_i+1 = E(K, X_i XOR B_i) */
xor_aes_block(x, data);
data += AES_BLOCK_SIZE;
crypto->aesEncrypt(x, x);
}
if (last) {
/* XOR zero-padded last block */
for (i = 0; i < last; i++)
x[i] ^= *data++;
crypto->aesEncrypt(x, x);
}
}
static void aes_ccm_encr_start(size_t L, const uint8_t *nonce, uint8_t *a)
{
/* A_i = Flags | Nonce N | Counter i */
a[0] = L - 1; /* Flags = L' */
memcpy(&a[1], nonce, 15 - L);
}
static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out, uint8_t *a)
{
size_t last = len % AES_BLOCK_SIZE;
size_t i;
/* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
for (i = 1; i <= len / AES_BLOCK_SIZE; i++) {
WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
/* S_i = E(K, A_i) */
crypto->aesEncrypt(a, out);
xor_aes_block(out, in);
out += AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
}
if (last) {
WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
crypto->aesEncrypt(a, out);
/* XOR zero-padded last block */
for (i = 0; i < last; i++)
*out++ ^= *in++;
}
}
static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth)
{
size_t i;
uint8_t tmp[AES_BLOCK_SIZE];
/* U = T XOR S_0; S_0 = E(K, A_0) */
WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
crypto->aesEncrypt(a, tmp);
for (i = 0; i < M; i++)
auth[i] = x[i] ^ tmp[i];
}
static void aes_ccm_decr_auth(size_t M, uint8_t *a, const uint8_t *auth, uint8_t *t)
{
size_t i;
uint8_t tmp[AES_BLOCK_SIZE];
/* U = T XOR S_0; S_0 = E(K, A_0) */
WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
crypto->aesEncrypt(a, tmp);
for (i = 0; i < M; i++)
t[i] = auth[i] ^ tmp[i];
}
/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len,
const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth)
{
const size_t L = 2;
uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
if (aad_len > 30 || M > AES_BLOCK_SIZE)
return -1;
crypto->aesSetKey(key, key_len);
aes_ccm_auth_start(M, L, nonce, aad, aad_len, plain_len, x);
aes_ccm_auth(plain, plain_len, x);
/* Encryption */
aes_ccm_encr_start(L, nonce, a);
aes_ccm_encr(L, plain, plain_len, crypt, a);
aes_ccm_encr_auth(M, x, a, auth);
return 0;
}
/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len,
const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain)
{
const size_t L = 2;
uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
uint8_t t[AES_BLOCK_SIZE];
if (aad_len > 30 || M > AES_BLOCK_SIZE)
return false;
crypto->aesSetKey(key, key_len);
/* Decryption */
aes_ccm_encr_start(L, nonce, a);
aes_ccm_decr_auth(M, a, auth, t);
/* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
aes_ccm_encr(L, crypt, crypt_len, plain, a);
aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x);
aes_ccm_auth(plain, crypt_len, x);
if (memcmp(x, t, M) != 0) { // FIXME make const comp
return false;
}
return true;
}
#endif

10
src/mesh/aes-ccm.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include "CryptoEngine.h"
#if !MESHTASTIC_EXCLUDE_PKI
int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len,
const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth);
bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len,
const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain);
#endif

View File

@@ -30,7 +30,11 @@ typedef enum _meshtastic_AdminMessage_ConfigType {
/* TODO: REPLACE */
meshtastic_AdminMessage_ConfigType_LORA_CONFIG = 5,
/* TODO: REPLACE */
meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6
meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6,
/* TODO: REPLACE */
meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7,
/* */
meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG = 8
} meshtastic_AdminMessage_ConfigType;
/* TODO: REPLACE */
@@ -85,6 +89,7 @@ typedef struct _meshtastic_NodeRemoteHardwarePinsResponse {
meshtastic_NodeRemoteHardwarePin node_remote_hardware_pins[16];
} meshtastic_NodeRemoteHardwarePinsResponse;
typedef PB_BYTES_ARRAY_T(8) meshtastic_AdminMessage_session_passkey_t;
/* This message is handled by the Admin module and is responsible for all settings/channel read/write operations.
This message is used to do settings operations to both remote AND local nodes.
(Prior to 1.2 these operations were done via special ToRadio operations) */
@@ -163,6 +168,9 @@ typedef struct _meshtastic_AdminMessage {
meshtastic_Position set_fixed_position;
/* Clear fixed position coordinates and then set position.fixed_position = false */
bool remove_fixed_position;
/* Set time only on the node
Convenience method to set the time on the node (as Net quality) without any other position data */
uint32_t set_time_only;
/* Begins an edit transaction for config, module config, owner, and channel settings changes
This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) */
bool begin_edit_settings;
@@ -185,6 +193,10 @@ typedef struct _meshtastic_AdminMessage {
/* Tell the node to reset the nodedb. */
int32_t nodedb_reset;
};
/* The node generates this key and sends it with any get_x_response packets.
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
Prevents replay attacks for admin messages. */
meshtastic_AdminMessage_session_passkey_t session_passkey;
} meshtastic_AdminMessage;
@@ -194,8 +206,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1))
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG+1))
#define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG
@@ -208,10 +220,10 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_AdminMessage_init_default {0, {0}}
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
#define meshtastic_AdminMessage_init_zero {0, {0}}
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
@@ -254,6 +266,7 @@ extern "C" {
#define meshtastic_AdminMessage_remove_favorite_node_tag 40
#define meshtastic_AdminMessage_set_fixed_position_tag 41
#define meshtastic_AdminMessage_remove_fixed_position_tag 42
#define meshtastic_AdminMessage_set_time_only_tag 43
#define meshtastic_AdminMessage_begin_edit_settings_tag 64
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_factory_reset_device_tag 94
@@ -263,6 +276,7 @@ extern "C" {
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
#define meshtastic_AdminMessage_factory_reset_config_tag 99
#define meshtastic_AdminMessage_nodedb_reset_tag 100
#define meshtastic_AdminMessage_session_passkey_tag 101
/* Struct field encoding specification for nanopb */
#define meshtastic_AdminMessage_FIELDLIST(X, a) \
@@ -299,6 +313,7 @@ X(a, STATIC, ONEOF, UINT32, (payload_variant,set_favorite_node,set_favori
X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_favorite_node,remove_favorite_node), 40) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed_position), 41) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \
X(a, STATIC, ONEOF, FIXED32, (payload_variant,set_time_only,set_time_only), 43) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
@@ -307,7 +322,8 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulato
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \
X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \
X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100)
X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) \
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101)
#define meshtastic_AdminMessage_CALLBACK NULL
#define meshtastic_AdminMessage_DEFAULT NULL
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
@@ -349,7 +365,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
#define meshtastic_AdminMessage_size 500
#define meshtastic_AdminMessage_size 511
#define meshtastic_HamParameters_size 31
#define meshtastic_NodeRemoteHardwarePinsResponse_size 496

View File

@@ -33,6 +33,12 @@ PB_BIND(meshtastic_Config_LoRaConfig, meshtastic_Config_LoRaConfig, 2)
PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AUTO)
PB_BIND(meshtastic_Config_SecurityConfig, meshtastic_Config_SecurityConfig, AUTO)
PB_BIND(meshtastic_Config_SessionkeyConfig, meshtastic_Config_SessionkeyConfig, AUTO)

View File

@@ -248,7 +248,8 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0,
/* Long Range - Slow */
meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1,
/* Very Long Range - Slow */
/* Very Long Range - Slow
Deprecated in 2.5: Works only with txco and is unusably slow */
meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2,
/* Medium Range - Slow */
meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3,
@@ -259,7 +260,11 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
/* Short Range - Fast */
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST = 6,
/* Long Range - Moderately Fast */
meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7
meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7,
/* Short Range - Turbo
This is the fastest preset and the only one with 500kHz bandwidth.
It is not legal to use in all regions due to this wider bandwidth. */
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8
} meshtastic_Config_LoRaConfig_ModemPreset;
typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
@@ -276,11 +281,9 @@ typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
typedef struct _meshtastic_Config_DeviceConfig {
/* Sets the role of node */
meshtastic_Config_DeviceConfig_Role role;
/* Disabling this will disable the SerialConsole by not initilizing the StreamAPI */
/* Disabling this will disable the SerialConsole by not initilizing the StreamAPI
Moved to SecurityConfig */
bool serial_enabled;
/* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
Set this to true to leave the debug log outputting even when API is active. */
bool debug_log_enabled;
/* For boards without a hard wired button, this is the pin number that will be used
Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */
uint32_t button_gpio;
@@ -295,7 +298,8 @@ typedef struct _meshtastic_Config_DeviceConfig {
/* Treat double tap interrupt on supported accelerometers as a button press if set to true */
bool double_tap_as_button_press;
/* If true, device is considered to be "managed" by a mesh administrator
Clients should then limit available configuration and administrative options inside the user interface */
Clients should then limit available configuration and administrative options inside the user interface
Moved to SecurityConfig */
bool is_managed;
/* Disables the triple-press of user button to enable or disable GPS */
bool disable_triple_click;
@@ -515,10 +519,37 @@ typedef struct _meshtastic_Config_BluetoothConfig {
meshtastic_Config_BluetoothConfig_PairingMode mode;
/* Specified PIN for PairingMode.FixedPin */
uint32_t fixed_pin;
/* Enables device (serial style logs) over Bluetooth */
bool device_logging_enabled;
} meshtastic_Config_BluetoothConfig;
typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t;
typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_private_key_t;
typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_admin_key_t;
typedef struct _meshtastic_Config_SecurityConfig {
/* The public key of the user's device.
Sent out to other nodes on the mesh to allow them to compute a shared secret key. */
meshtastic_Config_SecurityConfig_public_key_t public_key;
/* The private key of the device.
Used to create a shared key with a remote device. */
meshtastic_Config_SecurityConfig_private_key_t private_key;
/* The public key authorized to send admin messages to this node. */
meshtastic_Config_SecurityConfig_admin_key_t admin_key;
/* If true, device is considered to be "managed" by a mesh administrator via admin messages
Device is managed by a mesh administrator. */
bool is_managed;
/* Serial Console over the Stream API." */
bool serial_enabled;
/* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
Output live debug logging over serial or bluetooth is set to true. */
bool debug_log_api_enabled;
/* Allow incoming device control over the insecure legacy admin channel. */
bool admin_channel_enabled;
} meshtastic_Config_SecurityConfig;
/* Blank config request, strictly for getting the session key */
typedef struct _meshtastic_Config_SessionkeyConfig {
char dummy_field;
} meshtastic_Config_SessionkeyConfig;
typedef struct _meshtastic_Config {
pb_size_t which_payload_variant;
union {
@@ -529,6 +560,8 @@ typedef struct _meshtastic_Config {
meshtastic_Config_DisplayConfig display;
meshtastic_Config_LoRaConfig lora;
meshtastic_Config_BluetoothConfig bluetooth;
meshtastic_Config_SecurityConfig security;
meshtastic_Config_SessionkeyConfig sessionkey;
} payload_variant;
} meshtastic_Config;
@@ -583,8 +616,8 @@ extern "C" {
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1))
#define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
#define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN
@@ -612,30 +645,35 @@ extern "C" {
#define meshtastic_Config_BluetoothConfig_mode_ENUMTYPE meshtastic_Config_BluetoothConfig_PairingMode
/* Initializer values for message structs */
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_Config_SessionkeyConfig_init_default {0}
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_Config_SessionkeyConfig_init_zero {0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_Config_DeviceConfig_role_tag 1
#define meshtastic_Config_DeviceConfig_serial_enabled_tag 2
#define meshtastic_Config_DeviceConfig_debug_log_enabled_tag 3
#define meshtastic_Config_DeviceConfig_button_gpio_tag 4
#define meshtastic_Config_DeviceConfig_buzzer_gpio_tag 5
#define meshtastic_Config_DeviceConfig_rebroadcast_mode_tag 6
@@ -710,7 +748,13 @@ extern "C" {
#define meshtastic_Config_BluetoothConfig_enabled_tag 1
#define meshtastic_Config_BluetoothConfig_mode_tag 2
#define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3
#define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4
#define meshtastic_Config_SecurityConfig_public_key_tag 1
#define meshtastic_Config_SecurityConfig_private_key_tag 2
#define meshtastic_Config_SecurityConfig_admin_key_tag 3
#define meshtastic_Config_SecurityConfig_is_managed_tag 4
#define meshtastic_Config_SecurityConfig_serial_enabled_tag 5
#define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6
#define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8
#define meshtastic_Config_device_tag 1
#define meshtastic_Config_position_tag 2
#define meshtastic_Config_power_tag 3
@@ -718,6 +762,8 @@ extern "C" {
#define meshtastic_Config_display_tag 5
#define meshtastic_Config_lora_tag 6
#define meshtastic_Config_bluetooth_tag 7
#define meshtastic_Config_security_tag 8
#define meshtastic_Config_sessionkey_tag 9
/* Struct field encoding specification for nanopb */
#define meshtastic_Config_FIELDLIST(X, a) \
@@ -727,7 +773,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,power,payload_variant.power)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,network,payload_variant.network), 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.display), 5) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9)
#define meshtastic_Config_CALLBACK NULL
#define meshtastic_Config_DEFAULT NULL
#define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig
@@ -737,11 +785,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bl
#define meshtastic_Config_payload_variant_display_MSGTYPE meshtastic_Config_DisplayConfig
#define meshtastic_Config_payload_variant_lora_MSGTYPE meshtastic_Config_LoRaConfig
#define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig
#define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig
#define meshtastic_Config_payload_variant_sessionkey_MSGTYPE meshtastic_Config_SessionkeyConfig
#define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, role, 1) \
X(a, STATIC, SINGULAR, BOOL, serial_enabled, 2) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 3) \
X(a, STATIC, SINGULAR, UINT32, button_gpio, 4) \
X(a, STATIC, SINGULAR, UINT32, buzzer_gpio, 5) \
X(a, STATIC, SINGULAR, UENUM, rebroadcast_mode, 6) \
@@ -844,11 +893,26 @@ X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
#define meshtastic_Config_BluetoothConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, UENUM, mode, 2) \
X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) \
X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4)
X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3)
#define meshtastic_Config_BluetoothConfig_CALLBACK NULL
#define meshtastic_Config_BluetoothConfig_DEFAULT NULL
#define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BYTES, public_key, 1) \
X(a, STATIC, SINGULAR, BYTES, private_key, 2) \
X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \
X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \
X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \
X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \
X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8)
#define meshtastic_Config_SecurityConfig_CALLBACK NULL
#define meshtastic_Config_SecurityConfig_DEFAULT NULL
#define meshtastic_Config_SessionkeyConfig_FIELDLIST(X, a) \
#define meshtastic_Config_SessionkeyConfig_CALLBACK NULL
#define meshtastic_Config_SessionkeyConfig_DEFAULT NULL
extern const pb_msgdesc_t meshtastic_Config_msg;
extern const pb_msgdesc_t meshtastic_Config_DeviceConfig_msg;
extern const pb_msgdesc_t meshtastic_Config_PositionConfig_msg;
@@ -858,6 +922,8 @@ extern const pb_msgdesc_t meshtastic_Config_NetworkConfig_IpV4Config_msg;
extern const pb_msgdesc_t meshtastic_Config_DisplayConfig_msg;
extern const pb_msgdesc_t meshtastic_Config_LoRaConfig_msg;
extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg;
extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_Config_fields &meshtastic_Config_msg
@@ -869,17 +935,21 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
#define meshtastic_Config_DisplayConfig_fields &meshtastic_Config_DisplayConfig_msg
#define meshtastic_Config_LoRaConfig_fields &meshtastic_Config_LoRaConfig_msg
#define meshtastic_Config_BluetoothConfig_fields &meshtastic_Config_BluetoothConfig_msg
#define meshtastic_Config_SecurityConfig_fields &meshtastic_Config_SecurityConfig_msg
#define meshtastic_Config_SessionkeyConfig_fields &meshtastic_Config_SessionkeyConfig_msg
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
#define meshtastic_Config_BluetoothConfig_size 12
#define meshtastic_Config_DeviceConfig_size 100
#define meshtastic_Config_BluetoothConfig_size 10
#define meshtastic_Config_DeviceConfig_size 98
#define meshtastic_Config_DisplayConfig_size 30
#define meshtastic_Config_LoRaConfig_size 82
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_PositionConfig_size 62
#define meshtastic_Config_PowerConfig_size 52
#define meshtastic_Config_SecurityConfig_size 110
#define meshtastic_Config_SessionkeyConfig_size 0
#define meshtastic_Config_size 199
#ifdef __cplusplus

View File

@@ -9,6 +9,9 @@
PB_BIND(meshtastic_PositionLite, meshtastic_PositionLite, AUTO)
PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO)
PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO)

View File

@@ -9,6 +9,7 @@
#include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/telemetry.pb.h"
#include "meshtastic/config.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -45,12 +46,37 @@ typedef struct _meshtastic_PositionLite {
meshtastic_Position_LocSource location_source;
} meshtastic_PositionLite;
typedef PB_BYTES_ARRAY_T(32) meshtastic_UserLite_public_key_t;
typedef struct _meshtastic_UserLite {
/* This is the addr of the radio. */
pb_byte_t macaddr[6];
/* A full name for this user, i.e. "Kevin Hester" */
char long_name[40];
/* A VERY short name, ideally two characters.
Suitable for a tiny OLED screen */
char short_name[5];
/* TBEAM, HELTEC, etc...
Starting in 1.2.11 moved to hw_model enum in the NodeInfo object.
Apps will still need the string here for older builds
(so OTA update can find the right image), but if the enum is available it will be used instead. */
meshtastic_HardwareModel hw_model;
/* In some regions Ham radio operators have different bandwidth limitations than others.
If this user is a licensed operator, set this flag.
Also, "long_name" should be their licence number. */
bool is_licensed;
/* Indicates that the user's role in the mesh */
meshtastic_Config_DeviceConfig_Role role;
/* The public key of the user's device.
This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */
meshtastic_UserLite_public_key_t public_key;
} meshtastic_UserLite;
typedef struct _meshtastic_NodeInfoLite {
/* The node number */
uint32_t num;
/* The user info for this node */
bool has_user;
meshtastic_User user;
meshtastic_UserLite user;
/* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
Position.time now indicates the last time we received a POSITION from that node. */
bool has_position;
@@ -164,6 +190,9 @@ extern "C" {
#define meshtastic_PositionLite_location_source_ENUMTYPE meshtastic_Position_LocSource
#define meshtastic_UserLite_hw_model_ENUMTYPE meshtastic_HardwareModel
#define meshtastic_UserLite_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role
@@ -172,12 +201,14 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}}
#define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0}
#define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default}
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}}
#define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0}
#define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero}
@@ -188,6 +219,13 @@ extern "C" {
#define meshtastic_PositionLite_altitude_tag 3
#define meshtastic_PositionLite_time_tag 4
#define meshtastic_PositionLite_location_source_tag 5
#define meshtastic_UserLite_macaddr_tag 1
#define meshtastic_UserLite_long_name_tag 2
#define meshtastic_UserLite_short_name_tag 3
#define meshtastic_UserLite_hw_model_tag 4
#define meshtastic_UserLite_is_licensed_tag 5
#define meshtastic_UserLite_role_tag 6
#define meshtastic_UserLite_public_key_tag 7
#define meshtastic_NodeInfoLite_num_tag 1
#define meshtastic_NodeInfoLite_user_tag 2
#define meshtastic_NodeInfoLite_position_tag 3
@@ -229,6 +267,17 @@ X(a, STATIC, SINGULAR, UENUM, location_source, 5)
#define meshtastic_PositionLite_CALLBACK NULL
#define meshtastic_PositionLite_DEFAULT NULL
#define meshtastic_UserLite_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 1) \
X(a, STATIC, SINGULAR, STRING, long_name, 2) \
X(a, STATIC, SINGULAR, STRING, short_name, 3) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \
X(a, STATIC, SINGULAR, BOOL, is_licensed, 5) \
X(a, STATIC, SINGULAR, UENUM, role, 6) \
X(a, STATIC, SINGULAR, BYTES, public_key, 7)
#define meshtastic_UserLite_CALLBACK NULL
#define meshtastic_UserLite_DEFAULT NULL
#define meshtastic_NodeInfoLite_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, num, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \
@@ -242,7 +291,7 @@ X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_NodeInfoLite_CALLBACK NULL
#define meshtastic_NodeInfoLite_DEFAULT NULL
#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_User
#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_UserLite
#define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite
#define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics
@@ -290,6 +339,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8)
#define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig
extern const pb_msgdesc_t meshtastic_PositionLite_msg;
extern const pb_msgdesc_t meshtastic_UserLite_msg;
extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg;
extern const pb_msgdesc_t meshtastic_DeviceState_msg;
extern const pb_msgdesc_t meshtastic_ChannelFile_msg;
@@ -297,6 +347,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg
#define meshtastic_UserLite_fields &meshtastic_UserLite_msg
#define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg
#define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg
#define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg
@@ -306,9 +357,10 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
/* meshtastic_DeviceState_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
#define meshtastic_ChannelFile_size 718
#define meshtastic_NodeInfoLite_size 166
#define meshtastic_OEMStore_size 3388
#define meshtastic_NodeInfoLite_size 183
#define meshtastic_OEMStore_size 3496
#define meshtastic_PositionLite_size 28
#define meshtastic_UserLite_size 96
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -38,6 +38,9 @@ typedef struct _meshtastic_LocalConfig {
incompatible changes This integer is set at build time and is private to
NodeDB.cpp in the device code. */
uint32_t version;
/* The part of the config that is specific to Security settings */
bool has_security;
meshtastic_Config_SecurityConfig security;
} meshtastic_LocalConfig;
typedef struct _meshtastic_LocalModuleConfig {
@@ -92,9 +95,9 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0}
#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default}
#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default}
#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0}
#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero}
#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero}
/* Field tags (for use in manual encoding/decoding) */
@@ -106,6 +109,7 @@ extern "C" {
#define meshtastic_LocalConfig_lora_tag 6
#define meshtastic_LocalConfig_bluetooth_tag 7
#define meshtastic_LocalConfig_version_tag 8
#define meshtastic_LocalConfig_security_tag 9
#define meshtastic_LocalModuleConfig_mqtt_tag 1
#define meshtastic_LocalModuleConfig_serial_tag 2
#define meshtastic_LocalModuleConfig_external_notification_tag 3
@@ -130,7 +134,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, network, 4) \
X(a, STATIC, OPTIONAL, MESSAGE, display, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, lora, 6) \
X(a, STATIC, OPTIONAL, MESSAGE, bluetooth, 7) \
X(a, STATIC, SINGULAR, UINT32, version, 8)
X(a, STATIC, SINGULAR, UINT32, version, 8) \
X(a, STATIC, OPTIONAL, MESSAGE, security, 9)
#define meshtastic_LocalConfig_CALLBACK NULL
#define meshtastic_LocalConfig_DEFAULT NULL
#define meshtastic_LocalConfig_device_MSGTYPE meshtastic_Config_DeviceConfig
@@ -140,6 +145,7 @@ X(a, STATIC, SINGULAR, UINT32, version, 8)
#define meshtastic_LocalConfig_display_MSGTYPE meshtastic_Config_DisplayConfig
#define meshtastic_LocalConfig_lora_MSGTYPE meshtastic_Config_LoRaConfig
#define meshtastic_LocalConfig_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig
#define meshtastic_LocalConfig_security_MSGTYPE meshtastic_Config_SecurityConfig
#define meshtastic_LocalModuleConfig_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, mqtt, 1) \
@@ -181,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 555
#define meshtastic_LocalConfig_size 663
#define meshtastic_LocalModuleConfig_size 687
#ifdef __cplusplus

View File

@@ -30,7 +30,7 @@ PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2)
PB_BIND(meshtastic_MeshPacket, meshtastic_MeshPacket, 2)
PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO)
PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, 2)
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
@@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2)
PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO)

View File

@@ -187,6 +187,15 @@ typedef enum _meshtastic_HardwareModel {
/* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module
SSD1306 OLED and No GPS */
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74,
/* Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. */
meshtastic_HardwareModel_ME25LS01_4Y10TD = 75,
/* RP2040_FEATHER_RFM95
Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED
https://www.adafruit.com/product/5714
https://www.adafruit.com/product/326
https://www.adafruit.com/product/938
^^^ short A0 to switch to I2C address 0x3C */
meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -300,7 +309,11 @@ typedef enum _meshtastic_Routing_Error {
meshtastic_Routing_Error_BAD_REQUEST = 32,
/* The application layer service on the remote node received your request, but considered your request not authorized
(i.e you did not send the request on the required bound channel) */
meshtastic_Routing_Error_NOT_AUTHORIZED = 33
meshtastic_Routing_Error_NOT_AUTHORIZED = 33,
/* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */
meshtastic_Routing_Error_PKI_FAILED = 34,
/* The receiving node does not have a Public Key to decode with */
meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35
} meshtastic_Routing_Error;
/* The priority of this message for sending.
@@ -374,10 +387,13 @@ typedef enum _meshtastic_LogRecord_Level {
typedef struct _meshtastic_Position {
/* The new preferred location encoding, multiply by 1e-7 to get degrees
in floating point */
bool has_latitude_i;
int32_t latitude_i;
/* TODO: REPLACE */
bool has_longitude_i;
int32_t longitude_i;
/* In meters above MSL (but see issue #359) */
bool has_altitude;
int32_t altitude;
/* This is usually not sent over the mesh (to save space), but it is sent
from the phone so that the local device can set its time if it is sent over
@@ -393,8 +409,10 @@ typedef struct _meshtastic_Position {
/* Pos. timestamp milliseconds adjustment (rarely available or required) */
int32_t timestamp_millis_adjust;
/* HAE altitude in meters - can be used instead of MSL altitude */
bool has_altitude_hae;
int32_t altitude_hae;
/* Geoidal separation in meters */
bool has_altitude_geoidal_separation;
int32_t altitude_geoidal_separation;
/* Horizontal, Vertical and Position Dilution of Precision, in 1/100 units
- PDOP is sufficient for most cases
@@ -416,8 +434,10 @@ typedef struct _meshtastic_Position {
- "heading" is where the fuselage points (measured in horizontal plane)
- "yaw" indicates a relative rotation about the vertical axis
TODO: REMOVE/INTEGRATE */
bool has_ground_speed;
uint32_t ground_speed;
/* TODO: REPLACE */
bool has_ground_track;
uint32_t ground_track;
/* GPS fix quality (from NMEA GxGGA statement or similar) */
uint32_t fix_quality;
@@ -439,6 +459,7 @@ typedef struct _meshtastic_Position {
uint32_t precision_bits;
} meshtastic_Position;
typedef PB_BYTES_ARRAY_T(32) meshtastic_User_public_key_t;
/* Broadcast when a newly powered mesh node wants to find a node num it can use
Sent from the phone over bluetooth to set the user id for the owner of this node.
Also sent from nodes to each other when a new node signs on (so all clients can have this info)
@@ -485,13 +506,25 @@ typedef struct _meshtastic_User {
bool is_licensed;
/* Indicates that the user's role in the mesh */
meshtastic_Config_DeviceConfig_Role role;
/* The public key of the user's device.
This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */
meshtastic_User_public_key_t public_key;
} meshtastic_User;
/* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */
/* A message used in a traceroute */
typedef struct _meshtastic_RouteDiscovery {
/* The list of nodenums this packet has visited so far */
/* The list of nodenums this packet has visited so far to the destination. */
pb_size_t route_count;
uint32_t route[8];
/* The list of SNRs (in dB, scaled by 4) in the route towards the destination. */
pb_size_t snr_towards_count;
int8_t snr_towards[8];
/* The list of nodenums the packet has visited on the way back from the destination. */
pb_size_t route_back_count;
uint32_t route_back[8];
/* The list of SNRs (in dB, scaled by 4) in the route back from the destination. */
pb_size_t snr_back_count;
int8_t snr_back[8];
} meshtastic_RouteDiscovery;
/* A Routing control Data packet handled by the routing module */
@@ -546,8 +579,10 @@ typedef struct _meshtastic_Waypoint {
/* Id of the waypoint */
uint32_t id;
/* latitude_i */
bool has_latitude_i;
int32_t latitude_i;
/* longitude_i */
bool has_longitude_i;
int32_t longitude_i;
/* Time the waypoint is to expire (epoch) */
uint32_t expire;
@@ -579,6 +614,7 @@ typedef struct _meshtastic_MqttClientProxyMessage {
} meshtastic_MqttClientProxyMessage;
typedef PB_BYTES_ARRAY_T(256) meshtastic_MeshPacket_encrypted_t;
typedef PB_BYTES_ARRAY_T(32) meshtastic_MeshPacket_public_key_t;
/* A packet envelope sent/received over the mesh
only payload_variant is sent in the payload portion of the LORA packet.
The other fields are either not sent at all, or sent in the special 16 byte LORA header. */
@@ -649,6 +685,10 @@ typedef struct _meshtastic_MeshPacket {
/* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header.
When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */
uint8_t hop_start;
/* Records the public key the packet was encrypted with, if applicable. */
meshtastic_MeshPacket_public_key_t public_key;
/* Indicates whether the packet was en/decrypted using PKI */
bool pki_encrypted;
} meshtastic_MeshPacket;
/* The bluetooth to device link:
@@ -738,6 +778,22 @@ typedef struct _meshtastic_QueueStatus {
uint32_t mesh_packet_id;
} meshtastic_QueueStatus;
/* A notification message from the device to the client
To be used for important messages that should to be displayed to the user
in the form of push notifications or validation messages when saving
invalid configuration. */
typedef struct _meshtastic_ClientNotification {
/* The id of the packet we're notifying in response to */
bool has_reply_id;
uint32_t reply_id;
/* Seconds since 1970 - or 0 for unknown/unset */
uint32_t time;
/* The level type of notification */
meshtastic_LogRecord_Level level;
/* The message body of the notification */
char message[400];
} meshtastic_ClientNotification;
/* Individual File info for the device */
typedef struct _meshtastic_FileInfo {
/* The fully qualified path of the file */
@@ -852,6 +908,8 @@ typedef struct _meshtastic_FromRadio {
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
/* File system manifest messages */
meshtastic_FileInfo fileInfo;
/* Notification message to the client */
meshtastic_ClientNotification clientNotification;
};
} meshtastic_FromRadio;
@@ -957,8 +1015,8 @@ extern "C" {
#define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1))
#define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NOT_AUTHORIZED
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NOT_AUTHORIZED+1))
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY+1))
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
@@ -994,6 +1052,8 @@ extern "C" {
#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level
#define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum
@@ -1010,19 +1070,20 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Position_init_default {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0}
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
#define meshtastic_MyNodeInfo_init_default {0, 0, 0}
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""}
#define meshtastic_FileInfo_init_default {"", 0}
#define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
@@ -1034,19 +1095,20 @@ extern "C" {
#define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}}
#define meshtastic_resend_chunks_init_default {{{NULL}, NULL}}
#define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}}
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Position_init_zero {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0}
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0}
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""}
#define meshtastic_FileInfo_init_zero {"", 0}
#define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
@@ -1090,7 +1152,11 @@ extern "C" {
#define meshtastic_User_hw_model_tag 5
#define meshtastic_User_is_licensed_tag 6
#define meshtastic_User_role_tag 7
#define meshtastic_User_public_key_tag 8
#define meshtastic_RouteDiscovery_route_tag 1
#define meshtastic_RouteDiscovery_snr_towards_tag 2
#define meshtastic_RouteDiscovery_route_back_tag 3
#define meshtastic_RouteDiscovery_snr_back_tag 4
#define meshtastic_Routing_route_request_tag 1
#define meshtastic_Routing_route_reply_tag 2
#define meshtastic_Routing_error_reason_tag 3
@@ -1129,6 +1195,8 @@ extern "C" {
#define meshtastic_MeshPacket_delayed_tag 13
#define meshtastic_MeshPacket_via_mqtt_tag 14
#define meshtastic_MeshPacket_hop_start_tag 15
#define meshtastic_MeshPacket_public_key_tag 16
#define meshtastic_MeshPacket_pki_encrypted_tag 17
#define meshtastic_NodeInfo_num_tag 1
#define meshtastic_NodeInfo_user_tag 2
#define meshtastic_NodeInfo_position_tag 3
@@ -1150,6 +1218,10 @@ extern "C" {
#define meshtastic_QueueStatus_free_tag 2
#define meshtastic_QueueStatus_maxlen_tag 3
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
#define meshtastic_ClientNotification_reply_id_tag 1
#define meshtastic_ClientNotification_time_tag 2
#define meshtastic_ClientNotification_level_tag 3
#define meshtastic_ClientNotification_message_tag 4
#define meshtastic_FileInfo_file_name_tag 1
#define meshtastic_FileInfo_size_bytes_tag 2
#define meshtastic_Compressed_portnum_tag 1
@@ -1187,6 +1259,7 @@ extern "C" {
#define meshtastic_FromRadio_metadata_tag 13
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
#define meshtastic_FromRadio_fileInfo_tag 15
#define meshtastic_FromRadio_clientNotification_tag 16
#define meshtastic_ToRadio_packet_tag 1
#define meshtastic_ToRadio_want_config_id_tag 3
#define meshtastic_ToRadio_disconnect_tag 4
@@ -1207,22 +1280,22 @@ extern "C" {
/* Struct field encoding specification for nanopb */
#define meshtastic_Position_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \
X(a, STATIC, SINGULAR, INT32, altitude, 3) \
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 1) \
X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 2) \
X(a, STATIC, OPTIONAL, INT32, altitude, 3) \
X(a, STATIC, SINGULAR, FIXED32, time, 4) \
X(a, STATIC, SINGULAR, UENUM, location_source, 5) \
X(a, STATIC, SINGULAR, UENUM, altitude_source, 6) \
X(a, STATIC, SINGULAR, FIXED32, timestamp, 7) \
X(a, STATIC, SINGULAR, INT32, timestamp_millis_adjust, 8) \
X(a, STATIC, SINGULAR, SINT32, altitude_hae, 9) \
X(a, STATIC, SINGULAR, SINT32, altitude_geoidal_separation, 10) \
X(a, STATIC, OPTIONAL, SINT32, altitude_hae, 9) \
X(a, STATIC, OPTIONAL, SINT32, altitude_geoidal_separation, 10) \
X(a, STATIC, SINGULAR, UINT32, PDOP, 11) \
X(a, STATIC, SINGULAR, UINT32, HDOP, 12) \
X(a, STATIC, SINGULAR, UINT32, VDOP, 13) \
X(a, STATIC, SINGULAR, UINT32, gps_accuracy, 14) \
X(a, STATIC, SINGULAR, UINT32, ground_speed, 15) \
X(a, STATIC, SINGULAR, UINT32, ground_track, 16) \
X(a, STATIC, OPTIONAL, UINT32, ground_speed, 15) \
X(a, STATIC, OPTIONAL, UINT32, ground_track, 16) \
X(a, STATIC, SINGULAR, UINT32, fix_quality, 17) \
X(a, STATIC, SINGULAR, UINT32, fix_type, 18) \
X(a, STATIC, SINGULAR, UINT32, sats_in_view, 19) \
@@ -1240,12 +1313,16 @@ X(a, STATIC, SINGULAR, STRING, short_name, 3) \
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \
X(a, STATIC, SINGULAR, BOOL, is_licensed, 6) \
X(a, STATIC, SINGULAR, UENUM, role, 7)
X(a, STATIC, SINGULAR, UENUM, role, 7) \
X(a, STATIC, SINGULAR, BYTES, public_key, 8)
#define meshtastic_User_CALLBACK NULL
#define meshtastic_User_DEFAULT NULL
#define meshtastic_RouteDiscovery_FIELDLIST(X, a) \
X(a, STATIC, REPEATED, FIXED32, route, 1)
X(a, STATIC, REPEATED, FIXED32, route, 1) \
X(a, STATIC, REPEATED, INT32, snr_towards, 2) \
X(a, STATIC, REPEATED, FIXED32, route_back, 3) \
X(a, STATIC, REPEATED, INT32, snr_back, 4)
#define meshtastic_RouteDiscovery_CALLBACK NULL
#define meshtastic_RouteDiscovery_DEFAULT NULL
@@ -1272,8 +1349,8 @@ X(a, STATIC, SINGULAR, FIXED32, emoji, 8)
#define meshtastic_Waypoint_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, id, 1) \
X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 2) \
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \
X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 3) \
X(a, STATIC, SINGULAR, UINT32, expire, 4) \
X(a, STATIC, SINGULAR, UINT32, locked_to, 5) \
X(a, STATIC, SINGULAR, STRING, name, 6) \
@@ -1305,7 +1382,9 @@ X(a, STATIC, SINGULAR, UENUM, priority, 11) \
X(a, STATIC, SINGULAR, INT32, rx_rssi, 12) \
X(a, STATIC, SINGULAR, UENUM, delayed, 13) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \
X(a, STATIC, SINGULAR, UINT32, hop_start, 15)
X(a, STATIC, SINGULAR, UINT32, hop_start, 15) \
X(a, STATIC, SINGULAR, BYTES, public_key, 16) \
X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17)
#define meshtastic_MeshPacket_CALLBACK NULL
#define meshtastic_MeshPacket_DEFAULT NULL
#define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data
@@ -1365,7 +1444,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 1
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16)
#define meshtastic_FromRadio_CALLBACK NULL
#define meshtastic_FromRadio_DEFAULT NULL
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
@@ -1380,6 +1460,15 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata
#define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification
#define meshtastic_ClientNotification_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \
X(a, STATIC, SINGULAR, FIXED32, time, 2) \
X(a, STATIC, SINGULAR, UENUM, level, 3) \
X(a, STATIC, SINGULAR, STRING, message, 4)
#define meshtastic_ClientNotification_CALLBACK NULL
#define meshtastic_ClientNotification_DEFAULT NULL
#define meshtastic_FileInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, file_name, 1) \
@@ -1485,6 +1574,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
extern const pb_msgdesc_t meshtastic_LogRecord_msg;
extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
extern const pb_msgdesc_t meshtastic_FromRadio_msg;
extern const pb_msgdesc_t meshtastic_ClientNotification_msg;
extern const pb_msgdesc_t meshtastic_FileInfo_msg;
extern const pb_msgdesc_t meshtastic_ToRadio_msg;
extern const pb_msgdesc_t meshtastic_Compressed_msg;
@@ -1511,6 +1601,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg
#define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg
#define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg
#define meshtastic_Compressed_fields &meshtastic_Compressed_msg
@@ -1528,6 +1619,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
/* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size
#define meshtastic_ChunkedPayload_size 245
#define meshtastic_ClientNotification_size 415
#define meshtastic_Compressed_size 243
#define meshtastic_Data_size 270
#define meshtastic_DeviceMetadata_size 46
@@ -1535,19 +1627,19 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_FromRadio_size 510
#define meshtastic_Heartbeat_size 0
#define meshtastic_LogRecord_size 426
#define meshtastic_MeshPacket_size 326
#define meshtastic_MeshPacket_size 364
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_NeighborInfo_size 258
#define meshtastic_Neighbor_size 22
#define meshtastic_NodeInfo_size 283
#define meshtastic_NodeInfo_size 317
#define meshtastic_NodeRemoteHardwarePin_size 29
#define meshtastic_Position_size 144
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 40
#define meshtastic_Routing_size 42
#define meshtastic_RouteDiscovery_size 256
#define meshtastic_Routing_size 259
#define meshtastic_ToRadio_size 504
#define meshtastic_User_size 79
#define meshtastic_User_size 113
#define meshtastic_Waypoint_size 165
#ifdef __cplusplus

View File

@@ -341,7 +341,7 @@ typedef struct _meshtastic_ModuleConfig_CannedMessageConfig {
/* Enable/disable CannedMessageModule. */
bool enabled;
/* Input event origin accepted by the canned message module.
Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" */
Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" */
char allow_input_source[16];
/* CannedMessageModule also sends a bell character with the messages.
ExternalNotificationModule can benefit from this feature. */

View File

@@ -113,7 +113,7 @@ typedef enum _meshtastic_PortNum {
ENCODING: Protobuf (?) */
meshtastic_PortNum_SIMULATOR_APP = 69,
/* Provides a traceroute functionality to show the route a packet towards
a certain destination would take on the mesh.
a certain destination would take on the mesh. Contains a RouteDiscovery message as payload.
ENCODING: Protobuf */
meshtastic_PortNum_TRACEROUTE_APP = 70,
/* Aggregates edge info for the network by sending out a list of each node's neighbors

View File

@@ -18,6 +18,9 @@ PB_BIND(meshtastic_PowerMetrics, meshtastic_PowerMetrics, AUTO)
PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO)
PB_BIND(meshtastic_LocalStats, meshtastic_LocalStats, AUTO)
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)

View File

@@ -76,101 +76,161 @@ typedef enum _meshtastic_TelemetrySensorType {
/* Key native device metrics such as battery level */
typedef struct _meshtastic_DeviceMetrics {
/* 0-100 (>100 means powered) */
bool has_battery_level;
uint32_t battery_level;
/* Voltage measured */
bool has_voltage;
float voltage;
/* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
bool has_channel_utilization;
float channel_utilization;
/* Percent of airtime for transmission used within the last hour. */
bool has_air_util_tx;
float air_util_tx;
/* How long the device has been running since the last reboot (in seconds) */
bool has_uptime_seconds;
uint32_t uptime_seconds;
} meshtastic_DeviceMetrics;
/* Weather station or other environmental metrics */
typedef struct _meshtastic_EnvironmentMetrics {
/* Temperature measured */
bool has_temperature;
float temperature;
/* Relative humidity percent measured */
bool has_relative_humidity;
float relative_humidity;
/* Barometric pressure in hPA measured */
bool has_barometric_pressure;
float barometric_pressure;
/* Gas resistance in MOhm measured */
bool has_gas_resistance;
float gas_resistance;
/* Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */
bool has_voltage;
float voltage;
/* Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */
bool has_current;
float current;
/* relative scale IAQ value as measured by Bosch BME680 . value 0-500.
Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */
bool has_iaq;
uint16_t iaq;
/* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */
bool has_distance;
float distance;
/* VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. */
bool has_lux;
float lux;
/* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */
bool has_white_lux;
float white_lux;
/* Infrared lux */
bool has_ir_lux;
float ir_lux;
/* Ultraviolet lux */
bool has_uv_lux;
float uv_lux;
/* Wind direction in degrees
0 degrees = North, 90 = East, etc... */
bool has_wind_direction;
uint16_t wind_direction;
/* Wind speed in m/s */
bool has_wind_speed;
float wind_speed;
/* Weight in KG */
bool has_weight;
float weight;
/* Wind gust in m/s */
bool has_wind_gust;
float wind_gust;
/* Wind lull in m/s */
bool has_wind_lull;
float wind_lull;
} meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */
typedef struct _meshtastic_PowerMetrics {
/* Voltage (Ch1) */
bool has_ch1_voltage;
float ch1_voltage;
/* Current (Ch1) */
bool has_ch1_current;
float ch1_current;
/* Voltage (Ch2) */
bool has_ch2_voltage;
float ch2_voltage;
/* Current (Ch2) */
bool has_ch2_current;
float ch2_current;
/* Voltage (Ch3) */
bool has_ch3_voltage;
float ch3_voltage;
/* Current (Ch3) */
bool has_ch3_current;
float ch3_current;
} meshtastic_PowerMetrics;
/* Air quality metrics */
typedef struct _meshtastic_AirQualityMetrics {
/* Concentration Units Standard PM1.0 */
bool has_pm10_standard;
uint32_t pm10_standard;
/* Concentration Units Standard PM2.5 */
bool has_pm25_standard;
uint32_t pm25_standard;
/* Concentration Units Standard PM10.0 */
bool has_pm100_standard;
uint32_t pm100_standard;
/* Concentration Units Environmental PM1.0 */
bool has_pm10_environmental;
uint32_t pm10_environmental;
/* Concentration Units Environmental PM2.5 */
bool has_pm25_environmental;
uint32_t pm25_environmental;
/* Concentration Units Environmental PM10.0 */
bool has_pm100_environmental;
uint32_t pm100_environmental;
/* 0.3um Particle Count */
bool has_particles_03um;
uint32_t particles_03um;
/* 0.5um Particle Count */
bool has_particles_05um;
uint32_t particles_05um;
/* 1.0um Particle Count */
bool has_particles_10um;
uint32_t particles_10um;
/* 2.5um Particle Count */
bool has_particles_25um;
uint32_t particles_25um;
/* 5.0um Particle Count */
bool has_particles_50um;
uint32_t particles_50um;
/* 10.0um Particle Count */
bool has_particles_100um;
uint32_t particles_100um;
} meshtastic_AirQualityMetrics;
/* Local device mesh statistics */
typedef struct _meshtastic_LocalStats {
/* How long the device has been running since the last reboot (in seconds) */
uint32_t uptime_seconds;
/* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
float channel_utilization;
/* Percent of airtime for transmission used within the last hour. */
float air_util_tx;
/* Number of packets sent */
uint32_t num_packets_tx;
/* Number of packets received good */
uint32_t num_packets_rx;
/* Number of packets received that are malformed or violate the protocol */
uint32_t num_packets_rx_bad;
/* Number of nodes online (in the past 2 hours) */
uint16_t num_online_nodes;
/* Number of nodes total */
uint16_t num_total_nodes;
} meshtastic_LocalStats;
/* Types of Measurements the telemetry module is equipped to handle */
typedef struct _meshtastic_Telemetry {
/* Seconds since 1970 - or 0 for unknown/unset */
@@ -185,6 +245,8 @@ typedef struct _meshtastic_Telemetry {
meshtastic_AirQualityMetrics air_quality_metrics;
/* Power Metrics */
meshtastic_PowerMetrics power_metrics;
/* Local device mesh statistics */
meshtastic_LocalStats local_stats;
} variant;
} meshtastic_Telemetry;
@@ -213,17 +275,20 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
#define meshtastic_Nau7802Config_init_zero {0, 0}
@@ -268,83 +333,106 @@ extern "C" {
#define meshtastic_AirQualityMetrics_particles_25um_tag 10
#define meshtastic_AirQualityMetrics_particles_50um_tag 11
#define meshtastic_AirQualityMetrics_particles_100um_tag 12
#define meshtastic_LocalStats_uptime_seconds_tag 1
#define meshtastic_LocalStats_channel_utilization_tag 2
#define meshtastic_LocalStats_air_util_tx_tag 3
#define meshtastic_LocalStats_num_packets_tx_tag 4
#define meshtastic_LocalStats_num_packets_rx_tag 5
#define meshtastic_LocalStats_num_packets_rx_bad_tag 6
#define meshtastic_LocalStats_num_online_nodes_tag 7
#define meshtastic_LocalStats_num_total_nodes_tag 8
#define meshtastic_Telemetry_time_tag 1
#define meshtastic_Telemetry_device_metrics_tag 2
#define meshtastic_Telemetry_environment_metrics_tag 3
#define meshtastic_Telemetry_air_quality_metrics_tag 4
#define meshtastic_Telemetry_power_metrics_tag 5
#define meshtastic_Telemetry_local_stats_tag 6
#define meshtastic_Nau7802Config_zeroOffset_tag 1
#define meshtastic_Nau7802Config_calibrationFactor_tag 2
/* Struct field encoding specification for nanopb */
#define meshtastic_DeviceMetrics_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, battery_level, 1) \
X(a, STATIC, SINGULAR, FLOAT, voltage, 2) \
X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \
X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) \
X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 5)
X(a, STATIC, OPTIONAL, UINT32, battery_level, 1) \
X(a, STATIC, OPTIONAL, FLOAT, voltage, 2) \
X(a, STATIC, OPTIONAL, FLOAT, channel_utilization, 3) \
X(a, STATIC, OPTIONAL, FLOAT, air_util_tx, 4) \
X(a, STATIC, OPTIONAL, UINT32, uptime_seconds, 5)
#define meshtastic_DeviceMetrics_CALLBACK NULL
#define meshtastic_DeviceMetrics_DEFAULT NULL
#define meshtastic_EnvironmentMetrics_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \
X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \
X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \
X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \
X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \
X(a, STATIC, SINGULAR, FLOAT, current, 6) \
X(a, STATIC, SINGULAR, UINT32, iaq, 7) \
X(a, STATIC, SINGULAR, FLOAT, distance, 8) \
X(a, STATIC, SINGULAR, FLOAT, lux, 9) \
X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \
X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \
X(a, STATIC, SINGULAR, FLOAT, weight, 15) \
X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \
X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17)
X(a, STATIC, OPTIONAL, FLOAT, temperature, 1) \
X(a, STATIC, OPTIONAL, FLOAT, relative_humidity, 2) \
X(a, STATIC, OPTIONAL, FLOAT, barometric_pressure, 3) \
X(a, STATIC, OPTIONAL, FLOAT, gas_resistance, 4) \
X(a, STATIC, OPTIONAL, FLOAT, voltage, 5) \
X(a, STATIC, OPTIONAL, FLOAT, current, 6) \
X(a, STATIC, OPTIONAL, UINT32, iaq, 7) \
X(a, STATIC, OPTIONAL, FLOAT, distance, 8) \
X(a, STATIC, OPTIONAL, FLOAT, lux, 9) \
X(a, STATIC, OPTIONAL, FLOAT, white_lux, 10) \
X(a, STATIC, OPTIONAL, FLOAT, ir_lux, 11) \
X(a, STATIC, OPTIONAL, FLOAT, uv_lux, 12) \
X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \
X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \
X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \
X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \
X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
#define meshtastic_PowerMetrics_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, ch1_voltage, 1) \
X(a, STATIC, SINGULAR, FLOAT, ch1_current, 2) \
X(a, STATIC, SINGULAR, FLOAT, ch2_voltage, 3) \
X(a, STATIC, SINGULAR, FLOAT, ch2_current, 4) \
X(a, STATIC, SINGULAR, FLOAT, ch3_voltage, 5) \
X(a, STATIC, SINGULAR, FLOAT, ch3_current, 6)
X(a, STATIC, OPTIONAL, FLOAT, ch1_voltage, 1) \
X(a, STATIC, OPTIONAL, FLOAT, ch1_current, 2) \
X(a, STATIC, OPTIONAL, FLOAT, ch2_voltage, 3) \
X(a, STATIC, OPTIONAL, FLOAT, ch2_current, 4) \
X(a, STATIC, OPTIONAL, FLOAT, ch3_voltage, 5) \
X(a, STATIC, OPTIONAL, FLOAT, ch3_current, 6)
#define meshtastic_PowerMetrics_CALLBACK NULL
#define meshtastic_PowerMetrics_DEFAULT NULL
#define meshtastic_AirQualityMetrics_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, pm10_standard, 1) \
X(a, STATIC, SINGULAR, UINT32, pm25_standard, 2) \
X(a, STATIC, SINGULAR, UINT32, pm100_standard, 3) \
X(a, STATIC, SINGULAR, UINT32, pm10_environmental, 4) \
X(a, STATIC, SINGULAR, UINT32, pm25_environmental, 5) \
X(a, STATIC, SINGULAR, UINT32, pm100_environmental, 6) \
X(a, STATIC, SINGULAR, UINT32, particles_03um, 7) \
X(a, STATIC, SINGULAR, UINT32, particles_05um, 8) \
X(a, STATIC, SINGULAR, UINT32, particles_10um, 9) \
X(a, STATIC, SINGULAR, UINT32, particles_25um, 10) \
X(a, STATIC, SINGULAR, UINT32, particles_50um, 11) \
X(a, STATIC, SINGULAR, UINT32, particles_100um, 12)
X(a, STATIC, OPTIONAL, UINT32, pm10_standard, 1) \
X(a, STATIC, OPTIONAL, UINT32, pm25_standard, 2) \
X(a, STATIC, OPTIONAL, UINT32, pm100_standard, 3) \
X(a, STATIC, OPTIONAL, UINT32, pm10_environmental, 4) \
X(a, STATIC, OPTIONAL, UINT32, pm25_environmental, 5) \
X(a, STATIC, OPTIONAL, UINT32, pm100_environmental, 6) \
X(a, STATIC, OPTIONAL, UINT32, particles_03um, 7) \
X(a, STATIC, OPTIONAL, UINT32, particles_05um, 8) \
X(a, STATIC, OPTIONAL, UINT32, particles_10um, 9) \
X(a, STATIC, OPTIONAL, UINT32, particles_25um, 10) \
X(a, STATIC, OPTIONAL, UINT32, particles_50um, 11) \
X(a, STATIC, OPTIONAL, UINT32, particles_100um, 12)
#define meshtastic_AirQualityMetrics_CALLBACK NULL
#define meshtastic_AirQualityMetrics_DEFAULT NULL
#define meshtastic_LocalStats_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 1) \
X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 2) \
X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 3) \
X(a, STATIC, SINGULAR, UINT32, num_packets_tx, 4) \
X(a, STATIC, SINGULAR, UINT32, num_packets_rx, 5) \
X(a, STATIC, SINGULAR, UINT32, num_packets_rx_bad, 6) \
X(a, STATIC, SINGULAR, UINT32, num_online_nodes, 7) \
X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8)
#define meshtastic_LocalStats_CALLBACK NULL
#define meshtastic_LocalStats_DEFAULT NULL
#define meshtastic_Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FIXED32, time, 1) \
X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \
X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \
X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) \
X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5)
X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) \
X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6)
#define meshtastic_Telemetry_CALLBACK NULL
#define meshtastic_Telemetry_DEFAULT NULL
#define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics
#define meshtastic_Telemetry_variant_environment_metrics_MSGTYPE meshtastic_EnvironmentMetrics
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
#define meshtastic_Telemetry_variant_local_stats_MSGTYPE meshtastic_LocalStats
#define meshtastic_Nau7802Config_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \
@@ -356,6 +444,7 @@ extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg;
extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
extern const pb_msgdesc_t meshtastic_LocalStats_msg;
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
@@ -364,6 +453,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_EnvironmentMetrics_fields &meshtastic_EnvironmentMetrics_msg
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
#define meshtastic_LocalStats_fields &meshtastic_LocalStats_msg
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
#define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg
@@ -372,6 +462,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_AirQualityMetrics_size 72
#define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 85
#define meshtastic_LocalStats_size 42
#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 92

View File

@@ -64,4 +64,13 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes)
for (size_t i = 0; i < numbytes; i++)
LOG_DEBUG("%02x ", p[i]);
LOG_DEBUG("\n");
}
bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes)
{
for (int i = 0; i < numbytes; i++) {
if (mem[i] != find)
return false;
}
return true;
}

View File

@@ -12,4 +12,9 @@ template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi
#define STRNSTR
#include <string.h>
char *strnstr(const char *s, const char *find, size_t slen);
#endif
#endif
void printBytes(const char *label, const uint8_t *p, size_t numbytes);
// is the memory region filled with a single character?
bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes);

View File

@@ -3,6 +3,8 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "meshUtils.h"
#include <FSCommon.h>
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BleOta.h"
@@ -65,7 +67,28 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
bool handled = false;
assert(r);
bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum();
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
return handled;
}
meshtastic_Channel *ch = &channels.getByIndex(mp.channel);
// Could tighten this up further by tracking the last public_key we went an AdminMessage request to
// and only allowing responses from that remote.
if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) ||
(strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) ||
(mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) {
LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant);
return handled;
}
LOG_INFO("Handling admin payload %i\n", r->which_payload_variant);
// all of the get and set messages, including those for other modules, flow through here first.
// any message that changes state, we want to check the passkey for
if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) {
if (!checkPassKey(r)) {
LOG_WARN("Admin message without session_key!\n");
return handled;
}
}
switch (r->which_payload_variant) {
/**
@@ -215,6 +238,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node);
if (node != NULL) {
node->is_favorite = true;
saveChanges(SEGMENT_DEVICESTATE, false);
}
break;
}
@@ -223,6 +247,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node);
if (node != NULL) {
node->is_favorite = false;
saveChanges(SEGMENT_DEVICESTATE, false);
}
break;
}
@@ -257,6 +282,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
break;
}
case meshtastic_AdminMessage_set_time_only_tag: {
LOG_INFO("Client is receiving a set_time_only command.\n");
struct timeval tv;
tv.tv_sec = r->set_time_only;
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityFromNet, &tv, false);
break;
}
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
LOG_INFO("Client is requesting to enter DFU mode.\n");
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
@@ -287,6 +321,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllModules(mp, r, &res);
if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) {
setPassKey(&res);
myReply = allocDataProtobuf(res);
} else if (mp.decoded.want_response) {
LOG_DEBUG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_payload_variant);
@@ -317,7 +352,7 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp,
if (devicestate.node_remote_hardware_pins[i].node_num == 0 || !devicestate.node_remote_hardware_pins[i].has_pin) {
continue;
}
for (uint8_t j = 0; j < sizeof(r->get_module_config_response.payload_variant.remote_hardware.available_pins); j++) {
for (uint8_t j = 0; j < r->get_module_config_response.payload_variant.remote_hardware.available_pins_count; j++) {
auto availablePin = r->get_module_config_response.payload_variant.remote_hardware.available_pins[j];
if (i < devicestate.node_remote_hardware_pins_count) {
devicestate.node_remote_hardware_pins[i].node_num = mp.from;
@@ -383,8 +418,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
#endif
if (config.device.button_gpio == c.payload_variant.device.button_gpio &&
config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio &&
config.device.debug_log_enabled == c.payload_variant.device.debug_log_enabled &&
config.device.serial_enabled == c.payload_variant.device.serial_enabled &&
config.device.role == c.payload_variant.device.role &&
config.device.disable_triple_click == c.payload_variant.device.disable_triple_click &&
config.device.rebroadcast_mode == c.payload_variant.device.rebroadcast_mode) {
@@ -501,6 +534,19 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
config.has_bluetooth = true;
config.bluetooth = c.payload_variant.bluetooth;
break;
case meshtastic_Config_security_tag:
LOG_INFO("Setting config: Security\n");
config.security = c.payload_variant.security;
owner.public_key.size = config.security.public_key.size;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
#if !MESHTASTIC_EXCLUDE_PKI
crypto->setDHPrivateKey(config.security.private_key.bytes);
#endif
if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled &&
config.security.serial_enabled == c.payload_variant.security.serial_enabled)
requiresReboot = false;
break;
}
saveChanges(changes, requiresReboot);
@@ -602,6 +648,7 @@ void AdminModule::handleGetOwner(const meshtastic_MeshPacket &req)
res.get_owner_response = owner;
res.which_payload_variant = meshtastic_AdminMessage_get_owner_response_tag;
setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -649,6 +696,15 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
res.get_config_response.which_payload_variant = meshtastic_Config_bluetooth_tag;
res.get_config_response.payload_variant.bluetooth = config.bluetooth;
break;
case meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG:
LOG_INFO("Getting config: Security\n");
res.get_config_response.which_payload_variant = meshtastic_Config_security_tag;
res.get_config_response.payload_variant.security = config.security;
break;
case meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG:
LOG_INFO("Getting config: Sessionkey\n");
res.get_config_response.which_payload_variant = meshtastic_Config_sessionkey_tag;
break;
}
// 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
@@ -658,6 +714,7 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
// and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password);
// r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag;
res.which_payload_variant = meshtastic_AdminMessage_get_config_response_tag;
setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -743,6 +800,7 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
// and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password);
// r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag;
res.which_payload_variant = meshtastic_AdminMessage_get_module_config_response_tag;
setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -766,6 +824,7 @@ void AdminModule::handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &r
nodePin.pin = moduleConfig.remote_hardware.available_pins[i];
r.get_node_remote_hardware_pins_response.node_remote_hardware_pins[i + 12] = nodePin;
}
setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -774,6 +833,7 @@ void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req)
meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
r.get_device_metadata_response = getDeviceMetadata();
r.which_payload_variant = meshtastic_AdminMessage_get_device_metadata_response_tag;
setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -837,6 +897,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
r.get_device_connection_status_response = conn;
r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag;
setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -847,6 +908,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
r.get_channel_response = channels.getByIndex(channelIndex);
r.which_payload_variant = meshtastic_AdminMessage_get_channel_response_tag;
setPassKey(&r);
myReply = allocDataProtobuf(r);
}
}
@@ -900,5 +962,61 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)
AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg)
{
// restrict to the admin channel for rx
boundChannel = Channels::adminChannel;
// boundChannel = Channels::adminChannel;
}
void AdminModule::setPassKey(meshtastic_AdminMessage *res)
{
if (session_time == 0 || millis() / 1000 > session_time + 150) {
for (int i = 0; i < 8; i++) {
session_passkey[i] = random();
}
session_time = millis() / 1000;
}
memcpy(res->session_passkey.bytes, session_passkey, 8);
res->session_passkey.size = 8;
printBytes("Setting admin key to ", res->session_passkey.bytes, 8);
// if halfway to session_expire, regenerate session_passkey, reset the timeout
// set the key in the packet
}
bool AdminModule::checkPassKey(meshtastic_AdminMessage *res)
{ // check that the key in the packet is still valid
printBytes("Incoming session key: ", res->session_passkey.bytes, 8);
printBytes("Expected session key: ", session_passkey, 8);
return (session_time + 300 > millis() / 1000 && res->session_passkey.size == 8 &&
memcmp(res->session_passkey.bytes, session_passkey, 8) == 0);
}
bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r)
{
if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag ||
r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag)
return true;
else
return false;
}
bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r)
{
if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_owner_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_config_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_module_config_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag)
return true;
else
return false;
}

View File

@@ -25,6 +25,9 @@ class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Obser
private:
bool hasOpenEditTransaction = false;
uint8_t session_passkey[8] = {0};
uint session_time = 0;
void saveChanges(int saveWhat, bool shouldReboot = true);
/**
@@ -48,6 +51,12 @@ class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Obser
void handleSetChannel();
void handleSetHamMode(const meshtastic_HamParameters &req);
void reboot(int32_t seconds);
void setPassKey(meshtastic_AdminMessage *res);
bool checkPassKey(meshtastic_AdminMessage *res);
bool messageIsResponse(meshtastic_AdminMessage *r);
bool messageIsRequest(meshtastic_AdminMessage *r);
};
extern AdminModule *adminModule;

View File

@@ -120,7 +120,7 @@ int32_t NeighborInfoModule::runOnce()
if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) {
sendNeighborInfo(NODENUM_BROADCAST, false);
}
return Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
}
/*

View File

@@ -32,19 +32,22 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
return false; // Let others look at this message also if they want
}
void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel)
void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel, bool _shorterTimeout)
{
// cancel any not yet sent (now stale) position packets
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
service->cancelSending(prevPacketId);
shorterTimeout = _shorterTimeout;
meshtastic_MeshPacket *p = allocReply();
if (p) { // Check whether we didn't ignore it
p->to = dest;
p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
wantReplies;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
if (_shorterTimeout)
p->priority = meshtastic_MeshPacket_Priority_DEFAULT;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
if (channel > 0) {
LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel);
p->channel = channel;
@@ -53,6 +56,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
prevPacketId = p->id;
service->sendToMesh(p);
shorterTimeout = false;
}
}
@@ -65,10 +69,14 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
}
uint32_t now = millis();
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL;
} else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) {
LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL;
} else {
ignoreRequest = false; // Don't ignore requests anymore
meshtastic_User &u = owner;

View File

@@ -20,7 +20,8 @@ class NodeInfoModule : public ProtobufModule<meshtastic_User>, private concurren
/**
* Send our NodeInfo into the mesh
*/
void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0);
void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0,
bool _shorterTimeout = false);
protected:
/** Called to handle a particular incoming message
@@ -38,6 +39,7 @@ class NodeInfoModule : public ProtobufModule<meshtastic_User>, private concurren
private:
uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh
bool shorterTimeout = false;
};
extern NodeInfoModule *nodeInfoModule;

View File

@@ -187,16 +187,23 @@ meshtastic_MeshPacket *PositionModule::allocReply()
p.longitude_i = localPosition.longitude_i;
}
p.precision_bits = precision;
p.has_latitude_i = true;
p.has_longitude_i = true;
p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time;
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) {
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL)
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) {
p.altitude = localPosition.altitude;
else
p.has_altitude = true;
} else {
p.altitude_hae = localPosition.altitude_hae;
p.has_altitude_hae = true;
}
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION)
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) {
p.altitude_geoidal_separation = localPosition.altitude_geoidal_separation;
p.has_altitude_geoidal_separation = true;
}
}
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_DOP) {
@@ -216,11 +223,15 @@ meshtastic_MeshPacket *PositionModule::allocReply()
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SEQ_NO)
p.seq_number = localPosition.seq_number;
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING)
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) {
p.ground_track = localPosition.ground_track;
p.has_ground_track = true;
}
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED)
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) {
p.ground_speed = localPosition.ground_speed;
p.has_ground_speed = true;
}
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS or NTP to include the time, so that devices
@@ -298,7 +309,8 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
if (channels.getByIndex(channel).settings.has_module_settings) {
precision = channels.getByIndex(channel).settings.module_settings.position_precision;
} else if (channels.getByIndex(channel).role == meshtastic_Channel_Role_PRIMARY) {
precision = 32;
// backwards compatibility for Primary channels created before position_precision was set by default
precision = 13;
} else {
precision = 0;
}

View File

@@ -13,6 +13,19 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
bool maybePKI =
mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && mp.to != NODENUM_BROADCAST;
// Beginning of logic whether to drop the packet based on Rebroadcast mode
if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
(config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY ||
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) {
if (!maybePKI)
return false;
if ((nodeDB->getMeshNode(mp.from) == NULL || !nodeDB->getMeshNode(mp.from)->has_user) &&
(nodeDB->getMeshNode(mp.to) == NULL || !nodeDB->getMeshNode(mp.to)->has_user))
return false;
}
// FIXME - move this to a non promsicious PhoneAPI module?
// 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)) {
@@ -65,6 +78,9 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{
isPromiscuous = true;
encryptedOk = config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY &&
config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY;
// moved the ReboradcastMode logic into handleReceivedProtobuf
// LocalOnly requires either the from or to to be a known node
// knownOnly specifically requires the from to be a known node.
encryptedOk = true;
}

View File

@@ -5,6 +5,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "RadioLibInterface.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
@@ -31,6 +32,10 @@ int32_t DeviceTelemetryModule::runOnce()
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true);
if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) {
sendLocalStatsToPhone();
lastSentStatsToPhone = uptimeLastMs;
}
}
return sendToPhoneIntervalMs;
}
@@ -84,6 +89,13 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
t.time = getTime();
t.variant.device_metrics = meshtastic_DeviceMetrics_init_zero;
t.variant.device_metrics.has_air_util_tx = true;
t.variant.device_metrics.has_battery_level = true;
t.variant.device_metrics.has_channel_utilization = true;
t.variant.device_metrics.has_voltage = true;
t.variant.device_metrics.has_uptime_seconds = true;
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
#if ARCH_PORTDUINO
t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL;
@@ -98,6 +110,40 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
return t;
}
void DeviceTelemetryModule::sendLocalStatsToPhone()
{
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
telemetry.which_variant = meshtastic_Telemetry_local_stats_tag;
telemetry.variant.local_stats = meshtastic_LocalStats_init_zero;
telemetry.time = getTime();
telemetry.variant.local_stats.uptime_seconds = getUptimeSeconds();
telemetry.variant.local_stats.channel_utilization = airTime->channelUtilizationPercent();
telemetry.variant.local_stats.air_util_tx = airTime->utilizationTXPercent();
telemetry.variant.local_stats.num_online_nodes = numOnlineNodes;
telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes();
if (RadioLibInterface::instance) {
telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood;
telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood;
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
}
LOG_INFO(
"(Sending local stats): uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i\n",
telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization,
telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes,
telemetry.variant.local_stats.num_total_nodes);
LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i\n", telemetry.variant.local_stats.num_packets_tx,
telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad);
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
service->sendToPhone(p);
}
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry telemetry = getDeviceTelemetry();

View File

@@ -42,7 +42,10 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu
private:
meshtastic_Telemetry getDeviceTelemetry();
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
void sendLocalStatsToPhone();
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes
uint32_t lastSentStatsToPhone = 0;
uint32_t lastSentToMesh = 0;
void refreshUptime()

View File

@@ -289,6 +289,7 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
bool hasSensor = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero;
#ifdef T1000X_SENSOR_EN // add by WayenWeng
valid = valid && t1000xSensor.getMetrics(m);

View File

@@ -32,6 +32,9 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;

View File

@@ -31,6 +31,10 @@ void BME280Sensor::setup() {}
bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BME280Sensor::getMetrics\n");
bme280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bme280.readTemperature();

View File

@@ -54,6 +54,13 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_gas_resistance = true;
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
measurement->variant.environment_metrics.relative_humidity =
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;

View File

@@ -26,6 +26,9 @@ void BMP085Sensor::setup() {}
bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BMP085Sensor::getMetrics\n");
measurement->variant.environment_metrics.temperature = bmp085.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F;

View File

@@ -31,6 +31,9 @@ void BMP280Sensor::setup() {}
bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BMP280Sensor::getMetrics\n");
bmp280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bmp280.readTemperature();

View File

@@ -35,6 +35,12 @@ void DFRobotLarkSensor::setup() {}
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_wind_speed = true;
measurement->variant.environment_metrics.has_wind_direction = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();

View File

@@ -32,6 +32,9 @@ void INA219Sensor::setup() {}
bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V();
measurement->variant.environment_metrics.current = ina219.getCurrent_mA() * INA219_MULTIPLIER;
return true;

View File

@@ -26,6 +26,9 @@ void INA260Sensor::setup() {}
bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
// mV conversion to V
measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000;
measurement->variant.environment_metrics.current = ina260.readCurrent();

View File

@@ -67,6 +67,9 @@ bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurement m = getMeasurement(ENV_CH);
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = m.voltage;
measurement->variant.environment_metrics.current = m.current;
@@ -77,6 +80,13 @@ bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurements m = getMeasurements();
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch1_current = true;
measurement->variant.power_metrics.has_ch2_voltage = true;
measurement->variant.power_metrics.has_ch2_current = true;
measurement->variant.power_metrics.has_ch3_voltage = true;
measurement->variant.power_metrics.has_ch3_current = true;
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;

View File

@@ -27,6 +27,9 @@ void LPS22HBSensor::setup()
bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
sensors_event_t temp;
sensors_event_t pressure;
lps22hb.getEvent(&pressure, &temp);

View File

@@ -26,6 +26,8 @@ void MCP9808Sensor::setup()
bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
LOG_DEBUG("MCP9808Sensor::getMetrics\n");
measurement->variant.environment_metrics.temperature = mcp9808.readTempC();
return true;

View File

@@ -32,6 +32,7 @@ void MLX90632Sensor::setup() {}
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
return true;

View File

@@ -45,6 +45,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
return false;
}
}
measurement->variant.environment_metrics.has_weight = true;
// Check if we have correct calibration values after powerup
LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg

View File

@@ -25,10 +25,10 @@ void OPT3001Sensor::setup()
{
OPT3001_Config newConfig;
newConfig.RangeNumber = B1100;
newConfig.ConvertionTime = B0;
newConfig.Latch = B1;
newConfig.ModeOfConversionOperation = B11;
newConfig.RangeNumber = 0b1100;
newConfig.ConvertionTime = 0b0;
newConfig.Latch = 0b1;
newConfig.ModeOfConversionOperation = 0b11;
OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig);
if (errorConfig != NO_ERROR) {
@@ -38,6 +38,7 @@ void OPT3001Sensor::setup()
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
OPT3001 result = opt3001.readResult();
measurement->variant.environment_metrics.lux = result.lux;

View File

@@ -23,6 +23,7 @@ void RCWL9620Sensor::setup() {}
bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_distance = true;
LOG_DEBUG("RCWL9620Sensor::getMetrics\n");
measurement->variant.environment_metrics.distance = getDistance();
return true;

View File

@@ -27,6 +27,8 @@ void SHT31Sensor::setup()
bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.temperature = sht31.readTemperature();
measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity();

View File

@@ -39,6 +39,9 @@ void SHT4XSensor::setup()
bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
sensors_event_t humidity, temp;
sht4x.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;

View File

@@ -26,6 +26,9 @@ void SHTC3Sensor::setup()
bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);

View File

@@ -108,6 +108,9 @@ float T1000xSensor::getTemp()
bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.temperature = getTemp();
measurement->variant.environment_metrics.lux = getLux();
return true;

View File

@@ -29,6 +29,7 @@ void TSL2591Sensor::setup()
bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;

View File

@@ -53,6 +53,9 @@ float VEML7700Sensor::getResolution(void)
bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.has_white_lux = true;
int16_t white;
measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO);
white = veml7700.readWhite(true);

View File

@@ -5,71 +5,141 @@ TraceRouteModule *traceRouteModule;
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
{
// Only handle a response
if (mp.decoded.request_id) {
printRoute(r, mp.to, mp.from);
}
// We only alter the packet in alterReceivedProtobuf()
return false; // let it be handled by RoutingModule
}
void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
{
auto &incoming = p.decoded;
// Only append IDs for the request (one way)
if (!incoming.request_id) {
// Insert unknown hops if necessary
insertUnknownHops(p, r);
// Don't add ourselves if we are the destination (the reply will have our NodeNum already)
if (p.to != nodeDB->getNodeNum()) {
appendMyID(r);
printRoute(r, p.from, NODENUM_BROADCAST);
}
// Set updated route to the payload of the to be flooded packet
p.decoded.payload.size =
pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r);
}
// Insert unknown hops if necessary
insertUnknownHops(p, r, !incoming.request_id);
// Append ID and SNR. For the last hop (p.to == nodeDB->getNodeNum()), we only need to append the SNR
appendMyIDandSNR(r, p.rx_snr, !incoming.request_id, p.to == nodeDB->getNodeNum());
if (!incoming.request_id)
printRoute(r, p.from, p.to, true);
else
printRoute(r, p.to, p.from, false);
// Set updated route to the payload of the to be flooded packet
p.decoded.payload.size =
pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r);
}
void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination)
{
pb_size_t *route_count;
uint32_t *route;
pb_size_t *snr_count;
int8_t *snr_list;
// Pick the correct route array and SNR list
if (isTowardsDestination) {
route_count = &r->route_count;
route = r->route;
snr_count = &r->snr_towards_count;
snr_list = r->snr_towards;
} else {
route_count = &r->route_back_count;
route = r->route_back;
snr_count = &r->snr_back_count;
snr_list = r->snr_back;
}
// Only insert unknown hops if hop_start is valid
if (p.hop_start != 0 && p.hop_limit <= p.hop_start) {
uint8_t hopsTaken = p.hop_start - p.hop_limit;
int8_t diff = hopsTaken - r->route_count;
int8_t diff = hopsTaken - *route_count;
for (uint8_t i = 0; i < diff; i++) {
if (r->route_count < sizeof(r->route) / sizeof(r->route[0])) {
r->route[r->route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
r->route_count += 1;
if (*route_count < sizeof(*route) / sizeof(route[0])) {
route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
*route_count += 1;
}
}
// Add unknown SNR values if necessary
diff = *route_count - *snr_count;
for (uint8_t i = 0; i < diff; i++) {
if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) {
snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR
*snr_count += 1;
}
}
}
}
void TraceRouteModule::appendMyID(meshtastic_RouteDiscovery *updated)
void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, float snr, bool isTowardsDestination, bool SNRonly)
{
pb_size_t *route_count;
uint32_t *route;
pb_size_t *snr_count;
int8_t *snr_list;
// Pick the correct route array and SNR list
if (isTowardsDestination) {
route_count = &updated->route_count;
route = updated->route;
snr_count = &updated->snr_towards_count;
snr_list = updated->snr_towards;
} else {
route_count = &updated->route_back_count;
route = updated->route_back;
snr_count = &updated->snr_back_count;
snr_list = updated->snr_back;
}
if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) {
snr_list[*snr_count] = (int8_t)(snr * 4); // Convert SNR to 1 byte
*snr_count += 1;
}
if (SNRonly)
return;
// Length of route array can normally not be exceeded due to the max. hop_limit of 7
if (updated->route_count < sizeof(updated->route) / sizeof(updated->route[0])) {
updated->route[updated->route_count] = myNodeInfo.my_node_num;
updated->route_count += 1;
if (*route_count < sizeof(*route) / sizeof(route[0])) {
route[*route_count] = myNodeInfo.my_node_num;
*route_count += 1;
} else {
LOG_WARN("Route exceeded maximum hop limit, are you bridging networks?\n");
}
}
void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest)
void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination)
{
#ifdef DEBUG_PORT
LOG_INFO("Route traced:\n");
LOG_INFO("0x%x --> ", origin);
for (uint8_t i = 0; i < r->route_count; i++) {
LOG_INFO("0x%x --> ", r->route[i]);
if (i < r->snr_towards_count && r->snr_towards[i] != INT8_MIN)
LOG_INFO("0x%x (%.2fdB) --> ", r->route[i], (float)r->snr_towards[i] / 4);
else
LOG_INFO("0x%x (?dB) --> ", r->route[i]);
}
if (dest != NODENUM_BROADCAST)
LOG_INFO("0x%x\n", dest);
else
// If we are the destination, or it has already reached the destination, print it
if (dest == nodeDB->getNodeNum() || !isTowardsDestination) {
if (r->snr_towards_count > 0 && r->snr_towards[r->snr_towards_count - 1] != INT8_MIN)
LOG_INFO("0x%x (%.2fdB)\n", dest, (float)r->snr_towards[r->snr_towards_count - 1] / 4);
else
LOG_INFO("0x%x (?dB)\n", dest);
} else
LOG_INFO("...\n");
// If there's a route back (or we are the destination as then the route is complete), print it
if (r->route_back_count > 0 || origin == nodeDB->getNodeNum()) {
if (r->snr_towards_count > 0 && origin == nodeDB->getNodeNum())
LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[r->snr_back_count - 1] / 4, origin);
else
LOG_INFO("...");
for (int8_t i = r->route_back_count - 1; i >= 0; i--) {
if (i < r->snr_back_count && r->snr_back[i] != INT8_MIN)
LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[i] / 4, r->route_back[i]);
else
LOG_INFO("(?dB) 0x%x <-- ", r->route_back[i]);
}
LOG_INFO("0x%x\n", dest);
}
#endif
}
@@ -86,8 +156,6 @@ meshtastic_MeshPacket *TraceRouteModule::allocReply()
pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_RouteDiscovery_msg, &scratch);
updated = &scratch;
printRoute(updated, req.from, req.to);
// Create a MeshPacket with this payload and set it as the reply
meshtastic_MeshPacket *reply = allocDataProtobuf(*updated);

View File

@@ -20,15 +20,15 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>
private:
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
// Call to add your ID to the route array of a RouteDiscovery message
void appendMyID(meshtastic_RouteDiscovery *r);
void appendMyIDandSNR(meshtastic_RouteDiscovery *r, float snr, bool isTowardsDestination, bool SNRonly);
/* Call to print the route array of a RouteDiscovery message.
Set origin to where the request came from.
Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */
void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest);
void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination);
};
extern TraceRouteModule *traceRouteModule;

View File

@@ -152,7 +152,8 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
LOG_INFO("Ignoring downlink message we originally sent.\n");
} else {
// Find channel by channel_id and check downlink_enabled
if (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled) {
if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) ||
(strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) {
LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length);
meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet);
p->via_mqtt = true; // Mark that the packet was received via MQTT
@@ -161,8 +162,16 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
p->channel = ch.index;
}
// ignore messages if we don't have the channel key
if (router && perhapsDecode(p))
// PKI messages get accepted even if we can't decrypt
if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
strcmp(e.channel_id, "PKI") == 0) {
meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p));
meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
// Only accept PKI messages if we have both the sender and receiver in our nodeDB, as then it's likely
// they discovered each other via a channel we have downlink enabled for
if (tx && tx->has_user && rx && rx->has_user)
router->enqueueReceivedMessage(p);
} else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key
router->enqueueReceivedMessage(p);
else
packetPool.release(p);
@@ -361,22 +370,31 @@ void MQTT::reconnect()
void MQTT::sendSubscriptions()
{
#if HAS_NETWORKING
bool hasDownlink = false;
size_t numChan = channels.getNumChannels();
for (size_t i = 0; i < numChan; i++) {
const auto &ch = channels.getByIndex(i);
if (ch.settings.downlink_enabled) {
std::string topic = cryptTopic + channels.getGlobalId(i) + "/#";
hasDownlink = true;
std::string topic = cryptTopic + channels.getGlobalId(i) + "/+";
LOG_INFO("Subscribing to %s\n", topic.c_str());
pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right?
#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804
if (moduleConfig.mqtt.json_enabled == true) {
std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/#";
std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+";
LOG_INFO("Subscribing to %s\n", topicDecoded.c_str());
pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right?
}
#endif // ARCH_NRF52
}
}
#if !MESHTASTIC_EXCLUDE_PKI
if (hasDownlink) {
std::string topic = cryptTopic + "PKI/+";
LOG_INFO("Subscribing to %s\n", topic.c_str());
pubSub.subscribe(topic.c_str(), 1);
}
#endif
#endif
}
@@ -452,8 +470,12 @@ void MQTT::publishQueuedMessages()
meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
std::string topic = cryptTopic + env->channel_id + "/" + owner.id;
std::string topic;
if (env->packet->pki_encrypted) {
topic = cryptTopic + "PKI/" + owner.id;
} else {
topic = cryptTopic + env->channel_id + "/" + owner.id;
}
LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
publish(topic.c_str(), bytes, numBytes, false);
@@ -463,7 +485,12 @@ void MQTT::publishQueuedMessages()
// handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
if (jsonString.length() != 0) {
std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id;
std::string topicJson;
if (env->packet->pki_encrypted) {
topicJson = jsonTopic + "PKI/" + owner.id;
} else {
topicJson = jsonTopic + env->channel_id + "/" + owner.id;
}
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
jsonString.c_str());
publish(topicJson.c_str(), jsonString.c_str(), false);
@@ -478,7 +505,13 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
{
if (mp.via_mqtt)
return; // Don't send messages that came from MQTT back into MQTT
bool uplinkEnabled = false;
for (int i = 0; i <= 7; i++) {
if (channels.getByIndex(i).settings.uplink_enabled)
uplinkEnabled = true;
}
if (!uplinkEnabled)
return; // no channels have an uplink enabled
auto &ch = channels.getByIndex(chIndex);
if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
@@ -493,7 +526,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
return;
}
if (ch.settings.uplink_enabled) {
if (ch.settings.uplink_enabled || mp.pki_encrypted) {
const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel
meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed();
@@ -513,8 +546,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
std::string topic = cryptTopic + channelId + "/" + owner.id;
std::string topic;
if (mp.pki_encrypted) {
topic = cryptTopic + "PKI/" + owner.id;
} else {
topic = cryptTopic + channelId + "/" + owner.id;
}
LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes);
publish(topic.c_str(), bytes, numBytes, false);
@@ -524,7 +561,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
// handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded);
if (jsonString.length() != 0) {
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
std::string topicJson;
if (mp.pki_encrypted) {
topicJson = jsonTopic + "PKI/" + owner.id;
} else {
topicJson = jsonTopic + channelId + "/" + owner.id;
}
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
jsonString.c_str());
publish(topicJson.c_str(), jsonString.c_str(), false);
@@ -632,4 +674,4 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json)
(json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us
(json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
(json.find("payload") != json.end()); // should have a payload
}
}

View File

@@ -13,58 +13,29 @@ class ESP32CryptoEngine : public CryptoEngine
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); }
/**
* Set the key used for encrypt, decrypt.
*
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
*
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(const CryptoKey &k) override
{
CryptoEngine::setKey(k);
if (key.length != 0) {
auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8);
assert(!res);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
* TODO: return bool, and handle graciously when something fails
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 0) {
LOG_DEBUG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes);
initNonce(fromNode, packetId);
if (_key.length > 0) {
if (numBytes <= MAX_BLOCKSIZE) {
mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8);
static uint8_t scratch[MAX_BLOCKSIZE];
uint8_t stream_block[16];
size_t nc_off = 0;
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes);
assert(!res);
mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetId, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new ESP32CryptoEngine();
CryptoEngine *crypto = new ESP32CryptoEngine();

View File

@@ -42,6 +42,9 @@
#ifndef DEFAULT_VREF
#define DEFAULT_VREF 1100
#endif
#ifndef HAS_CUSTOM_CRYPTO_ENGINE
#define HAS_CUSTOM_CRYPTO_ENGINE 1
#endif
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
#define HAS_PMU
@@ -149,6 +152,8 @@
#define HW_VENDOR meshtastic_HardwareModel_WIPHONE
#elif defined(RADIOMASTER_900_BANDIT_NANO)
#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO
#elif defined(RADIOMASTER_900_BANDIT)
#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT
#elif defined(HELTEC_CAPSULE_SENSOR_V3)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3
#elif defined(HELTEC_VISION_MASTER_T190)

View File

@@ -9,41 +9,24 @@ class NRF52CryptoEngine : public CryptoEngine
~NRF52CryptoEngine() {}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 16) {
LOG_DEBUG("Software encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes);
if (_key.length > 16) {
AES_ctx ctx;
initNonce(fromNode, packetId);
AES_init_ctx_iv(&ctx, key.bytes, nonce);
AES_init_ctx_iv(&ctx, _key.bytes, _nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
} else if (key.length > 0) {
LOG_DEBUG("nRF52 encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes);
} else if (_key.length > 0) {
nRFCrypto.begin();
nRFCrypto_AES ctx;
uint8_t myLen = ctx.blockLen(numBytes);
char encBuf[myLen] = {0};
initNonce(fromNode, packetId);
ctx.begin();
ctx.Process((char *)bytes, numBytes, nonce, key.bytes, key.length, encBuf, ctx.encryptFlag, ctx.ctrMode);
ctx.Process((char *)bytes, numBytes, _nonce, _key.bytes, _key.length, encBuf, ctx.encryptFlag, ctx.ctrMode);
ctx.end();
nRFCrypto.end();
memcpy(bytes, encBuf, numBytes);
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetId, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new NRF52CryptoEngine();
CryptoEngine *crypto = new NRF52CryptoEngine();

View File

@@ -32,6 +32,9 @@
#ifndef HAS_CPU_SHUTDOWN
#define HAS_CPU_SHUTDOWN 1
#endif
#ifndef HAS_CUSTOM_CRYPTO_ENGINE
#define HAS_CUSTOM_CRYPTO_ENGINE 1
#endif
//
// set HW_VENDOR
@@ -62,8 +65,8 @@
#define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110
#elif defined(TRACKER_T1000_E)
#define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E
#elif defined(ME25LS01)
#define HW_VENDOR meshtastic_HardwareModel_ME25LS01
#elif defined(ME25LS01_4Y10TD)
#define HW_VENDOR meshtastic_HardwareModel_ME25LS01_4Y10TD
#elif defined(PRIVATE_HW) || defined(FEATHER_DIY)
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#else

View File

@@ -1,78 +0,0 @@
#include "AES.h"
#include "CTR.h"
#include "CryptoEngine.h"
#include "configuration.h"
/** A platform independent AES engine implemented using Tiny-AES
*/
class CrossPlatformCryptoEngine : public CryptoEngine
{
CTRCommon *ctr = NULL;
public:
CrossPlatformCryptoEngine() {}
~CrossPlatformCryptoEngine() {}
/**
* Set the key used for encrypt, decrypt.
*
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
*
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(const CryptoKey &k) override
{
CryptoEngine::setKey(k);
LOG_DEBUG("Installing AES%d key!\n", key.length * 8);
if (ctr) {
delete ctr;
ctr = NULL;
}
if (key.length != 0) {
if (key.length == 16)
ctr = new CTR<AES128>();
else
ctr = new CTR<AES256>();
ctr->setKey(key.bytes, key.length);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 0) {
initNonce(fromNode, packetId);
if (numBytes <= MAX_BLOCKSIZE) {
static uint8_t scratch[MAX_BLOCKSIZE];
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
ctr->setIV(nonce, sizeof(nonce));
ctr->setCounterSize(4);
ctr->encrypt(bytes, scratch, numBytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetId, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new CrossPlatformCryptoEngine();

View File

@@ -1,4 +1,5 @@
#include "configuration.h"
#include "hardware/xosc.h"
#include <hardware/clocks.h>
#include <hardware/pll.h>
#include <pico/stdlib.h>
@@ -12,7 +13,11 @@ void setBluetoothEnable(bool enable)
void cpuDeepSleep(uint32_t msecs)
{
// not needed
/* Disable both PLL to avoid power dissipation */
pll_deinit(pll_sys);
pll_deinit(pll_usb);
/* Set RP2040 in dormant mode. Will not wake up. */
xosc_dormant();
}
void updateBatteryLevel(uint8_t level)

View File

@@ -1,66 +0,0 @@
#include "AES.h"
#include "CTR.h"
#include "CryptoEngine.h"
#include "configuration.h"
class RP2040CryptoEngine : public CryptoEngine
{
CTRCommon *ctr = NULL;
public:
RP2040CryptoEngine() {}
~RP2040CryptoEngine() {}
virtual void setKey(const CryptoKey &k) override
{
CryptoEngine::setKey(k);
LOG_DEBUG("Installing AES%d key!\n", key.length * 8);
if (ctr) {
delete ctr;
ctr = NULL;
}
if (key.length != 0) {
if (key.length == 16)
ctr = new CTR<AES128>();
else
ctr = new CTR<AES256>();
ctr->setKey(key.bytes, key.length);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 0) {
initNonce(fromNode, packetId);
if (numBytes <= MAX_BLOCKSIZE) {
static uint8_t scratch[MAX_BLOCKSIZE];
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
ctr->setIV(nonce, sizeof(nonce));
ctr->setCounterSize(4);
ctr->encrypt(bytes, scratch, numBytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetId, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new RP2040CryptoEngine();

View File

@@ -1,67 +0,0 @@
#undef RNG
#include "AES.h"
#include "CTR.h"
#include "CryptoEngine.h"
#include "configuration.h"
class STM32WLCryptoEngine : public CryptoEngine
{
CTRCommon *ctr = NULL;
public:
STM32WLCryptoEngine() {}
~STM32WLCryptoEngine() {}
virtual void setKey(const CryptoKey &k) override
{
CryptoEngine::setKey(k);
LOG_DEBUG("Installing AES%d key!\n", key.length * 8);
if (ctr) {
delete ctr;
ctr = NULL;
}
if (key.length != 0) {
if (key.length == 16)
ctr = new CTR<AES128>();
else
ctr = new CTR<AES256>();
ctr->setKey(key.bytes, key.length);
}
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
if (key.length > 0) {
initNonce(fromNode, packetId);
if (numBytes <= MAX_BLOCKSIZE) {
static uint8_t scratch[MAX_BLOCKSIZE];
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
ctr->setIV(nonce, sizeof(nonce));
ctr->setCounterSize(4);
ctr->encrypt(bytes, scratch, numBytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes);
}
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// For CTR, the implementation is the same
encrypt(fromNode, packetId, numBytes, bytes);
}
private:
};
CryptoEngine *crypto = new STM32WLCryptoEngine();

58
test/end2end/flash.py Normal file
View File

@@ -0,0 +1,58 @@
# trunk-ignore-all(bandit/B404)
import subprocess
import usb.core
def find_usb_device(vendor_id, product_id):
# Find USB devices
dev = usb.core.find(find_all=True)
# Loop through devices, printing vendor and product ids in decimal and hex
for cfg in dev:
if cfg.idVendor == vendor_id and cfg.idProduct == product_id:
return cfg
return None
# Flash esp32 target
def flash_esp32(pio_env, port):
# trunk-ignore(bandit/B603)
# trunk-ignore(bandit/B607)
subprocess.run(
["platformio", "run", "-e", pio_env, "-t", "upload", "--upload-port", port]
)
def flash_nrf52(pio_env, port):
# trunk-ignore(bandit/B603)
# trunk-ignore(bandit/B607)
subprocess.run(
["platformio", "run", "-e", pio_env, "-t", "upload", "--upload-port", port]
)
def find_usb_device(vendor_id, product_id):
# Find USB devices
dev = usb.core.find(find_all=True)
# Loop through devices, printing vendor and product ids in decimal and hex
for cfg in dev:
if cfg.idVendor == vendor_id and cfg.idProduct == product_id:
return cfg
return None
# Flash esp32 target
def flash_esp32(pio_env, port):
# trunk-ignore(bandit/B603)
# trunk-ignore(bandit/B607)
subprocess.run(
["platformio", "run", "-e", pio_env, "-t", "upload", "--upload-port", port]
)
def flash_nrf52(pio_env, port):
# trunk-ignore(bandit/B603)
# trunk-ignore(bandit/B607)
subprocess.run(
["platformio", "run", "-e", pio_env, "-t", "upload", "--upload-port", port]
)

42
test/end2end/readprops.py Normal file
View File

@@ -0,0 +1,42 @@
import configparser
import subprocess
def readProps(prefsLoc):
"""Read the version of our project as a string"""
config = configparser.RawConfigParser()
config.read(prefsLoc)
version = dict(config.items("VERSION"))
verObj = dict(
short="{}.{}.{}".format(version["major"], version["minor"], version["build"]),
long="unset",
)
# Try to find current build SHA if if the workspace is clean. This could fail if git is not installed
try:
sha = (
subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
.decode("utf-8")
.strip()
)
isDirty = (
subprocess.check_output(["git", "diff", "HEAD"]).decode("utf-8").strip()
)
suffix = sha
# if isDirty:
# # short for 'dirty', we want to keep our verstrings source for protobuf reasons
# suffix = sha + "-d"
verObj["long"] = "{}.{}.{}.{}".format(
version["major"], version["minor"], version["build"], suffix
)
except:
# print("Unexpected error:", sys.exc_info()[0])
# traceback.print_exc()
verObj["long"] = verObj["short"]
# print("firmware version " + verStr)
return verObj
# print("path is" + ','.join(sys.path))

59
test/end2end/setup.py Normal file
View File

@@ -0,0 +1,59 @@
import time
import flash
import meshtastic
import meshtastic.serial_interface
from readprops import readProps
version = readProps("version.properties")["long"]
def setup_users_prefs(prefsLoc):
with open(prefsLoc, "r") as file:
filedata = file.read()
filedata = filedata.replace(
"// #define CONFIG_LORA_REGION_USERPREFS",
"#define CONFIG_LORA_REGION_USERPREFS",
)
filedata = filedata.replace(
"// #define LORACONFIG_CHANNEL_NUM_USERPREFS",
"#define LORACONFIG_CHANNEL_NUM_USERPREFS",
)
filedata = filedata.replace(
"// #define CHANNEL_0_PRECISION", "#define CHANNEL_0_PRECISION"
)
with open(prefsLoc, "w") as file:
file.write(filedata)
def setup_device(port, pio_env, arch):
interface = meshtastic.serial_interface.SerialInterface(port)
try:
interface.waitForConfig()
if interface.metadata.firmware_version == version:
print("Already at local ref version", version)
else:
print(
"Device has version",
interface.metadata.firmware_version,
" updating to",
version,
)
interface.close()
time.sleep(1)
flash_device(port, pio_env, arch)
time.sleep(2)
except:
interface.close()
time.sleep(1)
flash_device(port, pio_env, arch)
time.sleep(2)
interface = meshtastic.serial_interface.SerialInterface(port)
interface.waitForConfig()
def flash_device(port, pio_env, arch):
if arch == "esp32":
flash.flash_esp32(pio_env=pio_env, port=port)
elif arch == "nrf52":
flash.flash_nrf52(pio_env=pio_env, port=port)

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