Merge pull request #83 from meshtastic/master

update from head
This commit is contained in:
Jm Casler 2021-04-09 20:36:35 -07:00 committed by GitHub
commit a5e3f271ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1130 additions and 633 deletions

View File

@ -22,9 +22,6 @@ jobs:
- name: Install extra python tools
run: |
pip install -U adafruit-nrfutil
- name: Install libs needed for linux build
run: |
sudo apt install -y libpsocksxx-dev
- name: Build for tbeam
run: platformio run -e tbeam
- name: Build for heltec
@ -36,6 +33,7 @@ jobs:
- name: Integration test
run: |
.pio/build/native/program &
sleep 1
sleep 5
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'

View File

@ -15,16 +15,15 @@
<component name="ChangeListManager">
<list default="true" id="58922733-b05b-4b90-9655-b9b18914977a" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/software/TODO.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/software/TODO.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/proto" beforeDir="false" afterPath="$PROJECT_DIR$/proto" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" afterDir="false" />
<change beforePath="$PROJECT_DIR$/proto/docs/docs.md" beforeDir="false" afterPath="$PROJECT_DIR$/proto/docs/docs.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/mqtt/MQTT.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/mqtt/MQTT.h" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:native" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
@ -49,7 +48,7 @@
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="settings.editor.selected.configurable" value="CMakeSettings" />
</component>
<component name="RunManager" selected="PlatformIO.PlatformIO Upload">
<component name="RunManager" selected="GDB Remote Debug.gdbremote-localhost-2345">
<configuration default="true" type="CLion_Remote" version="1" remoteCommand="tcp:localhost:2345" symbolFile="" sysroot="">
<debugger kind="GDB" isBundled="true" />
<method v="2" />
@ -63,6 +62,11 @@
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration default="true" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration default="true" type="GradleAppRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="com.jetbrains.cidr.cpp.gradle.execution.GradleNativeBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
@ -96,6 +100,13 @@
<workItem from="1615788663210" duration="6661000" />
<workItem from="1615938346019" duration="1208000" />
<workItem from="1615971126983" duration="5945000" />
<workItem from="1617115374907" duration="357000" />
<workItem from="1617115747078" duration="1391000" />
<workItem from="1617117632667" duration="307000" />
<workItem from="1617160691713" duration="1016000" />
<workItem from="1617279002260" duration="1626000" />
<workItem from="1617425689081" duration="1896000" />
<workItem from="1617437366919" duration="1182000" />
</task>
<servers />
</component>
@ -117,18 +128,33 @@
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mesh/StreamAPI.cpp</url>
<line>20</line>
<option name="timeStamp" value="4" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mesh/wifi/WiFiServerAPI.cpp</url>
<line>53</line>
<option name="timeStamp" value="6" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mesh/wifi/WiFiServerAPI.cpp</url>
<line>37</line>
<option name="timeStamp" value="7" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mqtt/MQTT.cpp</url>
<line>84</line>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/.pio/libdeps/native/PubSubClient/src/PubSubClient.cpp</url>
<line>468</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
<watches-manager>
<configuration name="CLion_Remote">
<watch expression="radioConfig" language="ObjectiveC" />
</configuration>
</watches-manager>
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

3
bin/mqtt-listen.sh Executable file
View File

@ -0,0 +1,3 @@
mosquitto_sub -h test.mosquitto.org -v -t mesh/stat/\# -t mesh/json/\#
# mosquitto_sub -h test.mosquitto.org -v -t mesh/\# -F "%j"

1
bin/mqtt-send-status.sh Executable file
View File

@ -0,0 +1 @@
mosquitto_pub -h test.mosquitto.org -t mesh/stat/FakeNode -m online -d

View File

@ -1,3 +1,3 @@
set -e
pio run --environment native
gdbserver --once localhost:2345 .pio/build/native/program
gdbserver --once localhost:2345 .pio/build/native/program "$@"

View File

@ -1,3 +1,3 @@
set -e
pio run --environment native
.pio/build/native/program
.pio/build/native/program "$@"

View File

@ -1,11 +1,13 @@
set -e
pio run
TARG=tbeam
pio run -e $TARG
echo uploading to usb1
pio run --upload-port /dev/ttyUSB1 -t upload &
pio run --upload-port /dev/ttyUSB1 -t upload -e $TARG &
echo uploading to usb0
pio run --upload-port /dev/ttyUSB0 -t upload &
pio run --upload-port /dev/ttyUSB0 -t upload -e $TARG &
wait

View File

@ -4,9 +4,25 @@ You probably don't care about this section - skip to the next one.
## before next release
* DONE android speed settings https://github.com/meshtastic/Meshtastic-Android/issues/271
* fix heltec battery scaling
* DONE remote admin busted?
* DONE check android code - @havealoha comments about odd sleep behavior
* ABANDONED test github actions locally on linux
* DONE fix github actions per sasha tip
* tell ttgo to preinstall new bins
* DONE sendtext busted in portduino, due to bytetime calculations
* remove linux dependency in native build
* DONE tcp stream problem in python+pordtuino, server thinks client dropped when client DID NOT DROP
* DONE TCP mode for android, localhost is at 10.0.2.2
* DONE make sure USB still works in android
* add portduino builds to zip
* add license to portduino and make announcement
* DONE naks are being dropped (though enqueuedLocal) sometimes before phone/PC gets them
* DONE have android fill in if local GPS has poor signal
* fix heltec battery scaling
* optionally restrict position sends to a named channel
* release to beta and amazon
* add reference counting to mesh packets
* allow multiple simultanteous phoneapi connections
* DONE split position.time and last_heard
@ -28,7 +44,7 @@ You probably don't care about this section - skip to the next one.
* DONE before next relase: test empty channel sets on android
* DONE channel sharing in android
* DONE test 1.0 firmware update on android
* DONE test 1.1 firmware update on android
* DONE test 1.1 firmwhttps://github.com/meshtastic/Meshtastic-Android/issues/271are update on android
* DONE test 1.2.10 firmware update on android
* DONE test link sharing on android
* FIXED? luxon bug report - seeing rx acks for nodes that are not on the network
@ -36,8 +52,6 @@ You probably don't care about this section - skip to the next one.
* DONE show GPS time only if we know what global time is
* DONE android should always provide time to nodes - so that it is easier for the mesh to learn the current time
## MQTT
## Multichannel support
* DONE cleanup the external notification and serial plugins
@ -120,7 +134,7 @@ You probably don't care about this section - skip to the next one.
* DONE make all subpackets different versions of data
* DONE move routing control into a data packet
* have phoneapi done via plugin (will allow multiple simultaneous API clients - stop disabling BLE while using phone API)
* use reference counting and dynamic sizing for meshpackets.
* use reference counting and dynamic sizing for meshpackets. - use https://docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-160 (already used in arduino)
* let multiple PhoneAPI endpoints work at once
* allow multiple simultaneous bluetooth connections (create the bluetooth phoneapi instance dynamically based on client id)
* DONE figure out how to add micro_delta to position, make it so that phone apps don't need to understand it?
@ -138,6 +152,7 @@ You probably don't care about this section - skip to the next one.
For app cleanup:
* don't store redundant User admin or position broadcasts in the ToPhone queue (only keep one per sending node per proto type, and only most recent)
* use structured logging to kep logs in ram. Also send logs as packets to api clients
* DONE writeup nice python options docs (common cases, link to protobuf docs)
* have android app link to user manual

View File

@ -36,6 +36,14 @@ If you are reviewing our implementation, this is a brief statement of our method
- Each 16 byte BLOCK for a packet has an incrementing COUNTER. COUNTER starts at zero for the first block of each packet.
- The IV for each block is constructed by concatenating the NONCE as the upper 96 bits of the IV and the COUNTER as the bottom 32 bits. Since our packets are small counter portion will really never be higher than 32 (five bits).
### Details on the nonce encoding
(For those porting to other architectures)
Bytes 0-3 of the nonce are the "packet number" in little-endian order
Bytes 4-7 are currently zero
Bytes 8-11 are the sending node ID in little-endian order
Bytes 12-15 are currently zero
## Comments from reviewer #1
This reviewer is a cryptography professional, but would like to remain anonymous. We thank them for their comments ;-):

View File

