mirror of
https://github.com/meshtastic/firmware.git
synced 2025-05-05 13:23:11 +00:00
commit
a5e3f271ea
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@ -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()'
|
||||
|
||||
|
@ -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
3
bin/mqtt-listen.sh
Executable 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
1
bin/mqtt-send-status.sh
Executable file
@ -0,0 +1 @@
|
||||
mosquitto_pub -h test.mosquitto.org -t mesh/stat/FakeNode -m online -d
|
@ -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 "$@"
|
||||
|
@ -1,3 +1,3 @@
|
||||
set -e
|
||||
pio run --environment native
|
||||
.pio/build/native/program
|
||||
.pio/build/native/program "$@"
|
||||
|
@ -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
|
||||
wait
|
||||
|
@ -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
|
||||
|
@ -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 ;-):
|
||||
|
@ -12,14 +12,18 @@
|
||||
- [NODEID](#nodeid)
|
||||
- [USERID](#userid)
|
||||
- [CHANNELID](#channelid)
|
||||
- [PORTID](#portid)
|
||||
- [Gateway nodes](#gateway-nodes)
|
||||
- [Optional web services](#optional-web-services)
|
||||
- [Public MQTT broker service](#public-mqtt-broker-service)
|
||||
- [Riot.im messaging bridge](#riotim-messaging-bridge)
|
||||
- [Deprecated concepts](#deprecated-concepts)
|
||||
- [MESHID (deprecated)](#meshid-deprecated)
|
||||
- [DESTCLASS (deprecated)](#destclass-deprecated)
|
||||
- [DESTID (deprecated)](#destid-deprecated)
|
||||
- [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)
|
||||
- [DESTCLASS (deprecated)](#destclass-deprecated)
|
||||
- [DESTID (deprecated)](#destid-deprecated)
|
||||
- [Rejected idea: RAW UDP](#rejected-idea-raw-udp)
|
||||
- [Development plan](#development-plan)
|
||||
- [Work items](#work-items)
|
||||
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 >
|
||||
```
|
10
docs/software/running-github-actions.md
Normal file
10
docs/software/running-github-actions.md
Normal 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.
|
@ -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,10 +52,9 @@
|
||||
* 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
|
||||
#define NANOPB_VERSION nanopb - 0.4.4
|
||||
|
||||
/* Include all the system headers needed by nanopb. You will need the
|
||||
* definitions of the following:
|
||||
@ -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>
|
||||
@ -90,30 +89,30 @@ extern "C" {
|
||||
* This just reduces memory requirements, but is not required.
|
||||
*/
|
||||
#if defined(PB_NO_PACKED_STRUCTS)
|
||||
/* Disable struct packing */
|
||||
# define PB_PACKED_STRUCT_START
|
||||
# define PB_PACKED_STRUCT_END
|
||||
# define pb_packed
|
||||
/* Disable struct packing */
|
||||
#define PB_PACKED_STRUCT_START
|
||||
#define PB_PACKED_STRUCT_END
|
||||
#define pb_packed
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
/* For GCC and clang */
|
||||
# define PB_PACKED_STRUCT_START
|
||||
# define PB_PACKED_STRUCT_END
|
||||
# define pb_packed __attribute__((packed))
|
||||
/* For GCC and clang */
|
||||
#define PB_PACKED_STRUCT_START
|
||||
#define PB_PACKED_STRUCT_END
|
||||
#define pb_packed __attribute__((packed))
|
||||
#elif defined(__ICCARM__) || defined(__CC_ARM)
|
||||
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
|
||||
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
|
||||
# define pb_packed
|
||||
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||
#define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
|
||||
#define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
|
||||
#define pb_packed
|
||||
#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||
/* For Microsoft Visual C++ */
|
||||
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
|
||||
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
|
||||
# define pb_packed
|
||||
/* For Microsoft Visual C++ */
|
||||
#define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
|
||||
#define PB_PACKED_STRUCT_END __pragma(pack(pop))
|
||||
#define pb_packed
|
||||
#else
|
||||
/* Unknown compiler */
|
||||
# define PB_PACKED_STRUCT_START
|
||||
# define PB_PACKED_STRUCT_END
|
||||
# define pb_packed
|
||||
/* Unknown compiler */
|
||||
#define PB_PACKED_STRUCT_START
|
||||
#define PB_PACKED_STRUCT_END
|
||||
#define pb_packed
|
||||
#endif
|
||||
|
||||
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
|
||||
@ -126,11 +125,11 @@ extern "C" {
|
||||
#ifndef PB_PROGMEM
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#define PB_PROGMEM PROGMEM
|
||||
#define PB_PROGMEM_READU32(x) pgm_read_dword(&x)
|
||||
#define PB_PROGMEM PROGMEM
|
||||
#define PB_PROGMEM_READU32(x) pgm_read_dword(&x)
|
||||
#else
|
||||
#define PB_PROGMEM
|
||||
#define PB_PROGMEM_READU32(x) (x)
|
||||
#define PB_PROGMEM_READU32(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -144,20 +143,20 @@ extern "C" {
|
||||
* in the place where the PB_STATIC_ASSERT macro was called.
|
||||
*/
|
||||
#ifndef PB_NO_STATIC_ASSERT
|
||||
# ifndef PB_STATIC_ASSERT
|
||||
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
/* C11 standard _Static_assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
|
||||
# else
|
||||
/* Classic negative-size-array static assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
|
||||
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
|
||||
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
|
||||
# endif
|
||||
# endif
|
||||
#ifndef PB_STATIC_ASSERT
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
/* C11 standard _Static_assert mechanism */
|
||||
#define PB_STATIC_ASSERT(COND, MSG) _Static_assert(COND, #MSG);
|
||||
#else
|
||||
/* Static asserts disabled by PB_NO_STATIC_ASSERT */
|
||||
# define PB_STATIC_ASSERT(COND,MSG)
|
||||
/* Classic negative-size-array static assert mechanism */
|
||||
#define PB_STATIC_ASSERT(COND, MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND) ? 1 : -1];
|
||||
#define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
|
||||
#define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
/* Static asserts disabled by PB_NO_STATIC_ASSERT */
|
||||
#define PB_STATIC_ASSERT(COND, MSG)
|
||||
#endif
|
||||
|
||||
/* Number of required fields to keep track of. */
|
||||
@ -186,8 +185,8 @@ typedef uint_least8_t pb_type_t;
|
||||
/**** Field data types ****/
|
||||
|
||||
/* Numeric types */
|
||||
#define PB_LTYPE_BOOL 0x00U /* bool */
|
||||
#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */
|
||||
#define PB_LTYPE_BOOL 0x00U /* bool */
|
||||
#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */
|
||||
#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */
|
||||
#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */
|
||||
#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */
|
||||
@ -234,31 +233,30 @@ typedef uint_least8_t pb_type_t;
|
||||
#define PB_HTYPE_SINGULAR 0x10U
|
||||
#define PB_HTYPE_REPEATED 0x20U
|
||||
#define PB_HTYPE_FIXARRAY 0x20U
|
||||
#define PB_HTYPE_ONEOF 0x30U
|
||||
#define PB_HTYPE_MASK 0x30U
|
||||
#define PB_HTYPE_ONEOF 0x30U
|
||||
#define PB_HTYPE_MASK 0x30U
|
||||
|
||||
/**** Field allocation types ****/
|
||||
|
||||
#define PB_ATYPE_STATIC 0x00U
|
||||
#define PB_ATYPE_POINTER 0x80U
|
||||
#define PB_ATYPE_CALLBACK 0x40U
|
||||
#define PB_ATYPE_MASK 0xC0U
|
||||
|
||||
#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_ATYPE_STATIC 0x00U
|
||||
#define PB_ATYPE_POINTER 0x80U
|
||||
#define PB_ATYPE_CALLBACK 0x40U
|
||||
#define PB_ATYPE_MASK 0xC0U
|
||||
|
||||
#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)
|
||||
|
||||
/* Data type used for storing sizes of struct fields
|
||||
* and array counts.
|
||||
*/
|
||||
#if defined(PB_FIELD_32BIT)
|
||||
typedef uint32_t pb_size_t;
|
||||
typedef int32_t pb_ssize_t;
|
||||
typedef uint32_t pb_size_t;
|
||||
typedef int32_t pb_ssize_t;
|
||||
#else
|
||||
typedef uint_least16_t pb_size_t;
|
||||
typedef int_least16_t pb_ssize_t;
|
||||
typedef uint_least16_t pb_size_t;
|
||||
typedef int_least16_t pb_ssize_t;
|
||||
#endif
|
||||
#define PB_SIZE_MAX ((pb_size_t)-1)
|
||||
|
||||
@ -279,7 +277,7 @@ typedef struct pb_field_iter_s pb_field_iter_t;
|
||||
typedef struct pb_msgdesc_s pb_msgdesc_t;
|
||||
struct pb_msgdesc_s {
|
||||
const uint32_t *field_info;
|
||||
const pb_msgdesc_t * const * submsg_info;
|
||||
const pb_msgdesc_t *const *submsg_info;
|
||||
const pb_byte_t *default_value;
|
||||
|
||||
bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field);
|
||||
@ -291,22 +289,22 @@ struct pb_msgdesc_s {
|
||||
|
||||
/* Iterator for message descriptor */
|
||||
struct pb_field_iter_s {
|
||||
const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */
|
||||
void *message; /* Pointer to start of the structure */
|
||||
const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */
|
||||
void *message; /* Pointer to start of the structure */
|
||||
|
||||
pb_size_t index; /* Index of the field */
|
||||
pb_size_t field_info_index; /* Index to descriptor->field_info array */
|
||||
pb_size_t required_field_index; /* Index that counts only the required fields */
|
||||
pb_size_t submessage_index; /* Index that counts only submessages */
|
||||
pb_size_t index; /* Index of the field */
|
||||
pb_size_t field_info_index; /* Index to descriptor->field_info array */
|
||||
pb_size_t required_field_index; /* Index that counts only the required fields */
|
||||
pb_size_t submessage_index; /* Index that counts only submessages */
|
||||
|
||||
pb_size_t tag; /* Tag of current field */
|
||||
pb_size_t data_size; /* sizeof() of a single item */
|
||||
pb_size_t array_size; /* Number of array entries */
|
||||
pb_type_t type; /* Type of current field */
|
||||
pb_size_t tag; /* Tag of current field */
|
||||
pb_size_t data_size; /* sizeof() of a single item */
|
||||
pb_size_t array_size; /* Number of array entries */
|
||||
pb_type_t type; /* Type of current field */
|
||||
|
||||
void *pField; /* Pointer to current field in struct */
|
||||
void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */
|
||||
void *pSize; /* Pointer to count/has field */
|
||||
void *pField; /* Pointer to current field in struct */
|
||||
void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */
|
||||
void *pSize; /* Pointer to count/has field */
|
||||
|
||||
const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */
|
||||
};
|
||||
@ -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 {
|
||||
@ -363,9 +365,9 @@ struct pb_callback_s {
|
||||
*/
|
||||
union {
|
||||
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
|
||||
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
|
||||
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void *const *arg);
|
||||
} funcs;
|
||||
|
||||
|
||||
/* Free arg for use by callback */
|
||||
void *arg;
|
||||
};
|
||||
@ -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,9 +392,8 @@ 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.
|
||||
* If you do not have anything to write, just return true.
|
||||
@ -405,7 +401,7 @@ struct pb_extension_type_s {
|
||||
* Set to NULL for default handler.
|
||||
*/
|
||||
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||
|
||||
|
||||
/* Free field for use by the callback. */
|
||||
const void *arg;
|
||||
};
|
||||
@ -414,11 +410,11 @@ struct pb_extension_s {
|
||||
/* Type describing the extension field. Usually you'll initialize
|
||||
* this to a pointer to the automatically generated structure. */
|
||||
const pb_extension_type_t *type;
|
||||
|
||||
|
||||
/* Destination for the decoded data. This must match the datatype
|
||||
* of the extension field. */
|
||||
void *dest;
|
||||
|
||||
|
||||
/* Pointer to the next extension handler, or NULL.
|
||||
* If this extension does not match a field, the next handler is
|
||||
* automatically called. */
|
||||
@ -429,17 +425,20 @@ 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. */
|
||||
#ifdef PB_ENABLE_MALLOC
|
||||
# ifndef pb_realloc
|
||||
# define pb_realloc(ptr, size) realloc(ptr, size)
|
||||
# endif
|
||||
# ifndef pb_free
|
||||
# define pb_free(ptr) free(ptr)
|
||||
# endif
|
||||
#ifndef pb_realloc
|
||||
#define pb_realloc(ptr, size) realloc(ptr, size)
|
||||
#endif
|
||||
#ifndef pb_free
|
||||
#define pb_free(ptr) free(ptr)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
|
||||
@ -447,7 +446,7 @@ struct pb_extension_s {
|
||||
|
||||
/* These macros are used to declare pb_field_t's in the constant array. */
|
||||
/* Size of a structure member, in bytes. */
|
||||
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
|
||||
#define pb_membersize(st, m) (sizeof((st *)0)->m)
|
||||
/* Number of entries in an array. */
|
||||
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
|
||||
/* Delta from start of one member to the start of another member. */
|
||||
@ -457,126 +456,115 @@ struct pb_extension_s {
|
||||
#define PB_EXPAND(x) x
|
||||
|
||||
/* 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 = \
|
||||
{ \
|
||||
structname ## _field_info, \
|
||||
structname ## _submsg_info, \
|
||||
msgname ## _DEFAULT, \
|
||||
msgname ## _CALLBACK, \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \
|
||||
}; \
|
||||
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname)
|
||||
#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 = { \
|
||||
structname##_field_info, \
|
||||
structname##_submsg_info, \
|
||||
msgname##_DEFAULT, \
|
||||
msgname##_CALLBACK, \
|
||||
0 msgname##_FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
|
||||
0 msgname##_FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \
|
||||
0 msgname##_FIELDLIST(PB_GEN_LARGEST_TAG, structname), \
|
||||
}; \
|
||||
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) \
|
||||
PB_FIELDINFO_1(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_1(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_2(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_2(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_4(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_4(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_8(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_8(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), \
|
||||
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_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), \
|
||||
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_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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_##width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
/* X-macro for generating asserts that entries fit in struct_field_info[] array.
|
||||
* The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(),
|
||||
* but it is not easily reused because of how macro substitutions work. */
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_1(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_1(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_2(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_2(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_4(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_4(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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_8(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), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_8(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), \
|
||||
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_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), \
|
||||
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_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), \
|
||||
PB_ARRAY_SIZE_##atype(_PB_HTYPE_##htype, structname, fieldname))
|
||||
|
||||
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ASSERT_##width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO##htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO##htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO##htype(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
@ -584,16 +572,17 @@ struct pb_extension_s {
|
||||
#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
|
||||
|
||||
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO##htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR##htype(structname, fieldname)
|
||||
#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)
|
||||
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
|
||||
#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)
|
||||
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname##_count)
|
||||
#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||
@ -608,8 +597,8 @@ struct pb_extension_s {
|
||||
#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||
|
||||
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS##htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR##htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1
|
||||
@ -624,9 +613,9 @@ struct pb_extension_s {
|
||||
#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
|
||||
|
||||
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS##htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR##htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB##htype(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
@ -646,22 +635,25 @@ struct pb_extension_s {
|
||||
#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
|
||||
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple)
|
||||
#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname
|
||||
#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername
|
||||
#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname
|
||||
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_##type tuple)
|
||||
#define PB_ONEOF_NAME_UNION(unionname, membername, fullname) unionname
|
||||
#define PB_ONEOF_NAME_MEMBER(unionname, membername, fullname) membername
|
||||
#define PB_ONEOF_NAME_FULL(unionname, membername, fullname) fullname
|
||||
|
||||
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname)
|
||||
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_SUBMSG_INFO_##htype(_PB_LTYPE_##ltype, structname, fieldname)
|
||||
|
||||
#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_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_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_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)
|
||||
#define PB_SI_PB_LTYPE_BYTES(t)
|
||||
#define PB_SI_PB_LTYPE_DOUBLE(t)
|
||||
@ -672,7 +664,7 @@ struct pb_extension_s {
|
||||
#define PB_SI_PB_LTYPE_FLOAT(t)
|
||||
#define PB_SI_PB_LTYPE_INT32(t)
|
||||
#define PB_SI_PB_LTYPE_INT64(t)
|
||||
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SI_PB_LTYPE_SFIXED32(t)
|
||||
#define PB_SI_PB_LTYPE_SFIXED64(t)
|
||||
@ -683,7 +675,7 @@ struct pb_extension_s {
|
||||
#define PB_SI_PB_LTYPE_UINT64(t)
|
||||
#define PB_SI_PB_LTYPE_EXTENSION(t)
|
||||
#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t)
|
||||
#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg),
|
||||
#define PB_SUBMSG_DESCRIPTOR(t) &(t##_msg),
|
||||
|
||||
/* The field descriptors use a variable width format, with width of either
|
||||
* 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always
|
||||
@ -713,23 +705,23 @@ struct pb_extension_s {
|
||||
* [32-bit reserved]
|
||||
*/
|
||||
|
||||
#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
(0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \
|
||||
(((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)),
|
||||
#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
(0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset)&0xFF) << 16) | \
|
||||
(((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)), \
|
||||
(((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)),
|
||||
#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)), \
|
||||
(((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),
|
||||
#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),
|
||||
|
||||
#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,
|
||||
#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,
|
||||
|
||||
/* 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
|
||||
@ -738,92 +730,103 @@ struct pb_extension_s {
|
||||
* you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting
|
||||
* descriptorsize option in .options file.
|
||||
*/
|
||||
#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)
|
||||
#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)
|
||||
|
||||
#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)
|
||||
#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)
|
||||
|
||||
#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)
|
||||
#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)
|
||||
|
||||
#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)
|
||||
#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)
|
||||
#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)
|
||||
#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)
|
||||
|
||||
#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)
|
||||
#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)
|
||||
#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.
|
||||
* The generator will give explicit size argument when it knows that a message
|
||||
* structure grows beyond 1-word format limits.
|
||||
*/
|
||||
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH##atype(htype, ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH##htype(ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH##htype(ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2
|
||||
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH##ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH##ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH##ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH##ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2
|
||||
#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2
|
||||
|
||||
/* The mapping from protobuf types to LTYPEs is done using these macros. */
|
||||
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
|
||||
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
|
||||
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
|
||||
#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB
|
||||
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
|
||||
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
|
||||
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
|
||||
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
|
||||
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
|
||||
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
|
||||
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
|
||||
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
|
||||
#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB
|
||||
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
|
||||
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
|
||||
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
|
||||
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
|
||||
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
|
||||
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
|
||||
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
|
||||
#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES
|
||||
|
||||
/* These macros are used for giving out error messages.
|
||||
@ -855,21 +858,21 @@ struct pb_extension_s {
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus >= 201103L
|
||||
#define PB_CONSTEXPR constexpr
|
||||
#else // __cplusplus >= 201103L
|
||||
#else // __cplusplus >= 201103L
|
||||
#define PB_CONSTEXPR
|
||||
#endif // __cplusplus >= 201103L
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#define PB_INLINE_CONSTEXPR inline constexpr
|
||||
#else // __cplusplus >= 201703L
|
||||
#else // __cplusplus >= 201703L
|
||||
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
|
||||
#endif // __cplusplus >= 201703L
|
||||
#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 */
|
||||
} // namespace nanopb
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -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
|
||||
@ -396,4 +397,4 @@ lib_deps =
|
||||
;extends = esp32_base
|
||||
;board = genieblocks_lora
|
||||
;build_flags =
|
||||
; ${esp32_base.build_flags} -D GENIEBLOCKS
|
||||
; ${esp32_base.build_flags} -D GENIEBLOCKS
|
||||
|
2
proto
2
proto
@ -1 +1 @@
|
||||
Subproject commit 0ea232802651fd6aaa53c93c09f4c2eb36470dd0
|
||||
Subproject commit 3252ed0f1357d55233f2d5cb50acf20dbb9160ed
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency
|
||||
@ -28,4 +27,4 @@ class BinarySemaphorePosix
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace concurrency
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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 = ∓
|
||||
|
||||
/// 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)
|
||||
|
@ -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
|
||||
|
@ -12,10 +12,8 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
#define NODENUM_BROADCAST UINT32_MAX
|
||||
#define ERRNO_OK 0
|
||||
#define ERRNO_NO_INTERFACES 33
|
||||
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
|
||||
#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);
|
@ -15,7 +15,10 @@
|
||||
#error ToRadio is too big
|
||||
#endif
|
||||
|
||||
PhoneAPI::PhoneAPI() {}
|
||||
PhoneAPI::PhoneAPI()
|
||||
{
|
||||
lastContactMsec = millis();
|
||||
}
|
||||
|
||||
PhoneAPI::~PhoneAPI()
|
||||
{
|
||||
@ -53,9 +56,12 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,9 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Handle a received message, the data field in the message is already decoded and is provided
|
||||
*
|
||||
*
|
||||
* In general decoded will always be !NULL. But in some special applications (where you have handling packets
|
||||
* for multiple port numbers, decoding will ONLY be attempted for packets where the portnum matches our expected ourPortNum.
|
||||
*/
|
||||
@ -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) {
|
||||
T *decoded = NULL;
|
||||
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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
@ -153,7 +155,7 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
if (s.dest != 0)
|
||||
DEBUG_MSG(" dest=%08x", s.dest);
|
||||
|
||||
if(s.request_id)
|
||||
if (s.request_id)
|
||||
DEBUG_MSG(" requestId=%0x", s.request_id);
|
||||
|
||||
/* now inside Data and therefore kinda opaque
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
printPacket("Enqueued local", 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)
|
||||
|
@ -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
|
||||
@ -53,7 +48,7 @@ class Router : protected concurrency::OSThread
|
||||
ErrorCode sendLocal(MeshPacket *p);
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool cancelSending(NodeNum from, PacketId id);
|
||||
bool cancelSending(NodeNum from, PacketId id);
|
||||
|
||||
/** Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
* The returned packet is guaranteed to have a unique packet ID already assigned
|
||||
@ -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;
|
||||
|
||||
@ -83,7 +84,7 @@ class Router : protected concurrency::OSThread
|
||||
|
||||
/**
|
||||
* Should this incoming filter be dropped?
|
||||
*
|
||||
*
|
||||
* FIXME, move this into the new RoutingPlugin and do the filtering there using the regular plugin logic
|
||||
*
|
||||
* Called immedately on receiption, before any further processing.
|
||||
@ -97,18 +98,11 @@ 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
|
||||
*/
|
||||
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called from loop()
|
||||
@ -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
|
||||
|
@ -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);
|
||||
@ -38,11 +36,11 @@ bool SX1262Interface::init()
|
||||
float tcxoVoltage = 0; // None - we use an XTAL
|
||||
#else
|
||||
// Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
|
||||
float tcxoVoltage = 1.8;
|
||||
float tcxoVoltage = 1.8;
|
||||
#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;
|
||||
|
@ -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" */
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
12
src/mesh/generated/mqtt.pb.c
Normal file
12
src/mesh/generated/mqtt.pb.c
Normal 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)
|
||||
|
||||
|
||||
|
55
src/mesh/generated/mqtt.pb.h
Normal file
55
src/mesh/generated/mqtt.pb.h
Normal 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
|
@ -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
167
src/mqtt/MQTT.cpp
Normal 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 *)∓
|
||||
|
||||
// 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
60
src/mqtt/MQTT.h
Normal 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;
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -10,12 +10,13 @@
|
||||
|
||||
// Because (FIXME) we currently don't tell API clients status on sent messages
|
||||
// we need to throttle our sending, so that if a gpio is bouncing up and down we
|
||||
// don't generate more messages than the net can send. So we limit watch messages to
|
||||
// don't generate more messages than the net can send. So we limit watch messages to
|
||||
// a max of one change per 30 seconds
|
||||
#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")
|
||||
{
|
||||
}
|
||||
|
||||
@ -69,26 +70,26 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
|
||||
case HardwareMessage_Type_READ_GPIOS: {
|
||||
// Print notification to LCD screen
|
||||
if(screen)
|
||||
if (screen)
|
||||
screen->print("Read GPIOs\n");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case HardwareMessage_Type_WATCH_GPIOS: {
|
||||
watchGpios = p.gpio_mask;
|
||||
lastWatchMsec = 0; // Force a new publish soon
|
||||
lastWatchMsec = 0; // Force a new publish soon
|
||||
previousWatch = ~watchGpios; // generate a 'previous' value which is guaranteed to not match (to force an initial publish)
|
||||
enabled = true; // Let our thread run at least once
|
||||
enabled = true; // Let our thread run at least once
|
||||
DEBUG_MSG("Now watching GPIOs 0x%llx\n", watchGpios);
|
||||
break;
|
||||
}
|
||||
@ -101,31 +102,31 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ);
|
||||
break;
|
||||
}
|
||||
|
||||
return true; // handled
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t RemoteHardwarePlugin::runOnce() {
|
||||
if(watchGpios) {
|
||||
int32_t RemoteHardwarePlugin::runOnce()
|
||||
{
|
||||
if (watchGpios) {
|
||||
uint32_t now = millis();
|
||||
|
||||
if(now - lastWatchMsec >= WATCH_INTERVAL_MSEC) {
|
||||
if (now - lastWatchMsec >= WATCH_INTERVAL_MSEC) {
|
||||
uint64_t curVal = digitalReads(watchGpios);
|
||||
|
||||
if(curVal != previousWatch) {
|
||||
if (curVal != previousWatch) {
|
||||
previousWatch = curVal;
|
||||
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;
|
||||
}
|
||||
|
@ -1,76 +1,61 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel");
|
||||
|
||||
/** Dear pinetab hardware geeks!
|
||||
*
|
||||
*
|
||||
* The current pinetab lora module has a slight bug. The ch341 part only provides ISR assertions on edges.
|
||||
* This makes sense because USB interrupts happen through fast/repeated special irq urbs that are constantly
|
||||
* chattering on the USB bus.
|
||||
*
|
||||
*
|
||||
* But this isn't sufficient for level triggered ISR sources like the sx127x radios. The common way that seems to
|
||||
* be addressed by cs341 users is to **always** connect the INT# (pin 26 on the ch341f) signal to one of the GPIO signals
|
||||
* on the part. I'd recommend connecting that LORA_DIO0/INT# line to pin 19 (data 4) on the pinetab board. This would
|
||||
* provide an efficent mechanism so that the (kernel) code in the cs341 driver that I've slightly hacked up to see the
|
||||
* current state of LORA_DIO0. Without that access, I can't know if the interrupt is still pending - which would create
|
||||
* race conditions in packet handling.
|
||||
*
|
||||
* race conditions in packet handling.
|
||||
*
|
||||
* My workaround is to poll the status register internally to the sx127x. Which is expensive because it involves a number of
|
||||
* i2c transactions and many trips back and forth between kernel and my userspace app. I think shipping the current version
|
||||
* of the pinetab lora device would be fine because I can poll slowly (because lora is slow). But if you ever have cause to
|
||||
* rev this board. I highly encourage this small change.
|
||||
*
|
||||
*
|
||||
* Btw - your little "USB lora dongle" is really neat. I encourage you to sell it, because even non pinetab customers could
|
||||
* use it to easily add lora to rasberry pi, desktop pcs etc...
|
||||
*
|
||||
*
|
||||
* Porduino helper class to do this i2c based polling:
|
||||
*/
|
||||
class R595PolledIrqPin : public GPIOPin {
|
||||
public:
|
||||
class R595PolledIrqPin : public GPIOPin
|
||||
{
|
||||
public:
|
||||
R595PolledIrqPin() : GPIOPin(LORA_DIO0, "LORA_DIO0") {}
|
||||
|
||||
/// Read the low level hardware for this pin
|
||||
virtual PinStatus readPinHardware()
|
||||
{
|
||||
if(isrPinStatus < 0)
|
||||
if (isrPinStatus < 0)
|
||||
return LOW; // No interrupt handler attached, don't bother polling i2c right now
|
||||
else {
|
||||
extern RadioInterface *rIf; // FIXME, temporary hack until we know if we need to keep this
|
||||
extern RadioInterface *rIf; // FIXME, temporary hack until we know if we need to keep this
|
||||
|
||||
assert(rIf);
|
||||
RF95Interface *rIf95 = static_cast<RF95Interface *>(rIf);
|
||||
@ -81,17 +66,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** apps run under portduino can optionally define a portduinoSetup() to
|
||||
/** 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() {
|
||||
printf("Setting up Meshtastic on Porduino...\n");
|
||||
void portduinoSetup()
|
||||
{
|
||||
printf("Setting up Meshtastic on Porduino...\n");
|
||||
|
||||
// FIXME: disable while not testing with real hardware
|
||||
// gpioBind(new R595PolledIrqPin());
|
||||
// FIXME: disable while not testing with real hardware
|
||||
// gpioBind(new R595PolledIrqPin());
|
||||
|
||||
// gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET")));
|
||||
// gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent());
|
||||
// gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET")));
|
||||
// gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent());
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 1
|
||||
minor = 2
|
||||
build = 17
|
||||
build = 23
|
||||
|
Loading…
Reference in New Issue
Block a user