mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-26 09:59:01 +00:00
WIP on GPIO example
This commit is contained in:
parent
8f5a1f19d3
commit
90060e84c0
@ -13,7 +13,7 @@ For app cleanup:
|
|||||||
* on android for received positions handle either old or new positions / user messages
|
* on android for received positions handle either old or new positions / user messages
|
||||||
* on android side send old or new positions as needed / user messages
|
* on android side send old or new positions as needed / user messages
|
||||||
* test python side handle new position/user messages
|
* test python side handle new position/user messages
|
||||||
* make a gpio example
|
* make a gpio example. --gpiowrb 5, --gpiord 0x444, --gpiowatch 0x3ff
|
||||||
* DONE fix position sending to use new plugin
|
* DONE fix position sending to use new plugin
|
||||||
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
|
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
|
||||||
* DONE move positions into regular data packets (use new app framework)
|
* DONE move positions into regular data packets (use new app framework)
|
||||||
|
2
proto
2
proto
@ -1 +1 @@
|
|||||||
Subproject commit 6e8d220ad0d9f7ae6ce37db94c2b3f55a70f4f45
|
Subproject commit f38b8e3040528aaf1f1b4b9024ff8df2e14ba961
|
@ -1,5 +1,6 @@
|
|||||||
#include "MeshPlugin.h"
|
#include "MeshPlugin.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
|
#include "MeshService.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
||||||
@ -31,7 +32,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
|
|
||||||
// Possibly send replies (unless we are handling a locally generated message)
|
// Possibly send replies (unless we are handling a locally generated message)
|
||||||
if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum())
|
if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum())
|
||||||
pi.sendResponse(mp.from);
|
pi.sendResponse(mp);
|
||||||
|
|
||||||
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
|
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
|
||||||
if (handled)
|
if (handled)
|
||||||
@ -41,4 +42,28 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
DEBUG_MSG("Plugin %s not interested\n", pi.name);
|
DEBUG_MSG("Plugin %s not interested\n", pi.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
*/
|
||||||
|
void MeshPlugin::sendResponse(const MeshPacket &req) {
|
||||||
|
auto r = allocReply();
|
||||||
|
if(r) {
|
||||||
|
DEBUG_MSG("Sending response\n");
|
||||||
|
setReplyTo(r, req);
|
||||||
|
service.sendToMesh(r);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||||
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||||
|
*/
|
||||||
|
void setReplyTo(MeshPacket *p, const MeshPacket &to) {
|
||||||
|
p->to = to.from;
|
||||||
|
p->want_ack = to.want_ack;
|
||||||
}
|
}
|
@ -49,8 +49,19 @@ class MeshPlugin
|
|||||||
virtual bool handleReceived(const MeshPacket &mp) { return false; }
|
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
|
/** 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
|
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||||
* is optional
|
virtual MeshPacket *allocReply() { return NULL; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||||
|
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
|
||||||
|
* to generate the reply message, and if !NULL that message will be delivered to whoever sent req
|
||||||
*/
|
*/
|
||||||
virtual void sendResponse(NodeNum to) {}
|
void sendResponse(const MeshPacket &req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||||
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||||
|
*/
|
||||||
|
void setReplyTo(MeshPacket *p, const MeshPacket &to);
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "SinglePortPlugin.h"
|
#include "SinglePortPlugin.h"
|
||||||
#include "Router.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based
|
* A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based
|
||||||
@ -34,12 +33,10 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
|
|||||||
* You can then send this packet (after customizing any of the payload fields you might need) with
|
* You can then send this packet (after customizing any of the payload fields you might need) with
|
||||||
* service.sendToMesh()
|
* service.sendToMesh()
|
||||||
*/
|
*/
|
||||||
MeshPacket *allocForSending(const T &payload)
|
MeshPacket *allocDataProtobuf(const T &payload)
|
||||||
{
|
{
|
||||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
MeshPacket *p = router->allocForSending();
|
MeshPacket *p = allocDataPacket();
|
||||||
p->decoded.which_payload = SubPacket_data_tag;
|
|
||||||
p->decoded.data.portnum = ourPortNum;
|
|
||||||
|
|
||||||
p->decoded.data.payload.size =
|
p->decoded.data.payload.size =
|
||||||
pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload);
|
pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "MeshPlugin.h"
|
#include "MeshPlugin.h"
|
||||||
|
#include "Router.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common
|
* Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common
|
||||||
@ -21,4 +22,19 @@ class SinglePortPlugin : public MeshPlugin
|
|||||||
* @return true if you want to receive the specified portnum
|
* @return true if you want to receive the specified portnum
|
||||||
*/
|
*/
|
||||||
virtual bool wantPortnum(PortNum p) { return p == ourPortNum; }
|
virtual bool wantPortnum(PortNum p) { return p == ourPortNum; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a mesh packet which has been preinited as a data packet with a particular port number.
|
||||||
|
* You can then send this packet (after customizing any of the payload fields you might need) with
|
||||||
|
* service.sendToMesh()
|
||||||
|
*/
|
||||||
|
MeshPacket *allocDataPacket()
|
||||||
|
{
|
||||||
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||||
|
MeshPacket *p = router->allocForSending();
|
||||||
|
p->decoded.which_payload = SubPacket_data_tag;
|
||||||
|
p->decoded.data.portnum = ourPortNum;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -14,32 +14,32 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Enum definitions */
|
/* Enum definitions */
|
||||||
typedef enum _HardwareMessage_MessageType {
|
typedef enum _HardwareMessage_Type {
|
||||||
HardwareMessage_MessageType_UNSET = 0,
|
HardwareMessage_Type_UNSET = 0,
|
||||||
HardwareMessage_MessageType_WRITE_GPIOS = 1,
|
HardwareMessage_Type_WRITE_GPIOS = 1,
|
||||||
HardwareMessage_MessageType_WATCH_GPIOS = 2,
|
HardwareMessage_Type_WATCH_GPIOS = 2,
|
||||||
HardwareMessage_MessageType_GPIOS_CHANGED = 3,
|
HardwareMessage_Type_GPIOS_CHANGED = 3,
|
||||||
HardwareMessage_MessageType_READ_GPIOS = 4,
|
HardwareMessage_Type_READ_GPIOS = 4,
|
||||||
HardwareMessage_MessageType_READ_GPIOS_REPLY = 5
|
HardwareMessage_Type_READ_GPIOS_REPLY = 5
|
||||||
} HardwareMessage_MessageType;
|
} HardwareMessage_Type;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
typedef struct _HardwareMessage {
|
typedef struct _HardwareMessage {
|
||||||
HardwareMessage_MessageType typ;
|
HardwareMessage_Type typ;
|
||||||
uint64_t gpio_mask;
|
uint64_t gpio_mask;
|
||||||
uint64_t gpio_value;
|
uint64_t gpio_value;
|
||||||
} HardwareMessage;
|
} HardwareMessage;
|
||||||
|
|
||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
#define _HardwareMessage_MessageType_MIN HardwareMessage_MessageType_UNSET
|
#define _HardwareMessage_Type_MIN HardwareMessage_Type_UNSET
|
||||||
#define _HardwareMessage_MessageType_MAX HardwareMessage_MessageType_READ_GPIOS_REPLY
|
#define _HardwareMessage_Type_MAX HardwareMessage_Type_READ_GPIOS_REPLY
|
||||||
#define _HardwareMessage_MessageType_ARRAYSIZE ((HardwareMessage_MessageType)(HardwareMessage_MessageType_READ_GPIOS_REPLY+1))
|
#define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1))
|
||||||
|
|
||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define HardwareMessage_init_default {_HardwareMessage_MessageType_MIN, 0, 0}
|
#define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0}
|
||||||
#define HardwareMessage_init_zero {_HardwareMessage_MessageType_MIN, 0, 0}
|
#define HardwareMessage_init_zero {_HardwareMessage_Type_MIN, 0, 0}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define HardwareMessage_typ_tag 1
|
#define HardwareMessage_typ_tag 1
|
||||||
|
@ -28,22 +28,17 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
|
|||||||
|
|
||||||
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
|
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
|
||||||
{
|
{
|
||||||
User &u = owner;
|
MeshPacket *p = allocReply();
|
||||||
|
|
||||||
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
|
||||||
MeshPacket *p = allocForSending(u);
|
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
service.sendToMesh(p);
|
service.sendToMesh(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MeshPacket *NodeInfoPlugin::allocReply()
|
||||||
|
{
|
||||||
|
User &u = owner;
|
||||||
|
|
||||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
||||||
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
return allocDataProtobuf(u);
|
||||||
* is optional
|
}
|
||||||
*/
|
|
||||||
void NodeInfoPlugin::sendResponse(NodeNum to) {
|
|
||||||
DEBUG_MSG("Sending user reply\n");
|
|
||||||
sendOurNodeInfo(to, false);
|
|
||||||
}
|
|
||||||
|
@ -25,10 +25,8 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
|
|||||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p);
|
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p);
|
||||||
|
|
||||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
/** 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
|
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||||
* is optional
|
virtual MeshPacket *allocReply();
|
||||||
*/
|
|
||||||
virtual void sendResponse(NodeNum to);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NodeInfoPlugin nodeInfoPlugin;
|
extern NodeInfoPlugin nodeInfoPlugin;
|
@ -28,7 +28,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
|
|||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
MeshPacket *PositionPlugin::allocReply()
|
||||||
{
|
{
|
||||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
assert(node);
|
assert(node);
|
||||||
@ -38,18 +38,15 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||||||
auto position = node->position;
|
auto position = node->position;
|
||||||
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||||
|
|
||||||
MeshPacket *p = allocForSending(position);
|
return allocDataProtobuf(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||||
|
{
|
||||||
|
MeshPacket *p = allocReply();
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
service.sendToMesh(p);
|
service.sendToMesh(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. Implementing this method
|
|
||||||
* is optional
|
|
||||||
*/
|
|
||||||
void PositionPlugin::sendResponse(NodeNum to) {
|
|
||||||
DEBUG_MSG("Sending posistion reply\n");
|
|
||||||
sendOurPosition(to, false);
|
|
||||||
}
|
|
@ -26,10 +26,8 @@ class PositionPlugin : public ProtobufPlugin<Position>
|
|||||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position &p);
|
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position &p);
|
||||||
|
|
||||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
/** 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
|
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||||
* is optional
|
virtual MeshPacket *allocReply();
|
||||||
*/
|
|
||||||
virtual void sendResponse(NodeNum to);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PositionPlugin positionPlugin;
|
extern PositionPlugin positionPlugin;
|
@ -8,10 +8,53 @@
|
|||||||
|
|
||||||
RemoteHardwarePlugin remoteHardwarePlugin;
|
RemoteHardwarePlugin remoteHardwarePlugin;
|
||||||
|
|
||||||
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p)
|
#define NUM_GPIOS 64
|
||||||
|
|
||||||
|
// A macro for clearing a struct, FIXME, move elsewhere
|
||||||
|
#define CLEAR_STRUCT(r) memset(&r, 0, sizeof(r))
|
||||||
|
|
||||||
|
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p)
|
||||||
{
|
{
|
||||||
|
switch (p.typ) {
|
||||||
|
case HardwareMessage_Type_WRITE_GPIOS:
|
||||||
|
// Print notification to LCD screen
|
||||||
|
screen->print("Write GPIOs");
|
||||||
|
|
||||||
return false; // Let others look at this message also if they want
|
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
|
||||||
|
uint64_t mask = 1 << i;
|
||||||
|
if (p.gpio_mask & mask) {
|
||||||
|
digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
|
||||||
|
pinMode(i, OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HardwareMessage_Type_READ_GPIOS: {
|
||||||
|
// Print notification to LCD screen
|
||||||
|
screen->print("Read GPIOs");
|
||||||
|
|
||||||
|
uint64_t res = 0;
|
||||||
|
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
|
||||||
|
uint64_t mask = 1 << i;
|
||||||
|
if (p.gpio_mask & mask) {
|
||||||
|
pinMode(i, INPUT_PULLUP);
|
||||||
|
if (digitalRead(i))
|
||||||
|
res |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the reply
|
||||||
|
HardwareMessage reply;
|
||||||
|
CLEAR_STRUCT(reply);
|
||||||
|
reply.typ = HardwareMessage_Type_READ_GPIOS_REPLY;
|
||||||
|
reply.gpio_value = res;
|
||||||
|
MeshPacket *p = allocDataProtobuf(reply);
|
||||||
|
setReplyTo(p, req);
|
||||||
|
service.sendToMesh(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true; // handled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user