begin plugin-api tutorial

pull/548/head
Kevin Hester 2020-12-11 09:11:53 +08:00
rodzic c361c1fab7
commit b9f1ce70cb
14 zmienionych plików z 166 dodań i 40 usunięć

Wyświetl plik

@ -34,6 +34,8 @@ For app cleanup:
* move python ping functionality into device, reply with rxsnr info
* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104
* generate autodocs
* update positions and nodeinfos based on packets we just merely witness on the mesh. via isPromsciousPort bool.
* MeshPackets for sending should be reference counted so that API clients would have the option of checking sent status (would allow removing the nasty 30 sec timer in gpio watch sending)
For high speed/lots of devices/short range tasks:

Wyświetl plik

@ -1,8 +1,11 @@
# Build instructions
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode.
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode. Workflows from building from the GUI or from the commandline are listed below.
If you encounter any problems, please post a question in [our forum](meshtastic.discourse.group). And when you learn a fix, update these instructions for the next person (i.e. edit this file and send in a [pull-request](https://opensource.com/article/19/7/create-pull-request-github) which we will eagerly merge).
## GUI
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
2. Install [Python](https://www.python.org/downloads/).
3. Install [Git](https://git-scm.com/downloads).
@ -19,6 +22,7 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension
Note - To get a clean build you may have to delete the auto-generated file `./.vscode/c_cpp_properties.json`, close and re-open Visual Studio and WAIT until the file is auto-generated before compiling again.
## Command Line
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
2. Install [PlatformIO](https://platformio.org/platformio-ide)
3. Download this git repo and cd into it:

Wyświetl plik

@ -1,4 +1,6 @@
# Device API
# Bluetooth/serial/TCP protocol API
(This document describes the protocol for external API clients using our devices. If you are interested in running your own code on the device itself, see the [on-device](plugin-api.md) documentation instead)
The Device API is design to have only a simple stream of ToRadio and FromRadio packets and all polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.

Wyświetl plik

@ -0,0 +1,66 @@
# Plugin-API
This is a tutorial on how to write small plugins which run on the device. Plugins are bits regular 'arduino' code that can send and receive packets to other nodes/apps/PCs using our mesh.
## Key concepts
All plugins should be subclasses of MeshPlugin. By inheriting from this class and creating an instance of your new plugin your plugin will be automatically registered to receive packets.
Messages are sent to particular port numbers (similar to UDP networking). Your new plugin should eventually pick its own port number (see below), but at first you can simply use PRIVATE_APP (which is the default).
Packets can be sent/received either as raw binary structures or as [Protobufs](https://developers.google.com/protocol-buffers).
### Class heirarchy
The relevant bits of the class heirarchy are as follows
* MeshPlugin (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly
* SinglePortPlugin (in src/mesh/SinglePortPlugin.h) - for plugins that receive from a single port number (the normal case)
* ProtobufPlugin (in src/mesh/ProtobufPlugin.h) - for plugins that are sending/receiving a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
You will typically want to inherit from either SinglePortPlugin (if you are just sending raw bytes) or ProtobufPlugin (if you are sending protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
If you need to send a packet you can call service.sendToMesh with code like this (from the examples):
```
MeshPacket *p = allocReply();
p->to = dest;
service.sendToMesh(p);
```
## Example plugins
A number of [key services](/src/plugins) are implemented using the plugin API, these plugins are as follows:
* [TextMessagePlugin](/src/plugins/TextMessagePlugin.h) - receives text messages and displays them on the LCD screen/stores them in the local DB
* [NodeInfoPlugin](/src/plugins/NodeInfoPlugin.h) - receives/sends User information to other nodes so that usernames are available in the databases
* [RemoteHardwarePlugin](/src/plugins/RemoteHardwarePlugin.h) - a plugin that provides easy remote access to device hardware (for things like turning GPIOs on or off). Intended to be a more extensive example and provide a useful feature of its own. See [remote-hardware](remote-hardware.md) for details.
* [ReplyPlugin](/src/plugins/ReplyPlugin.h) - a simple plugin that just replies to any packet it receives (provides a 'ping' service).
## Getting started
The easiest way to get started is to copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.h) into src/plugins/YourPlugin.*. Then change the port number from REPLY_APP to PRIVATE_APP.
## Picking a port number
For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application.
If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range:
* 0-63 Core Meshtastic use, do not use for third party apps
* 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application
* 256-511 Use one of these portnums for your private applications that you don't want to register publically
* 1024-66559 Are reserved for use by IP tunneling (see FIXME for more information)
All other values are reserved.
## How to add custom protocol buffers
If you would like to use protocol buffers to define the structures you send over the mesh (recommended), here's how to do that.
* Create a new .proto file in the protos directory. You can use the existing [remote_hardware.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/remote_hardware.proto) file as an example.
* Run "bin/regen-protos.sh" to regenerate the C code for accessing the protocol buffers. If you don't have the required nanopb tool, follow the instructions printed by the script to get it.
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.

Wyświetl plik

@ -1,9 +1,10 @@
This is a mini design doc for developing the meshtastic software.
* [Build instructions](build-instructions.md)
* [On device plugin API](plugin-api.md) - a tutorial on how to write small Plugins which run on the device and can message other nodes.
* [TODO](TODO.md) - read this if you are looking for things to do (or curious about currently missing features)
* Our [project board](https://github.com/orgs/meshtastic/projects/1) - shows what things we are currently working on and remaining work items for the current release.
* [Power Management](power.md)
* [Mesh algorithm](mesh-alg.md)
* [Device API](device-api.md) and porting guide for new clients (iOS, python, etc...)
* [External client API](device-api.md) and porting guide for new clients (iOS, python, etc...)
* TODO: how to port the device code to a new device.

2
proto

@ -1 +1 @@
Subproject commit ebd18145cafb0d09528150b7a5eccd52b581902f
Subproject commit d0868e366bc8f8d9b7ed1d1c5a80cac0de9dc956

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#include "mesh.pb.h"
#if PB_PROTO_HEADER_VERSION != 40

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#ifndef PB_MESH_PB_H_INCLUDED
#define PB_MESH_PB_H_INCLUDED
@ -10,10 +10,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _RouteError {
RouteError_NONE = 0,
@ -271,6 +267,10 @@ typedef struct _ToRadio {
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0}
#define Data_init_default {_PortNum_MIN, {0, {0}}}
@ -306,12 +306,12 @@ typedef struct _ToRadio {
/* Field tags (for use in manual encoding/decoding) */
#define ChannelSettings_tx_power_tag 1
#define ChannelSettings_modem_config_tag 3
#define ChannelSettings_psk_tag 4
#define ChannelSettings_name_tag 5
#define ChannelSettings_bandwidth_tag 6
#define ChannelSettings_spread_factor_tag 7
#define ChannelSettings_coding_rate_tag 8
#define ChannelSettings_channel_num_tag 9
#define ChannelSettings_psk_tag 4
#define ChannelSettings_name_tag 5
#define Data_portnum_tag 1
#define Data_payload_tag 2
#define DebugString_message_tag 1
@ -329,10 +329,10 @@ typedef struct _ToRadio {
#define MyNodeInfo_node_num_bits_tag 12
#define MyNodeInfo_message_timeout_msec_tag 13
#define MyNodeInfo_min_app_version_tag 14
#define Position_latitude_i_tag 7
#define Position_longitude_i_tag 8
#define Position_altitude_tag 3
#define Position_battery_level_tag 4
#define Position_latitude_i_tag 7
#define Position_longitude_i_tag 8
#define Position_time_tag 9
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
@ -349,15 +349,15 @@ typedef struct _ToRadio {
#define RadioConfig_UserPreferences_wifi_password_tag 13
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
#define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
#define RadioConfig_UserPreferences_is_router_tag 37
#define RadioConfig_UserPreferences_is_low_power_tag 38
#define RadioConfig_UserPreferences_fixed_position_tag 39
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
#define RouteDiscovery_route_tag 2
#define User_id_tag 1
@ -367,8 +367,8 @@ typedef struct _ToRadio {
#define NodeInfo_num_tag 1
#define NodeInfo_user_tag 2
#define NodeInfo_position_tag 3
#define NodeInfo_snr_tag 7
#define NodeInfo_next_hop_tag 5
#define NodeInfo_snr_tag 7
#define RadioConfig_preferences_tag 1
#define RadioConfig_channel_settings_tag 2
#define SubPacket_position_tag 1
@ -377,19 +377,19 @@ typedef struct _ToRadio {
#define SubPacket_route_request_tag 6
#define SubPacket_route_reply_tag 7
#define SubPacket_route_error_tag 13
#define SubPacket_success_id_tag 10
#define SubPacket_fail_id_tag 11
#define SubPacket_original_id_tag 2
#define SubPacket_want_response_tag 5
#define SubPacket_dest_tag 9
#define SubPacket_success_id_tag 10
#define SubPacket_fail_id_tag 11
#define SubPacket_source_tag 12
#define SubPacket_original_id_tag 2
#define MeshPacket_decoded_tag 3
#define MeshPacket_encrypted_tag 8
#define MeshPacket_from_tag 1
#define MeshPacket_to_tag 2
#define MeshPacket_decoded_tag 3
#define MeshPacket_encrypted_tag 8
#define MeshPacket_id_tag 6
#define MeshPacket_rx_time_tag 9
#define MeshPacket_rx_snr_tag 7
#define MeshPacket_rx_time_tag 9
#define MeshPacket_hop_limit_tag 10
#define MeshPacket_want_ack_tag 11
#define DeviceState_radio_tag 1
@ -397,10 +397,11 @@ typedef struct _ToRadio {
#define DeviceState_owner_tag 3
#define DeviceState_node_db_tag 4
#define DeviceState_receive_queue_tag 5
#define DeviceState_version_tag 8
#define DeviceState_rx_text_message_tag 7
#define DeviceState_version_tag 8
#define DeviceState_no_save_tag 9
#define DeviceState_did_gps_reset_tag 11
#define FromRadio_num_tag 1
#define FromRadio_packet_tag 2
#define FromRadio_my_info_tag 3
#define FromRadio_node_info_tag 4
@ -408,7 +409,6 @@ typedef struct _ToRadio {
#define FromRadio_debug_string_tag 7
#define FromRadio_config_complete_id_tag 8
#define FromRadio_rebooted_tag 9
#define FromRadio_num_tag 1
#define ToRadio_packet_tag 1
#define ToRadio_want_config_id_tag 100
#define ToRadio_set_radio_tag 101
@ -449,7 +449,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \
X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \
X(a, STATIC, ONEOF, ENUM, (payload,route_error,route_error), 13) \
X(a, STATIC, ONEOF, UENUM, (payload,route_error,route_error), 13) \
X(a, STATIC, SINGULAR, UINT32, original_id, 2) \
X(a, STATIC, SINGULAR, BOOL, want_response, 5) \
X(a, STATIC, SINGULAR, UINT32, dest, 9) \

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#include "portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#ifndef PB_PORTNUMS_PB_H_INCLUDED
#define PB_PORTNUMS_PB_H_INCLUDED
@ -9,10 +9,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _PortNum {
PortNum_UNKNOWN_APP = 0,
@ -20,6 +16,7 @@ typedef enum _PortNum {
PortNum_REMOTE_HARDWARE_APP = 2,
PortNum_POSITION_APP = 3,
PortNum_NODEINFO_APP = 4,
PortNum_REPLY_APP = 32,
PortNum_PRIVATE_APP = 256,
PortNum_IP_TUNNEL_APP = 1024
} PortNum;
@ -30,6 +27,10 @@ typedef enum _PortNum {
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_IP_TUNNEL_APP+1))
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#include "remote_hardware.pb.h"
#if PB_PROTO_HEADER_VERSION != 40

Wyświetl plik

@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
/* Generated by nanopb-0.4.4 */
#ifndef PB_REMOTE_HARDWARE_PB_H_INCLUDED
#define PB_REMOTE_HARDWARE_PB_H_INCLUDED
@ -9,10 +9,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _HardwareMessage_Type {
HardwareMessage_Type_UNSET = 0,
@ -37,6 +33,10 @@ typedef struct _HardwareMessage {
#define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1))
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0}
#define HardwareMessage_init_zero {_HardwareMessage_Type_MIN, 0, 0}

Wyświetl plik

@ -0,0 +1,27 @@
#include "configuration.h"
#include "ReplyPlugin.h"
#include "MeshService.h"
#include "main.h"
#include <assert.h>
// Create an a static instance of our plugin - this registers with the plugin system
ReplyPlugin replyPlugin;
bool ReplyPlugin::handleReceived(const MeshPacket &req)
{
auto &p = req.decoded.data;
// The incoming message is in p.payload
DEBUG_MSG("Received message from=0x%0x, id=%d, msg=%.*s\n", req.from, req.id, p.payload.size, p.payload.bytes);
screen->print("Sending reply\n");
const char *replyStr = "Message Received";
auto reply = allocDataPacket(); // Allocate a packet for sending
reply->decoded.data.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
memcpy(reply->decoded.data.payload.bytes, replyStr, reply->decoded.data.payload.size);
setReplyTo(reply, req); // Set packet params so that this packet is marked as a reply to a previous request
service.sendToMesh(reply); // Queue the reply for sending
return true; // We handled it
}

Wyświetl plik

@ -0,0 +1,23 @@
#pragma once
#include "SinglePortPlugin.h"
/**
* A simple example plugin that just replies with "Message received" to any message it receives.
*/
class ReplyPlugin : public SinglePortPlugin
{
public:
/** Constructor
* name is for debugging output
*/
ReplyPlugin() : SinglePortPlugin("reply", PortNum_REPLY_APP) {}
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp);
};