@ -12,9 +12,13 @@
- [NODEID](#nodeid)
- [USERID](#userid)
- [CHANNELID](#channelid)
- [PORTID](#portid)
- [Gateway nodes](#gateway-nodes)
- [Optional web services](#optional-web-services)
- [MQTTSimInterface](#mqttsiminterface)
- [Web services](#web-services)
- [Public MQTT broker service](#public-mqtt-broker-service)
- [Broker selection](#broker-selection)
- [Admin service](#admin-service)
- [Riot.im messaging bridge](#riotim-messaging-bridge)
- [Deprecated concepts](#deprecated-concepts)
- [MESHID (deprecated)](#meshid-deprecated)
@ -68,17 +72,21 @@ Any gateway-device will contact the MQTT broker.
### Topics
The "mesh/crypt/CHANNELID/NODEID/PORTID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for messages sent from/to a mesh.
* The "/mesh/crypt/CHANNELID/NODEID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for (encrypted) messages sent from/to a mesh.
Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel.
For any channels in the local node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets.
For any channels in the gateway node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets.
If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable.
* If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID".
FIXME, discuss how text message global mirroring could scale (or not)
* If it was possible to republish on mesh/clear/... and the PORTID is wellknown (i.e. the protobufs needed for decoding it are in the master github), it will be decoded and republished as JSON on mesh/json/CHANNELID/NODEID/PortName. This is to facilitate the development of third party apps to visualize 'live' node positions and 'texts' (for users that have opted to send on those explicitly public channels).
FIXME, consider how text message global mirroring could scale (or not)
FIXME, possibly don't global mirror text messages - instead rely on matrix/riot?
FIXME, discuss possible attacks by griefers and how they can be prvented
FIXME, consider possible attacks by griefers and how they can be prvented
* The "/mesh/stat/NODEID" topic contains a simple string showing connection status of nodes. We rely on the MQTT feature for automatically publishing special failrue messages to this topic when the device disconnects.
#### Service Envelope
@ -88,7 +96,7 @@ ServiceEnvelope will include the message, and full information about arrival tim
#### NODEID
The unique ID for a node. A hex string that starts with a ! symbol.
The unique ID for a node. A 8 byte (16 character) hex string that starts with a ! symbol.
#### USERID
@ -96,7 +104,15 @@ A user ID string. This string is either a user ID if known or a nodeid to simply
#### CHANNELID
FIXME, figure out how channelids work
For the time being we simply use the local "channel name" - which is not quite good enough.
FIXME, figure out how channelids work in more detail. They should generally be globally unique, but this is not a requirement. If someone accidentially (or maliciously) sends data using a channel ID they do not 'own' they will still lacking a valid AES256 encryption, so it will be ignored by others.
idea to be pondered: When the user clicks to enable uplink/downlink check the name they entered and 'claim' it on the server?
#### PORTID
Portid is used to descriminated between different packet types which are sent over a channel. As used here it is an integer typically (but not necessarily) chosen from portnums.proto.
### Gateway nodes
@ -107,31 +123,74 @@ Gateway nodes (via code running in the phone) will contain two tables to whiteli
Since multiple gateway nodes might be connected to a single mesh, it is possible that duplicate messages will be published on any particular topic. Therefore subscribers to these topics should
deduplicate if needed by using the packet ID of each message.
### Optional web services
#### Public MQTT broker service
### MQTTSimInterface
This is a bit orthogonal from the main MQTT feature set, but a special simulated LoRa interface called MQTTSimInterface uses the
MQTT messaging infrastructure to send "LoRa" packets between simulated nodes running on Linux. This allows us to test radio topologies and code without having to use real hardware.
This service uses the standard mesh/crypt/... topic, but it picks a special CHANNEL_ID. That CHANNEL_ID is typcially of the form "simmesh_xxx".
FIXME: Figure out how to secure the creation and use of well known CHANNEL_IDs.
## Web services
### Public MQTT broker service
An existing public [MQTT broker](https://mosquitto.org/) will be the default for this service, but clients can use any MQTT broker they choose.
FIXME - figure out how to avoid impersonation (because we are initially using a public mqtt server with no special security options). FIXME, include some ideas on this in the ServiceEnvelope documentation.
#### Riot.im messaging bridge
#### Broker selection
On a previous project I used mosqitto, which I liked, but the admin interface for programmatically managing access was ugly. [This](https://www.openlogic.com/blog/activemq-vs-rabbitmq) article makes me think RabbitMQ might be best for us.
Initially I will try to avoid using any non MQTT broker/library/API
### Admin service
(This is a WIP draft collection of not complete ideas)
The admin service deals with misc global arbibration/access tasks. It is actually reached **through** the MQTT broker, though for security we depend on that broker having a few specialized rules about who can post to or see particular topics (see below).
Topics:
* mesh/ta/# - all requests going towards the admin server (only the admin server can see this topic)
* mesh/tn/NODEID/# - all responses/requests going towards a particlar gateway node (only this particular gateway node is allowed to see this topic)
* mesh/to/NODEID/# - unsecured messages sent to a gateway node (any attacker can see this topic) - used only for "request gateway id" responses
* mesh/ta/toadmin - a request to the admin server, payload is a ToAdmin protobuf
* mesh/tn/NODEID/tonode - a request/response to a particular gateway node. payload is a ToNode protobuf
Operations provided via the ToAdmin/ToNode protocol:
* Register a global channel ID (request a new channel ID). Optionally include the AES key if you would like the web service to automatically decrypt in the cloud
* Request gateway ID - the response is used to re-sign in to the broker.
Possibly might need public key encryption for the gateway request? Since the response is sent to the mesh/to endpoint? I would really like to use MQTT for all comms so I don't need yet another protocol/port from the device.
Idea 1: A gateway ID/signin can only be assigned once per node ID. If a user loses their signin info, they'll need to change their node number. yucky.
Idea 2: Instead gateway signins are assigned at "manufacture" time (and if lost, yes the user would need to "remanufacture" their node). Possibly a simple web service (which can be accessed via the python install script?) that goes to an https endpoint, gets signin info (and server keeps a copy) and stores it in the device. Hardware manufacturers could ask for N gateway IDs via the same API and get back a bunch of small files that could be programmed on each device. Would include node id, etc... Investigate alternatives like storing a particular private key to allow each device to generate their own signin key and the server would trust it by checking against a public key?
TODO/FIXME: look into mqtt broker options, possibly find one with better API support than mosquitto?
### Riot.im messaging bridge
@Geeksville will run a riot.im bridge that talks to the public MQTT broker and sends/receives into the riot.im network.
There is apparently [already](https://github.com/derEisele/tuple) a riot.im [bridge](https://matrix.org/bridges/) for MQTT. That will possibly need to be customized a bit. But by doing this, we should be able to let random riot.im users send/receive messages to/from any meshtastic device. (FIXME ponder security). See this [issue](https://github.com/meshtastic/Meshtastic-Android/issues/2#issuecomment-645660990) with discussion with the dev.
### Deprecated concepts
## Deprecated concepts
You can ignore these for now...
#### MESHID (deprecated)
### MESHID (deprecated)
Earlier drafts of this document included the concept of a MESHID. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
A unique ID for this mesh. There will be some sort of key exchange process so that the mesh ID can not be impersonated by other meshes.
#### DESTCLASS (deprecated)
### DESTCLASS (deprecated)
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
@ -145,7 +204,7 @@ The type of DESTID this message should be delivered to. A short one letter seque
| S | SMS gateway, DESTID is a phone number to reach via Twilio.com |
| E | Emergency message, see bug #fixme for more context |
#### DESTID (deprecated)
### DESTID (deprecated)
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:

View File

@ -26,7 +26,7 @@ These are the settings that can be configured.
For basic usage, you will need two devices both with a GPS. A device with a paired phone with GPS may work, I have not tried it.
The first thing to do is to turn on the plugin. With the plugin turned on, the other settings will be available:
The first thing to do is to turn on the plugin. The device will need to be restarted after appling the settings. With the plugin turned on, the other settings will be available:
range_test_plugin_enabled = 1

View File

@ -1,23 +1,60 @@
# Remote Hardware Service
FIXME - the following are a collection of notes moved from elsewhere. We need to refactor these notes into actual documentation on the remote-hardware/gpio service.
These are 'programmer focused' notes on using the "remote hardware" feature.
### 1.7.2. New 'no-code-IOT' mini-app
Note: This feature uses a preinstalled plugin in the device code and associated commandline flags/classes in the python code. You'll need to be running at least version 1.2.23 (or later) of the python and device code to use this feature.
Add a new 'remote GPIO/serial port/SPI/I2C access' mini-app. This new standard app would use the MQTT messaging layer to let users (developers that don't need to write device code) do basic (potentially dangerous) operations remotely.
You can get the latest python tool/library with "pip3 install --upgrade meshtastic" on Windows/Linux/OS-X.
#### 1.7.2.1. Supported operations in the initial release
Initially supported features for no-code-IOT.
## Supported operations in the initial release
- Set any GPIO
- Read any GPIO
- Receive notification of changes in any GPIO.
#### 1.7.2.2. Supported operations eventually
## Setup
General ideas for no-code IOT.
GPIO access is fundamentally 'dangerous' because invalid options can physically burn-up hardware. To prevent access from untrusted users you must first make a "gpio" channel that is used for authenticated access to this feature. You'll need to install this channel on both the local and remote node.
- Subscribe for notification of GPIO input status change (i.e. when pin goes low, send my app a message)
- Write/read N bytes over I2C/SPI bus Y (as one atomic I2C/SPI transaction)
- Send N bytes out serial port Z
- Subscribe for notification for when regex X matches the bytes that were received on serial port Z
The procedure using the python command line tool is:
1. Connect local device via USB
2. "meshtastic --ch-add admin; meshtastic --info" thn copy the (long) "Complete URL" that info printed
3. Connect remote device via USB (or use the remote admin feature to reach it through the mesh, but that's beyond the scope of this tutorial)
4. "meshtastic --seturl theurlyoucopiedinstep2"
Now both devices can talk over the "gpio" channel.
## Doing GPIO operations
Here's some examples using the command line tool.
## Using GPIOs from python
You can programmatically do operations from your own python code by using the meshtastic "RemoteHardwareClient" class - see the python documentation for more details.
Writing a GPIO
```
meshtastic --port /dev/ttyUSB0 --gpio-wrb 4 1 --dest \!28979058
Connected to radio
Writing GPIO mask 0x10 with value 0x10 to !28979058
```
Reading a GPIO
```
meshtastic --port /dev/ttyUSB0 --gpio-rd 0x10 --dest \!28979058
Connected to radio
Reading GPIO mask 0x10 from !28979058
GPIO read response gpio_value=16
```
Watching for GPIO changes:
```
meshtastic --port /dev/ttyUSB0 --gpio-watch 0x10 --dest \!28979058
Connected to radio
Watching GPIO mask 0x10 from !28979058
Received RemoteHardware typ=GPIOS_CHANGED, gpio_value=16
Received RemoteHardware typ=GPIOS_CHANGED, gpio_value=0
Received RemoteHardware typ=GPIOS_CHANGED, gpio_value=16
< press ctrl-c to exit >
```

View File

@ -0,0 +1,10 @@
# Running github actions locally
If you'd like to run the **same** integration tests we run on github but on your own machine, you can do the following.
1. Install homebrew per https://brew.sh/
2. Install https://github.com/nektos/act with "brew install act"
3. cd into meshtastic-device and run "act"
4. Select a "medium" sized image
5. Alas - this "act" build **almost** works, but fails because it can't find lib/nanopb/include/pb.h! So someone (you the reader? @geeksville ays hopefully...) needs to fix that before these instructions are complete.
6.

View File

@ -11,7 +11,7 @@
*****************************************************************/
/* Enable support for dynamically allocated fields */
/* #define PB_ENABLE_MALLOC 1 */
#define PB_ENABLE_MALLOC 1
/* Define this if your CPU / compiler combination does not support
* unaligned memory access to packed structures. */
@ -52,7 +52,6 @@
* Feel free to look around and use the defined macros, though. *
******************************************************************/
/* Version of the nanopb library. Just in case you want to check it in
* your own program. */
#define NANOPB_VERSION nanopb - 0.4.4
@ -71,11 +70,11 @@
#ifdef PB_SYSTEM_HEADER
#include PB_SYSTEM_HEADER
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef PB_ENABLE_MALLOC
#include <stdlib.h>
@ -247,8 +246,7 @@ typedef uint_least8_t pb_type_t;
#define PB_ATYPE(x) ((x)&PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x)&PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x)&PB_LTYPE_MASK)
#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \
PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB)
#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB)
/* Data type used for storing sizes of struct fields
* and array counts.
@ -329,7 +327,11 @@ PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
* It has the number of bytes in the beginning, and after that an array.
* Note that actual structs used will have a different length of bytes array.
*/
#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
#define PB_BYTES_ARRAY_T(n) \
struct { \
pb_size_t size; \
pb_byte_t bytes[n]; \
}
#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
struct pb_bytes_array_s {
@ -373,12 +375,7 @@ struct pb_callback_s {
extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
/* Wire types. Library user needs these only in encoder callbacks. */
typedef enum {
PB_WT_VARINT = 0,
PB_WT_64BIT = 1,
PB_WT_STRING = 2,
PB_WT_32BIT = 5
} pb_wire_type_t;
typedef enum { PB_WT_VARINT = 0, PB_WT_64BIT = 1, PB_WT_STRING = 2, PB_WT_32BIT = 5 } pb_wire_type_t;
/* Structure for defining the handling of unknown/extension fields.
* Usually the pb_extension_type_t structure is automatically generated,
@ -395,8 +392,7 @@ struct pb_extension_type_s {
* If you run into an error, return false.
* Set to NULL for default handler.
*/
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
uint32_t tag, pb_wire_type_t wire_type);
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
/* Called once after all regular fields have been encoded.
* If you have something to write, do so and return true.
@ -429,7 +425,10 @@ struct pb_extension_s {
bool found;
};
#define pb_extension_init_zero {NULL,NULL,NULL,false}
#define pb_extension_init_zero \
{ \
NULL, NULL, NULL, false \
}
/* Memory allocation functions to use. You can define pb_realloc and
* pb_free to custom functions if you want. */
@ -458,18 +457,9 @@ struct pb_extension_s {
/* Binding of a message field set into a specific structure */
#define PB_BIND(msgname, structname, width) \
const uint32_t structname ## _field_info[] PB_PROGMEM = \
{ \
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \
0 \
}; \
const pb_msgdesc_t* const structname ## _submsg_info[] = \
{ \
msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \
NULL \
}; \
const pb_msgdesc_t structname ## _msg = \
{ \
const uint32_t structname##_field_info[] PB_PROGMEM = {msgname##_FIELDLIST(PB_GEN_FIELD_INFO_##width, structname) 0}; \
const pb_msgdesc_t *const structname##_submsg_info[] = {msgname##_FIELDLIST(PB_GEN_SUBMSG_INFO, structname) NULL}; \
const pb_msgdesc_t structname##_msg = { \
structname##_field_info, \
structname##_submsg_info, \
msgname##_DEFAULT, \
@ -481,10 +471,8 @@ struct pb_extension_s {
msgname##_FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_##width, structname)
#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1
#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \
+ (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED)
#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \
* 0 + tag
#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +(PB_HTYPE_##htype == PB_HTYPE_REQUIRED)
#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) *0 + tag
/* X-macro for generating the entries in struct_field_info[] array. */
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
@ -516,8 +504,8 @@ struct pb_extension_s {
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_##atype, _PB_HTYPE_##htype, _PB_LTYPE_##ltype), tag, \
PB_ATYPE_##atype | PB_HTYPE_##htype | PB_LTYPE_MAP_##ltype, \
PB_DATA_OFFSET_##atype(_PB_HTYPE_##htype, structname, fieldname), \
PB_DATA_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname), \
PB_SIZE_OFFSET_##atype(_PB_HTYPE_##htype, structname, fieldname), \
@ -561,8 +549,8 @@ struct pb_extension_s {
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_##atype, _PB_HTYPE_##htype, _PB_LTYPE_##ltype), tag, \
PB_ATYPE_##atype | PB_HTYPE_##htype | PB_LTYPE_MAP_##ltype, \
PB_DATA_OFFSET_##atype(_PB_HTYPE_##htype, structname, fieldname), \
PB_DATA_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname), \
PB_SIZE_OFFSET_##atype(_PB_HTYPE_##htype, structname, fieldname), \
@ -589,7 +577,8 @@ struct pb_extension_s {
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB##htype(structname, fieldname)
#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) \
PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname)
#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_##unionname)
#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_##fieldname)
@ -657,9 +646,12 @@ struct pb_extension_s {
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI##ltype(structname##_##fieldname##_MSGTYPE)
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI##ltype(structname##_##fieldname##_MSGTYPE)
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI##ltype(structname##_##fieldname##_MSGTYPE)
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) \
PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) \
PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) \
PB_SI##ltype(structname##_##unionname##_##membername##_MSGTYPE)
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI##ltype(structname##_##fieldname##_MSGTYPE)
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI##ltype(structname##_##fieldname##_MSGTYPE)
#define PB_SI_PB_LTYPE_BOOL(t)
@ -718,18 +710,18 @@ struct pb_extension_s {
(((uint32_t)(size_offset)&0x0F) << 24) | (((uint32_t)(data_size)&0x0F) << 28)),
#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \
(1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \
(1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size)&0xFFF) << 16) | \
(((uint32_t)(size_offset)&0x0F) << 28)), \
(((uint32_t)(data_offset)&0xFFFF) | (((uint32_t)(data_size)&0xFFF) << 16) | (((uint32_t)(tag)&0x3c0) << 22)),
#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \
(2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size)&0xFFFF) << 16)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size),
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), (data_offset), (data_size),
#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \
(3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size), (array_size), 0, 0, 0,
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), (data_offset), (data_size), \
(array_size), 0, 0, 0,
/* These assertions verify that the field information fits in the allocated space.
* The generator tries to automatically determine the correct width that can fit all
@ -740,31 +732,42 @@ struct pb_extension_s {
*/
#define PB_FITS(value, bits) ((uint32_t)(value) < ((uint32_t)1 << bits))
#define PB_FIELDINFO_ASSERT_1(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,6) && PB_FITS(data_offset,8) && PB_FITS(size_offset,4) && PB_FITS(data_size,4) && PB_FITS(array_size,1), FIELDINFO_DOES_NOT_FIT_width1_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 6) && PB_FITS(data_offset, 8) && PB_FITS(size_offset, 4) && PB_FITS(data_size, 4) && \
PB_FITS(array_size, 1), \
FIELDINFO_DOES_NOT_FIT_width1_field##tag)
#define PB_FIELDINFO_ASSERT_2(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,10) && PB_FITS(data_offset,16) && PB_FITS(size_offset,4) && PB_FITS(data_size,12) && PB_FITS(array_size,12), FIELDINFO_DOES_NOT_FIT_width2_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 10) && PB_FITS(data_offset, 16) && PB_FITS(size_offset, 4) && PB_FITS(data_size, 12) && \
PB_FITS(array_size, 12), \
FIELDINFO_DOES_NOT_FIT_width2_field##tag)
#ifndef PB_FIELD_32BIT
/* Maximum field sizes are still 16-bit if pb_size_t is 16-bit */
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 16) && PB_FITS(data_offset, 16) && PB_FITS((int_least8_t)size_offset, 8) && \
PB_FITS(data_size, 16) && PB_FITS(array_size, 16), \
FIELDINFO_DOES_NOT_FIT_width4_field##tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 16) && PB_FITS(data_offset, 16) && PB_FITS((int_least8_t)size_offset, 8) && \
PB_FITS(data_size, 16) && PB_FITS(array_size, 16), \
FIELDINFO_DOES_NOT_FIT_width8_field##tag)
#else
/* Up to 32-bit fields supported.
* Note that the checks are against 31 bits to avoid compiler warnings about shift wider than type in the test.
* I expect that there is no reasonable use for >2GB messages with nanopb anyway.
*/
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 30) && PB_FITS(data_offset, 31) && PB_FITS(size_offset, 8) && PB_FITS(data_size, 31) && \
PB_FITS(array_size, 16), \
FIELDINFO_DOES_NOT_FIT_width4_field##tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
PB_STATIC_ASSERT(PB_FITS(tag, 30) && PB_FITS(data_offset, 31) && PB_FITS(size_offset, 8) && PB_FITS(data_size, 31) && \
PB_FITS(array_size, 31), \
FIELDINFO_DOES_NOT_FIT_width8_field##tag)
#endif
/* Automatic picking of FIELDINFO width:
* Uses width 1 when possible, otherwise resorts to width 2.
* This is used when PB_BIND() is called with "AUTO" as the argument.
@ -865,11 +868,11 @@ struct pb_extension_s {
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
#endif // __cplusplus >= 201703L
namespace nanopb {
namespace nanopb
{
// Each type will be partially specialized by the generator.
template <typename GenMessageT> struct MessageDescriptor;
} // namespace nanopb
#endif /* __cplusplus */
#endif

View File

@ -40,7 +40,7 @@ extra_scripts = bin/platformio-custom.py
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
build_flags = -Wno-missing-field-initializers
-Wno-format
-Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Isrc/buzz -Wl,-Map,.pio/build/output.map
-Isrc -Isrc/mesh -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map
-DHW_VERSION_${sysenv.COUNTRY}
-DHW_VERSION=${sysenv.HW_VERSION}
-DUSE_THREAD_NAMES
@ -68,7 +68,7 @@ lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
https://github.com/meshtastic/arduino-fsm.git#55c47b6cded91645aff05a27b6e5821d8d0f64be
https://github.com/meshtastic/arduino-fsm.git#829e967b8a95c094f73c60ef8dacfe66eae38940
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79
@ -76,6 +76,7 @@ lib_deps =
Wire ; explicitly needed here because the AXP202 library forgets to add it
SPI
https://github.com/geeksville/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
PubSubClient
; Common settings for conventional (non Portduino) Ardino targets
[arduino_base]
@ -110,7 +111,7 @@ lib_deps =
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt
platform_packages =
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#4cde0f5d412d2695184f32e8a47e9bea57b45276
; leave this commented out to avoid breaking Windows
upload_port = /dev/ttyUSB0
@ -202,7 +203,7 @@ build_flags =
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
;-DCFG_DEBUG=3
src_filter =
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<plugins/esp32>
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<plugins/esp32> -<mqtt/>
lib_ignore =
BluetoothOTA
monitor_port = /dev/ttyACM1

2
proto

@ -1 +1 @@
Subproject commit 0ea232802651fd6aaa53c93c09f4c2eb36470dd0
Subproject commit 3252ed0f1357d55233f2d5cb50acf20dbb9160ed

View File

@ -40,7 +40,7 @@ bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2
tParam1 = param1;
tParam2 = param2;
timerAlarmWrite(timer, delayMsec * 1000L, false); // Do not reload, we want it to be a single shot timer
timerAlarmWrite(timer, delayMsec * 1000UL, false); // Do not reload, we want it to be a single shot timer
timerRestart(timer);
timerAlarmEnable(timer);
return true;

View File

@ -303,7 +303,9 @@ void PowerFSM_setup()
#ifndef NRF52_SERIES
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
// I don't think this transition is correct, turning off for now - @geeksville
// powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
#else

View File

@ -40,7 +40,7 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
va_end(arg);
return 0;
};
if (len >= printBufLen) {
if (len >= (int)printBufLen) {
delete[] printBuf;
printBufLen *= 2;
printBuf = new char[printBufLen];

View File

@ -1,6 +1,5 @@
#pragma once
#include "configuration.h"
#include "../freertosinc.h"
namespace concurrency
@ -28,4 +27,4 @@ class BinarySemaphorePosix
#endif
}
} // namespace concurrency

View File

@ -39,7 +39,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
currentQuality = q;
shouldSet = true;
DEBUG_MSG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000L)) {
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
shouldSet = true;
DEBUG_MSG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec);

View File

@ -32,8 +32,9 @@
#include "nimble/BluetoothUtil.h"
#endif
#ifdef PORTDUINO
#if defined(HAS_WIFI) || defined(PORTDUINO)
#include "mesh/wifi/WiFiServerAPI.h"
#include "mqtt/MQTT.h"
#endif
#include "RF95Interface.h"
@ -317,7 +318,7 @@ void setup()
#endif
#ifdef DEBUG_PORT
if (radioConfig.preferences.serial_disabled) {
if (!radioConfig.preferences.serial_disabled) {
consoleInit(); // Set serial baud rate and init our mesh console
}
#endif
@ -541,6 +542,10 @@ void setup()
initApiServer();
#endif
#if defined(PORTDUINO) || defined(HAS_WIFI)
mqttInit();
#endif
// Start airtime logger thread.
airTime = new AirTime();

View File

@ -29,7 +29,6 @@ class Channels
int16_t hashes[MAX_NUM_CHANNELS];
public:
/// Well known channel names
static const char *adminChannel, *gpioChannel, *serialChannel;
@ -43,8 +42,15 @@ class Channels
*/
void setChannel(const Channel &c);
/** Return a human friendly name for this channel (and expand any short strings as needed)
*/
const char *getName(size_t chIndex);
/**
* Return a globally unique channel ID usable with MQTT.
*/
const char *getGlobalId(size_t chIndex) { return getName(chIndex); } // FIXME, not correct
/** The index of the primary channel */
ChannelIndex getPrimaryIndex() const { return primaryIndex; }

View File

@ -70,7 +70,8 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
// DEBUG_MSG("In call plugins\n");
bool pluginFound = false;
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
// We now allow **encrypted** packets to pass through the plugins
bool isDecoded = mp.which_payloadVariant == MeshPacket_decoded_tag;
currentReply = NULL; // No reply yet
@ -82,19 +83,21 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
pi.currentRequest = &mp;
/// received channel
auto ch = channels.getByIndex(mp.channel);
assert(ch.has_settings);
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
bool wantsPacket = (isDecoded || pi.encryptedOk) && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
if (wantsPacket) {
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
pluginFound = true;
/// received channel (or NULL if not decoded)
Channel *ch = isDecoded ? &channels.getByIndex(mp.channel) : NULL;
/// Is the channel this packet arrived on acceptable? (security check)
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
/// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel plugins
bool rxChannelOk = !pi.boundChannel || (ch && ((mp.from == 0) || (strcmp(ch->settings.name, pi.boundChannel) == 0)));
if (!rxChannelOk) {
// no one should have already replied!
@ -123,6 +126,14 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
} else {
DEBUG_MSG("Plugin %s considered\n", pi.name);
}
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
if (pi.myReply) {
DEBUG_MSG("Discarding an unneeded response\n");
packetPool.release(pi.myReply);
pi.myReply = NULL;
}
if (handled) {
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
break;
@ -135,12 +146,16 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
if (mp.decoded.want_response && toUs) {
if (currentReply) {
DEBUG_MSG("Sending response\n");
printPacket("Sending response", currentReply);
service.sendToMesh(currentReply);
currentReply = NULL;
} else {
// No one wanted to reply to this requst, tell the requster that happened
DEBUG_MSG("No one responded, send a nak\n");
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad.
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
}
}
@ -149,6 +164,13 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
}
MeshPacket *MeshPlugin::allocReply()
{
auto r = myReply;
myReply = NULL; // Only use each reply once
return r;
}
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
* is optional
@ -171,7 +193,7 @@ void MeshPlugin::sendResponse(const MeshPacket &req)
void setReplyTo(MeshPacket *p, const MeshPacket &to)
{
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to);
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
p->channel = to.channel; // Use the same channel that the request came in on
// No need for an ack if we are just delivering locally (it just generates an ignored ack)

View File

@ -42,12 +42,16 @@ class MeshPlugin
protected:
const char *name;
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
/** Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
plugins can set this to true and their handleReceived() will be called for every packet.
*/
bool isPromiscuous = false;
/** Most plugins only understand decrypted packets. For plugins that also want to see encrypted packets, they should set this
* flag */
bool encryptedOk = false;
/** If a bound channel name is set, we will only accept received packets that come in on that channel.
* A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
* are allowed on any channel (this lets the local user do anything).
@ -65,6 +69,11 @@ class MeshPlugin
*/
static const MeshPacket *currentRequest;
/**
* If your handler wants to send a response, simply set currentReply and it will be sent at the end of response handling.
*/
MeshPacket *myReply = NULL;
/**
* Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have
* been initialized
@ -83,8 +92,12 @@ class MeshPlugin
virtual bool handleReceived(const MeshPacket &mp) { return false; }
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */
virtual MeshPacket *allocReply() { return NULL; }
* so that subclasses can (optionally) send a response back to the original sender.
*
* Note: most implementers don't need to override this, instead: If while handling a request you have a reply, just set
* the protected reply field in this instance.
* */
virtual MeshPacket *allocReply();
/***
* @return true if you want to be alloced a UI screen frame
@ -102,6 +115,7 @@ class MeshPlugin
* the RoutingPlugin to avoid sending redundant acks
*/
static MeshPacket *currentReply;
friend class ReliableRouter;
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked

View File

@ -14,8 +14,6 @@ typedef uint32_t PacketId; // A packet sequence number
#define ERRNO_NO_INTERFACES 33
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
#define ERRNO_DISABLED 34 // the itnerface is disabled
#define ERRNO_TOO_LARGE 35
#define ERRNO_NO_CHANNEL 36
/**
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.
@ -35,7 +33,7 @@ typedef int ErrorCode;
extern Allocator<MeshPacket> &packetPool;
/**
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node.
* If from is zero this function returns our node number instead
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
* the local node. If from is zero this function returns our node number instead
*/
NodeNum getFrom(const MeshPacket *p);

View File

@ -15,7 +15,10 @@
#error ToRadio is too big
#endif
PhoneAPI::PhoneAPI() {}
PhoneAPI::PhoneAPI()
{
lastContactMsec = millis();
}
PhoneAPI::~PhoneAPI()
{
@ -53,11 +56,14 @@ void PhoneAPI::close()
void PhoneAPI::checkConnectionTimeout()
{
if (isConnected()) {
bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L);
if (!newConnected)
uint32_t now = millis();
bool newContact = (now - lastContactMsec) < getPref_phone_timeout_secs() * 1000UL;
if (!newContact) {
DEBUG_MSG("Timed out on phone contact, dropping phone connection\n");
close();
}
}
}
/**
* Handle a ToRadio protobuf

View File

@ -22,7 +22,6 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
}
protected:
/**
* Handle a received message, the data field in the message is already decoded and is provided
*
@ -58,11 +57,12 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
// it would be better to update even if the message was destined to others.
auto &p = mp.decoded;
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size);
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum,
p.payload.size);
T scratch;
T *decoded = NULL;
if(mp.decoded.portnum == ourPortNum) {
if (mp.which_payloadVariant == MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) {
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
decoded = &scratch;

View File

@ -36,8 +36,6 @@ bool RF95Interface::init()
{
RadioLibInterface::init();
applyModemConfig();
if (power == 0)
power = POWER_DEFAULT;
@ -86,24 +84,25 @@ void INTERRUPT_ATTR RF95Interface::disableInterrupt()
lora->clearDio0Action();
}
bool RF95Interface::reconfigure()
{
applyModemConfig();
RadioLibInterface::reconfigure();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora->setSpreadingFactor(sf);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
err = lora->setBandwidth(bw);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
err = lora->setCodingRate(cr);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
err = lora->setSyncWord(syncWord);
assert(err == ERR_NONE);
@ -115,12 +114,14 @@ bool RF95Interface::reconfigure()
assert(err == ERR_NONE);
err = lora->setFrequency(freq);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
err = lora->setOutputPower(power);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
startReceive(); // restart receiving

View File

@ -1,12 +1,12 @@
#include "configuration.h"
#include "RadioInterface.h"
#include "Channels.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "assert.h"
#include "Router.h"
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include <assert.h>
#include <pb_decode.h>
@ -31,8 +31,8 @@ const RegionInfo regions[] = {
/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2):
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision 18-46-03-1 (September 11th 2018) https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1
We have 3 options for 868 MHz:
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision 18-46-03-1 (September 11th 2018)
https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1 We have 3 options for 868 MHz:
864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports.
866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports
@ -112,6 +112,8 @@ uint32_t RadioInterface::getPacketTime(MeshPacket *p)
/** The delay to use for retransmitting dropped packets */
uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p)
{
assert(shortPacketMsec); // Better be non zero
// was 20 and 22 secs respectively, but now with shortPacketMsec as 2269, this should give the same range
return random(9 * shortPacketMsec, 10 * shortPacketMsec);
}
@ -185,6 +187,12 @@ RadioInterface::RadioInterface()
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
}
bool RadioInterface::reconfigure()
{
applyModemConfig();
return true;
}
bool RadioInterface::init()
{
DEBUG_MSG("Starting meshradio init...\n");
@ -197,6 +205,8 @@ bool RadioInterface::init()
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
// time.
applyModemConfig();
return true;
}
@ -309,7 +319,7 @@ void RadioInterface::applyModemConfig()
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
const char *channelName = channels.getName(channels.getPrimaryIndex());
int channel_num = channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName) % myRegion->numChannels;
freq = myRegion->freq + myRegion->spacing * channel_num;
freq = myRegion->freq + radioConfig.preferences.frequency_offset + myRegion->spacing * channel_num;
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power);
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
@ -351,12 +361,8 @@ ErrorCode SimRadio::send(MeshPacket *p)
void RadioInterface::deliverToReceiver(MeshPacket *p)
{
assert(rxDest);
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
if (router)
router->setReceivedMessage();
router->enqueueReceivedMessage(p);
}
/***

View File

@ -42,7 +42,6 @@ typedef struct {
class RadioInterface
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;
CallbackObserver<RadioInterface, void *> configChangedObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::reloadConfig);
@ -82,17 +81,11 @@ class RadioInterface
float freq = 915.0;
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
RadioInterface();
virtual ~RadioInterface() {}
/**
* Set where to deliver received packets. This method should only be used by the Router class
*/
void setReceiver(PointerQueue<MeshPacket> *_rxDest) { rxDest = _rxDest; }
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
@ -104,7 +97,11 @@ class RadioInterface
virtual bool sleep() { return true; }
/// Disable this interface (while disabled, no packets can be sent or received)
void disable() { disabled = true; sleep(); }
void disable()
{
disabled = true;
sleep();
}
/**
* Send a packet (possibly by enquing in a private fifo). This routine will
@ -126,7 +123,7 @@ class RadioInterface
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() = 0;
virtual bool reconfigure();
/** The delay to use for retransmitting dropped packets */
uint32_t getRetransmissionMsec(const MeshPacket *p);
@ -174,13 +171,6 @@ class RadioInterface
*/
void limitPower();
/**
* Convert our modemConfig enum into wf, sf, etc...
*
* These paramaters will be pull from the channelSettings global
*/
virtual void applyModemConfig();
/**
* Save the frequency we selected for later reuse.
*/
@ -192,6 +182,13 @@ class RadioInterface
virtual void saveChannelNum(uint32_t savedChannelNum);
private:
/**
* Convert our modemConfig enum into wf, sf, etc...
*
* These paramaters will be pull from the channelSettings global
*/
void applyModemConfig();
/// Return 0 if sleep is okay
int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; }
@ -208,18 +205,6 @@ class SimRadio : public RadioInterface
{
public:
virtual ErrorCode send(MeshPacket *p);
// methods from radiohead
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() { return true; }
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() { return true; }
};
/// Debug printing for packets

View File

@ -8,6 +8,10 @@
#include "mesh-pb-constants.h"
#include "plugins/RoutingPlugin.h"
#if defined(HAS_WIFI) || defined(PORTDUINO)
#include "mqtt/MQTT.h"
#endif
/**
* Router todo
*
@ -64,6 +68,22 @@ int32_t Router::runOnce()
return INT32_MAX; // Wait a long time - until we get woken for the message queue
}
/**
* RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for
* freeing the packet
*/
void Router::enqueueReceivedMessage(MeshPacket *p)
{
if (fromRadioQueue.enqueue(p, 0)) { // NOWAIT - fixme, if queue is full, delete older messages
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
setReceivedMessage();
} else {
printPacket("BUG! fromRadioQueue is full! Discarding!", p);
packetPool.release(p);
}
}
/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId()
@ -129,13 +149,8 @@ ErrorCode Router::sendLocal(MeshPacket *p)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
if (fromRadioQueue.enqueue(p, 0)) {
printPacket("Enqueued local", p);
setReceivedMessage();
} else {
printPacket("BUG! fromRadioQueue is full! Discarding!", p);
packetPool.release(p);
}
enqueueReceivedMessage(p);
return ERRNO_OK;
} else if (!iface) {
// We must be sending to remote nodes also, fail if no interface found
@ -187,36 +202,20 @@ ErrorCode Router::send(MeshPacket *p)
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
p->which_payloadVariant == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
// First convert from protobufs to raw bytes
// If the packet is not yet encrypted, do so now
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
// printPacket("pre encrypt", p); // portnum valid here
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
if (numbytes > MAX_RHPACKETLEN) {
abortSendAndNak(Routing_Error_TOO_LARGE, p);
return ERRNO_TOO_LARGE;
auto encodeResult = perhapsEncode(p);
if (encodeResult != Routing_Error_NONE) {
abortSendAndNak(encodeResult, p);
return encodeResult; // FIXME - this isn't a valid ErrorCode
}
// printBytes("plaintext", bytes, numbytes);
auto hash = channels.setActiveByIndex(p->channel);
if (hash < 0) {
// No suitable channel could be found for sending
abortSendAndNak(Routing_Error_NO_CHANNEL, p);
return ERRNO_NO_CHANNEL;
}
// 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);
// Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes);
p->encrypted.size = numbytes;
p->which_payloadVariant = MeshPacket_encrypted_tag;
#if defined(HAS_WIFI) || defined(PORTDUINO)
if (mqtt)
mqtt->onSend(*p, chIndex);
#endif
}
assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
@ -239,7 +238,7 @@ void Router::sniffReceived(const MeshPacket *p, const Routing *c)
// FIXME, update nodedb here for any packet that passes through us
}
bool Router::perhapsDecode(MeshPacket *p)
bool perhapsDecode(MeshPacket *p)
{
if (p->which_payloadVariant == MeshPacket_decoded_tag)
return true; // If packet was already decoded just return
@ -280,6 +279,42 @@ bool Router::perhapsDecode(MeshPacket *p)
return false;
}
/** Return 0 for success or a Routing_Errror code for failure
*/
Routing_Error perhapsEncode(MeshPacket *p)
{
// If the packet is not yet encrypted, do so now
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
// printPacket("pre encrypt", p); // portnum valid here
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
if (numbytes > MAX_RHPACKETLEN)
return Routing_Error_TOO_LARGE;
// 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 Routing_Error_NO_CHANNEL;
// 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);
// Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes);
p->encrypted.size = numbytes;
p->which_payloadVariant = MeshPacket_encrypted_tag;
}
return Routing_Error_NONE;
}
NodeNum Router::getNodeNum()
{
return nodeDB.getNodeNum();
@ -299,13 +334,12 @@ void Router::handleReceived(MeshPacket *p)
if (decoded) {
// parsing was successful, queue for our recipient
printPacket("handleReceived", p);
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
// sniffReceived(p);
MeshPlugin::callPlugins(*p);
} else {
DEBUG_MSG("packet decoding failed\n");
printPacket("packet decoding failed (no PSK?)", p);
}
// call plugins here
MeshPlugin::callPlugins(*p);
}
void Router::perhapsHandleReceived(MeshPacket *p)

View File

@ -1,12 +1,12 @@
#pragma once
#include "Channels.h"
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "concurrency/OSThread.h"
#include "Channels.h"
/**
* A mesh aware router that supports multiple interfaces.
@ -22,7 +22,6 @@ class Router : protected concurrency::OSThread
RadioInterface *iface = NULL;
public:
/**
* Constructor
*
@ -32,11 +31,7 @@ class Router : protected concurrency::OSThread
/**
* Currently we only allow one interface, that may change in the future
*/
void addInterface(RadioInterface *_iface)
{
iface = _iface;
iface->setReceiver(&fromRadioQueue);
}
void addInterface(RadioInterface *_iface) { iface = _iface; }
/**
* do idle processing
@ -69,6 +64,12 @@ class Router : protected concurrency::OSThread
*/
void setReceivedMessage();
/**
* RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for
* freeing the packet
*/
void enqueueReceivedMessage(MeshPacket *p);
protected:
friend class RoutingPlugin;
@ -97,13 +98,6 @@ class Router : protected concurrency::OSThread
*/
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
/**
* Remove any encryption and decode the protobufs inside this packet (if necessary).
*
* @return true for success, false for corrupt packet.
*/
bool perhapsDecode(MeshPacket *p);
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
@ -134,6 +128,17 @@ class Router : protected concurrency::OSThread
void abortSendAndNak(Routing_Error err, MeshPacket *p);
};
/** FIXME - move this into a mesh packet class
* Remove any encryption and decode the protobufs inside this packet (if necessary).
*
* @return true for success, false for corrupt packet.
*/
bool perhapsDecode(MeshPacket *p);
/** Return 0 for success or a Routing_Errror code for failure
*/
Routing_Error perhapsEncode(MeshPacket *p);
extern Router *router;
/// Generate a unique packet id

View File

@ -23,8 +23,6 @@ bool SX1262Interface::init()
pinMode(SX1262_POWER_EN, OUTPUT);
#endif
RadioLibInterface::init();
#ifdef SX1262_RXEN // set not rx or tx mode
digitalWrite(SX1262_RXEN, LOW); // Set low before becoming an output
pinMode(SX1262_RXEN, OUTPUT);
@ -42,7 +40,7 @@ bool SX1262Interface::init()
#endif
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
applyModemConfig();
RadioLibInterface::init();
if (power == 0)
power = SX1262_MAX_POWER;
@ -72,20 +70,23 @@ bool SX1262Interface::init()
bool SX1262Interface::reconfigure()
{
applyModemConfig();
RadioLibInterface::reconfigure();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora.setSpreadingFactor(sf);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
err = lora.setBandwidth(bw);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
err = lora.setCodingRate(cr);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
err = lora.setRxGain(true);
@ -101,7 +102,8 @@ bool SX1262Interface::reconfigure()
assert(err == ERR_NONE);
err = lora.setFrequency(freq);
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (err != ERR_NONE)
recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
if (power > 22) // This chip has lower power limits than some
power = 22;

View File

@ -79,7 +79,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */
#define AdminMessage_size 354
#define AdminMessage_size 397
#ifdef __cplusplus
} /* extern "C" */

View File

@ -6,9 +6,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(ServiceEnvelope, ServiceEnvelope, 2)
PB_BIND(ChannelSet, ChannelSet, AUTO)

View File

@ -4,7 +4,6 @@
#ifndef PB_APPONLY_PB_H_INCLUDED
#define PB_APPONLY_PB_H_INCLUDED
#include <pb.h>
#include "mesh.pb.h"
#include "channel.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@ -16,54 +15,31 @@ typedef struct _ChannelSet {
pb_callback_t settings;
} ChannelSet;
typedef struct _ServiceEnvelope {
bool has_packet;
MeshPacket packet;
pb_callback_t channel_id;
pb_callback_t gateway_id;
} ServiceEnvelope;
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define ServiceEnvelope_init_default {false, MeshPacket_init_default, {{NULL}, NULL}, {{NULL}, NULL}}
#define ChannelSet_init_default {{{NULL}, NULL}}
#define ServiceEnvelope_init_zero {false, MeshPacket_init_zero, {{NULL}, NULL}, {{NULL}, NULL}}
#define ChannelSet_init_zero {{{NULL}, NULL}}
/* Field tags (for use in manual encoding/decoding) */
#define ChannelSet_settings_tag 1
#define ServiceEnvelope_packet_tag 1
#define ServiceEnvelope_channel_id_tag 2
#define ServiceEnvelope_gateway_id_tag 3
/* Struct field encoding specification for nanopb */
#define ServiceEnvelope_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, packet, 1) \
X(a, CALLBACK, SINGULAR, STRING, channel_id, 2) \
X(a, CALLBACK, SINGULAR, STRING, gateway_id, 3)
#define ServiceEnvelope_CALLBACK pb_default_field_callback
#define ServiceEnvelope_DEFAULT NULL
#define ServiceEnvelope_packet_MSGTYPE MeshPacket
#define ChannelSet_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, MESSAGE, settings, 1)
#define ChannelSet_CALLBACK pb_default_field_callback
#define ChannelSet_DEFAULT NULL
#define ChannelSet_settings_MSGTYPE ChannelSettings
extern const pb_msgdesc_t ServiceEnvelope_msg;
extern const pb_msgdesc_t ChannelSet_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define ServiceEnvelope_fields &ServiceEnvelope_msg
#define ChannelSet_fields &ChannelSet_msg
/* Maximum encoded size of messages (where known) */
/* ServiceEnvelope_size depends on runtime parameters */
/* ChannelSet_size depends on runtime parameters */
#ifdef __cplusplus

View File

@ -0,0 +1,12 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.4 */
#include "mqtt.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(ServiceEnvelope, ServiceEnvelope, AUTO)

View File

@ -0,0 +1,55 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.4 */
#ifndef PB_MQTT_PB_H_INCLUDED
#define PB_MQTT_PB_H_INCLUDED
#include <pb.h>
#include "mesh.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Struct definitions */
typedef struct _ServiceEnvelope {
struct _MeshPacket *packet;
char *channel_id;
char *gateway_id;
} ServiceEnvelope;
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define ServiceEnvelope_init_default {NULL, NULL, NULL}
#define ServiceEnvelope_init_zero {NULL, NULL, NULL}
/* Field tags (for use in manual encoding/decoding) */
#define ServiceEnvelope_packet_tag 1
#define ServiceEnvelope_channel_id_tag 2
#define ServiceEnvelope_gateway_id_tag 3
/* Struct field encoding specification for nanopb */
#define ServiceEnvelope_FIELDLIST(X, a) \
X(a, POINTER, OPTIONAL, MESSAGE, packet, 1) \
X(a, POINTER, SINGULAR, STRING, channel_id, 2) \
X(a, POINTER, SINGULAR, STRING, gateway_id, 3)
#define ServiceEnvelope_CALLBACK NULL
#define ServiceEnvelope_DEFAULT NULL
#define ServiceEnvelope_packet_MSGTYPE MeshPacket
extern const pb_msgdesc_t ServiceEnvelope_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define ServiceEnvelope_fields &ServiceEnvelope_msg
/* Maximum encoded size of messages (where known) */
/* ServiceEnvelope_size depends on runtime parameters */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -86,6 +86,9 @@ typedef struct _RadioConfig_UserPreferences {
bool is_low_power;
bool fixed_position;
bool serial_disabled;
float frequency_offset;
char mqtt_server[32];
bool mqtt_disabled;
bool factory_reset;
bool debug_log_enabled;
pb_size_t ignore_incoming_count;
@ -151,9 +154,9 @@ extern "C" {
/* Initializer values for message structs */
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
@ -179,6 +182,9 @@ extern "C" {
#define RadioConfig_UserPreferences_is_low_power_tag 38
#define RadioConfig_UserPreferences_fixed_position_tag 39
#define RadioConfig_UserPreferences_serial_disabled_tag 40
#define RadioConfig_UserPreferences_frequency_offset_tag 41
#define RadioConfig_UserPreferences_mqtt_server_tag 42
#define RadioConfig_UserPreferences_mqtt_disabled_tag 43
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
@ -240,6 +246,9 @@ X(a, STATIC, SINGULAR, BOOL, is_router, 37) \
X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
X(a, STATIC, SINGULAR, BOOL, serial_disabled, 40) \
X(a, STATIC, SINGULAR, FLOAT, frequency_offset, 41) \
X(a, STATIC, SINGULAR, STRING, mqtt_server, 42) \
X(a, STATIC, SINGULAR, BOOL, mqtt_disabled, 43) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
@ -279,8 +288,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
/* Maximum encoded size of messages (where known) */
#define RadioConfig_size 351
#define RadioConfig_UserPreferences_size 348
#define RadioConfig_size 394
#define RadioConfig_UserPreferences_size 391
#ifdef __cplusplus
} /* extern "C" */

167
src/mqtt/MQTT.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "MQTT.h"
#include "NodeDB.h"
#include "main.h"
#include "mesh/Channels.h"
#include "mesh/Router.h"
#include "mesh/generated/mqtt.pb.h"
#include <WiFi.h>
#include <assert.h>
MQTT *mqtt;
String statusTopic = "mesh/stat/";
String cryptTopic = "mesh/crypt/"; // mesh/crypt/CHANNELID/NODEID
void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
{
mqtt->onPublish(topic, payload, length);
}
void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
{
// parsing ServiceEnvelope
ServiceEnvelope e = ServiceEnvelope_init_default;
if (!pb_decode_from_bytes(payload, length, ServiceEnvelope_fields, &e)) {
DEBUG_MSG("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length);
} else {
DEBUG_MSG("Received MQTT topic %s, len=%u\n", topic, length);
if (e.packet) {
MeshPacket *p = packetPool.allocCopy(*e.packet);
// ignore messages sent by us or if we don't have the channel key
if (router && p->from != nodeDB.getNodeNum() && perhapsDecode(p))
router->enqueueReceivedMessage(p);
else
packetPool.release(p);
}
// make sure to free both strings and the MeshPacket (passing in NULL is acceptable)
free(e.channel_id);
free(e.gateway_id);
free(e.packet);
}
}
void mqttInit()
{
new MQTT();
}
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient)
{
assert(!mqtt);
mqtt = this;
pubSub.setCallback(mqttCallback);
}
void MQTT::reconnect()
{
// pubSub.setServer("devsrv.ezdevice.net", 1883); or 192.168.10.188
const char *serverAddr = "test.mosquitto.org"; // "mqtt.meshtastic.org"; // default hostname
if (*radioConfig.preferences.mqtt_server)
serverAddr = radioConfig.preferences.mqtt_server; // Override the default
pubSub.setServer(serverAddr, 1883);
DEBUG_MSG("Connecting to MQTT server\n", serverAddr);
auto myStatus = (statusTopic + owner.id);
// bool connected = pubSub.connect(nodeId.c_str(), "meshdev", "apes4cats", myStatus.c_str(), 1, true, "offline");
bool connected = pubSub.connect(owner.id, myStatus.c_str(), 1, true, "offline");
if (connected) {
DEBUG_MSG("MQTT connected\n");
enabled = true; // Start running background process again
runASAP = true;
/// FIXME, include more information in the status text
bool ok = pubSub.publish(myStatus.c_str(), "online", true);
DEBUG_MSG("published %d\n", ok);
sendSubscriptions();
} else
DEBUG_MSG("Failed to contact MQTT server...\n");
}
void MQTT::sendSubscriptions()
{
size_t numChan = channels.getNumChannels();
for (size_t i = 0; i < numChan; i++) {
auto &ch = channels.getByIndex(i);
if (ch.settings.uplink_enabled) {
String topic = cryptTopic + channels.getGlobalId(i) + "/#";
DEBUG_MSG("Subscribing to %s\n", topic.c_str());
pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right?
}
}
}
bool MQTT::wantsLink() const
{
bool hasChannel = false;
if (radioConfig.preferences.mqtt_disabled) {
// DEBUG_MSG("MQTT disabled...\n");
} else {
// No need for link if no channel needed it
size_t numChan = channels.getNumChannels();
for (size_t i = 0; i < numChan; i++) {
auto &ch = channels.getByIndex(i);
if (ch.settings.uplink_enabled || ch.settings.downlink_enabled) {
hasChannel = true;
break;
}
}
}
return hasChannel && WiFi.isConnected();
}
int32_t MQTT::runOnce()
{
bool wantConnection = wantsLink();
// If connected poll rapidly, otherwise only occasionally check for a wifi connection change and ability to contact server
if (!pubSub.loop()) {
if (wantConnection) {
reconnect();
// If we succeeded, start reading rapidly, else try again in 30 seconds (TCP connections are EXPENSIVE so try rarely)
return pubSub.connected() ? 20 : 30000;
} else
return 5000; // If we don't want connection now, check again in 5 secs
} else {
// we are connected to server, check often for new requests on the TCP port
if (!wantConnection) {
DEBUG_MSG("MQTT link not needed, dropping\n");
pubSub.disconnect();
}
return 20;
}
}
void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex)
{
auto &ch = channels.getByIndex(chIndex);
// don't bother sending if not connected...
if (pubSub.connected() && ch.settings.uplink_enabled) {
const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel
ServiceEnvelope env = ServiceEnvelope_init_default;
env.channel_id = (char *)channelId;
env.gateway_id = owner.id;
env.packet = (MeshPacket *)&mp;
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
static uint8_t bytes[MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, &env);
String topic = cryptTopic + channelId + "/" + owner.id;
DEBUG_MSG("publish %s, %u bytes\n", topic.c_str(), numBytes);
pubSub.publish(topic.c_str(), bytes, numBytes, false);
}
}

60
src/mqtt/MQTT.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include "configuration.h"
#include "concurrency/OSThread.h"
#include "mesh/Channels.h"
#include <PubSubClient.h>
#include <WiFiClient.h>
/**
* Our wrapper/singleton for sending/receiving MQTT "udp" packets. This object isolates the MQTT protocol implementation from
* the two components that use it: MQTTPlugin and MQTTSimInterface.
*/
class MQTT : private concurrency::OSThread
{
// supposedly the current version is busted:
// http://www.iotsharing.com/2017/08/how-to-use-esp32-mqtts-with-mqtts-mosquitto-broker-tls-ssl.html
// WiFiClientSecure wifiClient;
WiFiClient mqttClient;
PubSubClient pubSub;
public:
MQTT();
/**
* Publish a packet on the glboal MQTT server.
* This hook must be called **after** the packet is encrypted (including the channel being changed to a hash).
* @param chIndex the index of the channel for this message
*
* Note: for messages we are forwarding on the mesh that we can't find the channel for (because we don't have the keys), we
* can not forward those messages to the cloud - becuase no way to find a global channel ID.
*/
void onSend(const MeshPacket &mp, ChannelIndex chIndex);
protected:
virtual int32_t runOnce();
private:
/** return true if we have a channel that wants uplink/downlink
*/
bool wantsLink() const;
/** Attempt to connect to server if necessary
*/
void reconnect();
/** Tell the server what subscriptions we want (based on channels.downlink_enabled)
*/
void sendSubscriptions();
/// Just C glue to call onPublish
static void mqttCallback(char *topic, byte *payload, unsigned int length);
/// Called when a new publish arrives from the MQTT server
void onPublish(char *topic, byte *payload, unsigned int length);
};
void mqttInit();
extern MQTT *mqtt;

View File

@ -19,7 +19,7 @@ void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
AdminMessage r = AdminMessage_init_default;
r.get_channel_response = channels.getByIndex(channelIndex);
r.which_variant = AdminMessage_get_channel_response_tag;
reply = allocDataProtobuf(r);
myReply = allocDataProtobuf(r);
}
}
@ -36,7 +36,7 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req)
r.get_radio_response.preferences.ls_secs = getPref_ls_secs();
r.which_variant = AdminMessage_get_radio_response_tag;
reply = allocDataProtobuf(r);
myReply = allocDataProtobuf(r);
}
}
@ -56,8 +56,8 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
case AdminMessage_set_channel_tag:
DEBUG_MSG("Client is setting channel %d\n", r->set_channel.index);
if (r->set_channel.index < 0 || r->set_channel.index >= MAX_NUM_CHANNELS)
reply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp);
if (r->set_channel.index < 0 || r->set_channel.index >= (int)MAX_NUM_CHANNELS)
myReply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp);
else
handleSetChannel(r->set_channel);
break;
@ -66,7 +66,7 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
uint32_t i = r->get_channel_request - 1;
DEBUG_MSG("Client is getting channel %u\n", i);
if (i >= MAX_NUM_CHANNELS)
reply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp);
myReply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp);
else
handleGetChannel(mp, i);
break;
@ -141,13 +141,6 @@ void AdminPlugin::handleSetRadio(const RadioConfig &r)
service.reloadConfig();
}
MeshPacket *AdminPlugin::allocReply()
{
auto r = reply;
reply = NULL; // Only use each reply once
return r;
}
AdminPlugin::AdminPlugin() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
{
// restrict to the admin channel for rx

View File

@ -6,8 +6,6 @@
*/
class AdminPlugin : public ProtobufPlugin<AdminMessage>
{
MeshPacket *reply = NULL;
public:
/** Constructor
* name is for debugging output
@ -21,10 +19,6 @@ class AdminPlugin : public ProtobufPlugin<AdminMessage>
*/
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const AdminMessage *p);
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */
virtual MeshPacket *allocReply();
private:
void handleSetOwner(const User &o);
void handleSetChannel(const Channel &cc);

View File

@ -15,7 +15,8 @@
#define WATCH_INTERVAL_MSEC (30 * 1000)
/// Set pin modes for every set bit in a mask
static void pinModes(uint64_t mask, uint8_t mode) {
static void pinModes(uint64_t mask, uint8_t mode)
{
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
if (mask & (1 << i)) {
pinMode(i, mode);
@ -24,7 +25,8 @@ static void pinModes(uint64_t mask, uint8_t mode) {
}
/// Read all the pins mentioned in a mask
static uint64_t digitalReads(uint64_t mask) {
static uint64_t digitalReads(uint64_t mask)
{
uint64_t res = 0;
pinModes(mask, INPUT_PULLUP);
@ -40,10 +42,9 @@ static uint64_t digitalReads(uint64_t mask) {
return res;
}
RemoteHardwarePlugin::RemoteHardwarePlugin()
: ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields),
concurrency::OSThread("remotehardware")
: ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields), concurrency::OSThread(
"remotehardware")
{
}
@ -75,12 +76,12 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
uint64_t res = digitalReads(p.gpio_mask);
// Send the reply
HardwareMessage reply = HardwareMessage_init_default;
reply.typ = HardwareMessage_Type_READ_GPIOS_REPLY;
reply.gpio_value = res;
MeshPacket *p = allocDataProtobuf(reply);
HardwareMessage r = HardwareMessage_init_default;
r.typ = HardwareMessage_Type_READ_GPIOS_REPLY;
r.gpio_value = res;
MeshPacket *p = allocDataProtobuf(r);
setReplyTo(p, req);
service.sendToMesh(p);
myReply = p;
break;
}
@ -102,10 +103,11 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
break;
}
return true; // handled
return false;
}
int32_t RemoteHardwarePlugin::runOnce() {
int32_t RemoteHardwarePlugin::runOnce()
{
if (watchGpios) {
uint32_t now = millis();
@ -117,15 +119,14 @@ int32_t RemoteHardwarePlugin::runOnce() {
DEBUG_MSG("Broadcasting GPIOS 0x%llx changed!\n", curVal);
// Something changed! Tell the world with a broadcast message
HardwareMessage reply = HardwareMessage_init_default;
reply.typ = HardwareMessage_Type_GPIOS_CHANGED;
reply.gpio_value = curVal;
MeshPacket *p = allocDataProtobuf(reply);
HardwareMessage r = HardwareMessage_init_default;
r.typ = HardwareMessage_Type_GPIOS_CHANGED;
r.gpio_value = curVal;
MeshPacket *p = allocDataProtobuf(r);
service.sendToMesh(p);
}
}
}
else {
} else {
// No longer watching anything - stop using CPU
enabled = false;
}

View File

@ -1,37 +1,21 @@
#include "CryptoEngine.h"
#include "target_specific.h"
#include "PortduinoGPIO.h"
#include "mesh/RF95Interface.h"
#include "sleep.h"
#include "target_specific.h"
#include <Utility.h>
#include <assert.h>
// FIXME - move getMacAddr/setBluetoothEnable into a HALPlatform class
uint32_t hwId; // fixme move into portduino
void getMacAddr(uint8_t *dmac)
{
if (!hwId) {
notImplemented("getMacAddr");
hwId = random();
}
dmac[0] = 0x80;
dmac[1] = 0;
dmac[2] = 0;
dmac[3] = hwId >> 16;
dmac[4] = hwId >> 8;
dmac[5] = hwId & 0xff;
}
// FIXME - move setBluetoothEnable into a HALPlatform class
void setBluetoothEnable(bool on)
{
// not needed
}
void cpuDeepSleep(uint64_t msecs) {
void cpuDeepSleep(uint64_t msecs)
{
notImplemented("cpuDeepSleep");
}
@ -60,7 +44,8 @@ void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel");
*
* Porduino helper class to do this i2c based polling:
*/
class R595PolledIrqPin : public GPIOPin {
class R595PolledIrqPin : public GPIOPin
{
public:
R595PolledIrqPin() : GPIOPin(LORA_DIO0, "LORA_DIO0") {}
@ -81,12 +66,12 @@ public:
}
};
/** apps run under portduino can optionally define a portduinoSetup() to
* use portduino specific init code (such as gpioBind) to setup portduino on their host machine,
* before running 'arduino' code.
*/
void portduinoSetup() {
void portduinoSetup()
{
printf("Setting up Meshtastic on Porduino...\n");
// FIXME: disable while not testing with real hardware

View File

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