mirror of
https://gitlab.com/wirelos/sprocket-lib.git
synced 2025-12-14 12:46:50 +01:00
Merge branch 'mediator' into develop
This commit is contained in:
@@ -37,9 +37,7 @@ firmware-build:
|
||||
- pio run -t clean
|
||||
- pio run --environment basic
|
||||
- pio run --environment mesh
|
||||
- pio run --environment meshPixel
|
||||
- pio run --environment meshMqttBridge
|
||||
- pio run --environment ota
|
||||
artifacts:
|
||||
paths:
|
||||
- .pioenvs/*/firmware.*
|
||||
|
||||
@@ -22,6 +22,8 @@ lib_deps =
|
||||
ESPAsyncTCP
|
||||
TaskScheduler
|
||||
SPIFFS
|
||||
ESP8266mDNS
|
||||
ArduinoOTA
|
||||
|
||||
;[env:build]
|
||||
;src_filter = +<*> -<examples/>
|
||||
@@ -68,17 +70,4 @@ monitor_baud = ${common.monitor_baud}
|
||||
framework = ${common.framework}
|
||||
lib_deps = ${common.lib_deps}
|
||||
painlessMesh
|
||||
PubSubClient
|
||||
|
||||
[env:ota]
|
||||
src_filter = +<*> +<*/plugins/*> -<examples/> +<examples/ota/>
|
||||
platform = espressif8266
|
||||
board = esp12e
|
||||
upload_speed = ${common.upload_speed}
|
||||
upload_flags = --auth=f4ncy
|
||||
monitor_baud = ${common.monitor_baud}
|
||||
framework = ${common.framework}
|
||||
lib_deps = ${common.lib_deps}
|
||||
ESP8266mDNS
|
||||
painlessMesh
|
||||
ArduinoOTA
|
||||
PubSubClient
|
||||
31
src/Mediator.h
Normal file
31
src/Mediator.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef __MEDIATOR__
|
||||
#define __MEDIATOR__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef std::function<void(String msg)> subscriptionHandler_t;
|
||||
|
||||
class Mediator {
|
||||
public:
|
||||
std::map<std::string, vector<subscriptionHandler_t>> subscriptions;
|
||||
void subscribe(String topic, subscriptionHandler_t handler) {
|
||||
subscriptions[topic.c_str()].reserve(1);
|
||||
subscriptions[topic.c_str()].push_back(handler);
|
||||
}
|
||||
void publish(String topic, String msg) {
|
||||
if (subscriptions.find(topic.c_str()) != subscriptions.end()){
|
||||
for(subscriptionHandler_t h : subscriptions[topic.c_str()]){
|
||||
h(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -32,7 +32,7 @@ Network* MeshNet::init(){
|
||||
Network* MeshNet::connectStation(int doConnect) {
|
||||
if(doConnect){
|
||||
Serial.println("connect station");
|
||||
mesh.stationManual(config.stationSSID, config.stationPassword);
|
||||
mesh.stationManual(config.stationSSID, config.stationPassword, 5555, IPAddress(192,168,1,142));
|
||||
mesh.setHostname(config.hostname.c_str());
|
||||
}
|
||||
return this;
|
||||
|
||||
16
src/Plugin.h
16
src/Plugin.h
@@ -3,14 +3,26 @@
|
||||
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <Network.h>
|
||||
#include <base/MeshMessage.h>
|
||||
#include <base/SprocketMessage.h>
|
||||
#include <Mediator.h>
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
Mediator* mediator;
|
||||
virtual void activate(Scheduler*, Network*);
|
||||
virtual void enable(){};
|
||||
virtual void disable(){};
|
||||
virtual void onMessage(MeshMessage msg){};
|
||||
virtual void onMessage(SprocketMessage msg){};
|
||||
Plugin* mediate(Mediator* m) {
|
||||
mediator = m;
|
||||
return this;
|
||||
}
|
||||
void subscribe(String topic, subscriptionHandler_t handler){
|
||||
mediator->subscribe(topic, handler);
|
||||
}
|
||||
void publish(String topic, String str){
|
||||
mediator->publish(topic, str);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -43,13 +43,15 @@ void Sprocket::loop(){
|
||||
}
|
||||
|
||||
void Sprocket::dispatch( uint32_t from, String &msg ) {
|
||||
MeshMessage mMsg;
|
||||
if(mMsg.fromJsonString(msg)){
|
||||
SprocketMessage mMsg;
|
||||
mMsg.fromJsonString(msg);
|
||||
if(mMsg.valid){
|
||||
dispatchMessageToPlugins(mMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void Sprocket::addPlugin(Plugin* p){
|
||||
p->mediate(this);
|
||||
plugins.reserve(1);
|
||||
plugins.push_back(p);
|
||||
}
|
||||
@@ -60,10 +62,11 @@ void Sprocket::activatePlugins(Scheduler* scheduler, Network* network){
|
||||
}
|
||||
}
|
||||
|
||||
void Sprocket::dispatchMessageToPlugins(MeshMessage msg){
|
||||
if(msg.type != MeshMessage::NONE){ // baaaa
|
||||
// TODO remove plugin dispatching and onMessage in favor of mediator pub/sub
|
||||
void Sprocket::dispatchMessageToPlugins(SprocketMessage msg){
|
||||
if(msg.type != SprocketMessage::NONE){ // baaaa
|
||||
for(Plugin* p : plugins){
|
||||
Serial.println("dispatch to plugins");
|
||||
//Serial.println("dispatch to plugins");
|
||||
p->onMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ using namespace std::placeholders;
|
||||
// FIXME move to some global fnc lib
|
||||
#define ARRAY_LENGTH(array) sizeof(array)/sizeof(array[0])
|
||||
|
||||
class Sprocket {
|
||||
class Sprocket : public Mediator {
|
||||
protected:
|
||||
Scheduler scheduler;
|
||||
public:
|
||||
@@ -37,7 +37,7 @@ class Sprocket {
|
||||
|
||||
void addPlugin(Plugin* p);
|
||||
void activatePlugins(Scheduler* scheduler, Network* network);
|
||||
void dispatchMessageToPlugins(MeshMessage msg);
|
||||
void dispatchMessageToPlugins(SprocketMessage msg);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef __MESH_MESSAGE__
|
||||
#define __MESH_MESSAGE__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#define JSON_DOMAIN "domain"
|
||||
#define JSON_FROM "from"
|
||||
#define JSON_TO "target"
|
||||
#define JSON_MSG "msg"
|
||||
#define JSON_TYPE "type"
|
||||
|
||||
struct MeshMessage {
|
||||
String domain;
|
||||
String to;
|
||||
String from;
|
||||
String msg;
|
||||
enum MeshMessageType { NONE, SYSTEM, APP, OTA } type;
|
||||
int valid = 0;
|
||||
// ------------------------------------------------------------------------------------------
|
||||
//void init() {
|
||||
// from = reinterpret_cast<char*>(ESP.getChipId());
|
||||
//}
|
||||
int verifyJsonObject(JsonObject& json){
|
||||
return json.success();
|
||||
//&& json.containsKey(JSON_DOMAIN)
|
||||
//&& json.containsKey(JSON_TO)
|
||||
//&& json.containsKey(JSON_FROM)
|
||||
//&& json.containsKey(JSON_TYPE)
|
||||
// && json.containsKey(JSON_MSG); // msg is only tx'ed from mqtt
|
||||
};
|
||||
String toJsonString(){
|
||||
//StaticJsonBuffer<200> jsonBuffer;
|
||||
DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(300));
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root[JSON_DOMAIN] = domain;
|
||||
root[JSON_TO] = to;
|
||||
root[JSON_FROM] = from;
|
||||
root[JSON_MSG] = msg;
|
||||
root[JSON_TYPE] = type;
|
||||
String jsonString;
|
||||
root.printTo(jsonString);
|
||||
return jsonString;
|
||||
}
|
||||
String getAttrFromJson(JsonObject& json, const char* attr){
|
||||
if(json.containsKey(attr)){
|
||||
return json[attr];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
int getIntAttrFromJson(JsonObject& json, const char* attr){
|
||||
if(json.containsKey(attr)){
|
||||
return json[attr];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// Map a json object to this struct.
|
||||
int fromJsonObject(JsonObject& json){
|
||||
if(!verifyJsonObject(json)){
|
||||
Serial.println("ERROR: cannot parse MeshMessage JSON object");
|
||||
valid = 0;
|
||||
return valid;
|
||||
}
|
||||
domain = getAttrFromJson(json, JSON_DOMAIN);
|
||||
to = getAttrFromJson(json, JSON_TO);
|
||||
from = getAttrFromJson(json, JSON_FROM);
|
||||
msg = getAttrFromJson(json, JSON_MSG);
|
||||
type = (MeshMessageType) getIntAttrFromJson(json, JSON_TYPE);
|
||||
valid = 1;
|
||||
return valid;
|
||||
};
|
||||
// Parse a json string and map parsed object
|
||||
int fromJsonString(String& str){
|
||||
//StaticJsonBuffer<200> jsonBuffer;
|
||||
DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(300));
|
||||
JsonObject& json = jsonBuffer.parseObject(str);
|
||||
return fromJsonObject(json);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <painlessMesh.h>
|
||||
#include <Sprocket.h>
|
||||
#include <MeshNet.h>
|
||||
#include <base/MeshMessage.h>
|
||||
#include <base/SprocketMessage.h>
|
||||
#include <base/MeshSprocketConfig.h>
|
||||
#include "config.h"
|
||||
#include "utils_print.h"
|
||||
@@ -18,6 +18,7 @@ class MeshSprocket : public Sprocket {
|
||||
public:
|
||||
MeshNet* net;
|
||||
|
||||
MeshSprocket(){};
|
||||
MeshSprocket(SprocketConfig cfg) : Sprocket(cfg) {
|
||||
|
||||
}
|
||||
@@ -30,13 +31,13 @@ class MeshSprocket : public Sprocket {
|
||||
return this;
|
||||
} using Sprocket::activate;
|
||||
|
||||
virtual void onMessage(uint32_t from, String &msg) {
|
||||
Serial.printf("MeshSprocket onMessage: received from %u msg=%s\n", from, msg.c_str());
|
||||
};
|
||||
|
||||
void dispatch( uint32_t from, String &msg ) {
|
||||
Sprocket::dispatch(from, msg);
|
||||
onMessage(from, msg);
|
||||
SprocketMessage sMsg;
|
||||
sMsg.fromJsonString(msg);
|
||||
if(sMsg.valid){
|
||||
sMsg.from = from;
|
||||
publish(sMsg.topic, sMsg.payload);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
59
src/base/SprocketMessage.h
Normal file
59
src/base/SprocketMessage.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __MESH_MESSAGE__
|
||||
#define __MESH_MESSAGE__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <JsonStruct.h>
|
||||
|
||||
#define JSON_DOMAIN "domain"
|
||||
#define JSON_FROM "from"
|
||||
#define JSON_TO "target"
|
||||
#define JSON_PAYLOAD "payload"
|
||||
#define JSON_TYPE "type"
|
||||
#define JSON_TOPIC "topic"
|
||||
|
||||
struct SprocketMessage : public JsonStruct {
|
||||
String domain;
|
||||
String to;
|
||||
String from;
|
||||
String payload;
|
||||
String topic;
|
||||
|
||||
// TODO do we even need that?
|
||||
enum SprocketMessageType { NONE, SYSTEM, APP, OTA } type;
|
||||
// ------------------------------------------------------------------------------------------
|
||||
int verifyJsonObject(JsonObject& json){
|
||||
return json.success()
|
||||
//&& json.containsKey(JSON_DOMAIN)
|
||||
//&& json.containsKey(JSON_TO)
|
||||
//&& json.containsKey(JSON_FROM)
|
||||
//&& json.containsKey(JSON_TYPE)
|
||||
&& json.containsKey(JSON_TOPIC)
|
||||
&& json.containsKey(JSON_PAYLOAD);
|
||||
};
|
||||
void mapJsonObject(JsonObject& root){
|
||||
root[JSON_DOMAIN] = domain;
|
||||
root[JSON_TO] = to;
|
||||
root[JSON_FROM] = from;
|
||||
root[JSON_PAYLOAD] = payload;
|
||||
root[JSON_TOPIC] = topic;
|
||||
root[JSON_TYPE] = type;
|
||||
}
|
||||
// Map a json object to this struct.
|
||||
void fromJsonObject(JsonObject& json){
|
||||
if(!verifyJsonObject(json)){
|
||||
Serial.println("ERROR: cannot parse SprocketMessage JSON object");
|
||||
valid = 0;
|
||||
return;
|
||||
}
|
||||
domain = getAttrFromJson(json, JSON_DOMAIN);
|
||||
to = getAttrFromJson(json, JSON_TO);
|
||||
from = getAttrFromJson(json, JSON_FROM);
|
||||
payload = getAttrFromJson(json, JSON_PAYLOAD);
|
||||
topic = getAttrFromJson(json, JSON_TOPIC);
|
||||
type = (SprocketMessageType) getIntAttrFromJson(json, JSON_TYPE);
|
||||
valid = 1;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <plugins/WebServerPlugin.cpp>
|
||||
#include <plugins/WebConfigPlugin.cpp>
|
||||
#include <plugins/MeshManPlugin.cpp>
|
||||
#include "Mediator.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
@@ -18,12 +19,13 @@ AsyncWebServer WEBSERVER(80);
|
||||
class MeshApp : public MeshSprocket {
|
||||
public:
|
||||
Task heartbeatTask;
|
||||
|
||||
|
||||
MeshApp(SprocketConfig cfg, OtaConfig otaCfg, WebServerConfig webCfg) : MeshSprocket(cfg) {
|
||||
addPlugin(new OtaTcpPlugin(otaCfg));
|
||||
addPlugin(new WebServerPlugin(webCfg, &WEBSERVER));
|
||||
addPlugin(new WebConfigPlugin(&WEBSERVER));
|
||||
addPlugin(new MeshManPlugin(&WEBSERVER));
|
||||
subscribe("mesh/heartbeat", bind(&MeshApp::messageHandler, this, _1));
|
||||
}
|
||||
|
||||
Sprocket* activate(Scheduler* scheduler, Network* network) {
|
||||
@@ -37,18 +39,25 @@ class MeshApp : public MeshSprocket {
|
||||
return this;
|
||||
} using MeshSprocket::activate;
|
||||
|
||||
void messageHandler(String msg){
|
||||
Serial.println(String("MeshApp: ") + msg);
|
||||
}
|
||||
|
||||
void heartbeat(MeshNet* network){
|
||||
MeshMessage msg; // = { "wirelos", "broadcast", "local", "alive", 0, };
|
||||
SprocketMessage msg; // = { "wirelos", "broadcast", "local", "alive", 0, };
|
||||
msg.domain = "wirelos";
|
||||
msg.to = "broadcast";
|
||||
msg.msg = "alive";
|
||||
msg.type = MeshMessage::APP;
|
||||
msg.payload = "alive";
|
||||
msg.topic = "mesh/heartbeat";
|
||||
msg.type = SprocketMessage::APP;
|
||||
String msgStr = msg.toJsonString();
|
||||
network->mesh.sendBroadcast(msgStr/* , true */);
|
||||
}
|
||||
void onMessage( uint32_t from, String &msg ) {
|
||||
Serial.printf("MeshApp onMessage: received from %u msg=%s\n", from, msg.c_str());
|
||||
network->mesh.sendBroadcast(msgStr, true);
|
||||
//String mMsg = String("hoi");
|
||||
//publish("mediatorMsg", "hi mediator");
|
||||
}
|
||||
//void onMessage( uint32_t from, String &msg ) {
|
||||
// Serial.printf("MeshApp onMessage: received from %u msg=%s\n", from, msg.c_str());
|
||||
//}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef __MESH_APP__
|
||||
#define __MESH_APP__
|
||||
|
||||
#define DEBUG_ESP_OTA
|
||||
#include <MeshNet.h>
|
||||
#include <base/MeshSprocket.h>
|
||||
#include "config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
|
||||
// MeshSprocket base class integrates OTA plugin by default
|
||||
class SprocketOTA : public MeshSprocket {
|
||||
public:
|
||||
SprocketOTA(SprocketConfig cfg, OtaConfig otaCfg) : MeshSprocket(cfg, otaCfg) {}
|
||||
|
||||
Sprocket* activate(Scheduler* scheduler, Network* network) {
|
||||
// call parent method that enables dispatching and plugins
|
||||
MeshSprocket::activate(scheduler, network);
|
||||
return this;
|
||||
} using MeshSprocket::activate;
|
||||
|
||||
void onMessage( uint32_t from, String &msg ) {
|
||||
Serial.printf("SprocketOTA onMessage: received from %u msg=%s\n", from, msg.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef __OTA_NODE__CONFIG__
|
||||
#define __OTA_NODE__CONFIG__
|
||||
|
||||
// Scheduler config
|
||||
#define _TASK_SLEEP_ON_IDLE_RUN
|
||||
#define _TASK_STD_FUNCTION
|
||||
|
||||
// Chip config
|
||||
#define SERIAL_BAUD_RATE 115200
|
||||
#define STARTUP_DELAY 3000
|
||||
|
||||
// Mesh config
|
||||
#define STATION_MODE 1 // must be 1 => connected to a normal network to receive update
|
||||
#define WIFI_CHANNEL 11
|
||||
#define MESH_PORT 5555
|
||||
#define MESH_PREFIX "whateverYouLike"
|
||||
#define MESH_PASSWORD "somethingSneaky"
|
||||
#define STATION_SSID "Th1ngs4P"
|
||||
#define STATION_PASSWORD "th3r31sn0sp00n"
|
||||
#define HOSTNAME "ota-node"
|
||||
#define MESH_DEBUG_TYPES ERROR | STARTUP | CONNECTION
|
||||
|
||||
// OTA config
|
||||
#define OTA_PORT 8266
|
||||
#define OTA_PASSWORD "f4ncy"
|
||||
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
#include <ArduinoOTA.h>
|
||||
#include "config.h"
|
||||
#include "MeshNet.h"
|
||||
#include "SprocketOTA.cpp"
|
||||
|
||||
MeshNet net({
|
||||
STATION_MODE, WIFI_CHANNEL,
|
||||
MESH_PORT, MESH_PREFIX, MESH_PASSWORD,
|
||||
STATION_SSID, STATION_PASSWORD, HOSTNAME,
|
||||
MESH_DEBUG_TYPES
|
||||
});
|
||||
|
||||
SprocketOTA sprocket(
|
||||
{ STARTUP_DELAY, SERIAL_BAUD_RATE },
|
||||
{OTA_PORT, OTA_PASSWORD}
|
||||
);
|
||||
|
||||
void setup() {
|
||||
sprocket.join(net);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
sprocket.loop();
|
||||
yield();
|
||||
}
|
||||
@@ -27,6 +27,11 @@ class MeshManPlugin : public Plugin {
|
||||
server->on("/mesh", HTTP_POST, std::bind(&MeshManPlugin::sendMsg, this, std::placeholders::_1));
|
||||
server->on("/mesh/nodeId", HTTP_GET, std::bind(&MeshManPlugin::getNodeId, this, std::placeholders::_1));
|
||||
server->on("/mesh/broadcast", HTTP_POST, std::bind(&MeshManPlugin::broadcast, this, std::placeholders::_1));
|
||||
|
||||
subscribe("mesh/heartbeat", std::bind(&MeshManPlugin::gotHeartbeat, this, std::placeholders::_1));
|
||||
}
|
||||
void gotHeartbeat(String msg){
|
||||
Serial.println(String("MeshManPlugin / Heartbeat: ") + msg);
|
||||
}
|
||||
void getMeshConnections(AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", net->mesh.subConnectionJson());
|
||||
|
||||
@@ -33,8 +33,8 @@ class OtaTcpPlugin : public Plugin {
|
||||
ArduinoOTA.begin();
|
||||
otaTask.enable();
|
||||
}
|
||||
void onMessage(MeshMessage msg) {
|
||||
if(msg.type == MeshMessage::OTA){
|
||||
void onMessage(SprocketMessage msg) {
|
||||
if(msg.type == SprocketMessage::OTA){
|
||||
Serial.println("OTA msg received");
|
||||
WiFi.disconnect();
|
||||
net->mesh.stop();
|
||||
|
||||
Reference in New Issue
Block a user