Version 0.2

This commit is contained in:
hsaturn
2021-03-19 19:02:40 +01:00
parent bb2a2e6737
commit b33c9ba687
5 changed files with 159 additions and 40 deletions

View File

@@ -16,7 +16,6 @@ void setup()
WiFi.begin(ssid, password); WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial << '.'; Serial << '.';
delay(500); delay(500);
} }

View File

@@ -0,0 +1,32 @@
#include <ESP8266WiFi.h>
#include "TinyMqtt.h" // https://github.com/hsaturn/TinyMqtt
#include <Streaming.h> // https://github.com/janelia-arduino/Streaming
const char *ssid = ; // Put here your wifi SSID ("ssid")
const char *password = ; // Put here your Wifi password ("pwd")
#define PORT 1883
MqttBroker broker(PORT);
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial << '.';
delay(500);
}
Serial << "Connected to " << ssid << "IP address: " << WiFi.localIP() << endl;
broker.begin();
Serial << "Broker ready : " << WiFi.localIP() << " on port " << PORT << endl;
}
void loop()
{
broker.loop();
}

View File

@@ -3,16 +3,23 @@
####################################### #######################################
####################################### #######################################
# Datatypes (KEYWORD1) # Datatypes and functions
####################################### #######################################
TinyMqtt KEYWORD1
MqttBroker KEYWORD1 MqttBroker KEYWORD1
begin KEYWORD2
loop KEYWORD2
MqttClient KEYWORD1 MqttClient KEYWORD1
publish KEYWORD2
setCallback KEYWORD2
subscribe KEYWORD2
####################################### Topic KEYWORD1
# Methods and Functions (KEYWORD2) matches KEYWORD2
####################################### c_str KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)

View File

@@ -15,20 +15,29 @@ MqttBroker::MqttBroker(uint16_t port)
{ {
} }
MqttCnx::MqttCnx(MqttBroker* parent, WiFiClient& new_client) MqttClient::MqttClient(MqttBroker* parent, WiFiClient& new_client)
: parent(parent), : parent(parent),
mqtt_connected(false) mqtt_connected(false)
{ {
client = new_client ? new WiFiClient(new_client) : nullptr; client = new_client ? new WiFiClient(new_client) : nullptr;
clientAlive(); alive = millis()+5000; // client expires after 5s if no CONNECT msg
} }
MqttCnx::~MqttCnx() MqttClient::MqttClient(MqttBroker* parent)
: parent(parent)
{
client = nullptr;
parent->addClient(this);
}
MqttClient::~MqttClient()
{ {
close(); close();
parent->removeClient(this);
} }
void MqttCnx::close() void MqttClient::close()
{ {
if (client) if (client)
{ {
@@ -38,13 +47,32 @@ void MqttCnx::close()
} }
} }
void MqttBroker::addClient(MqttClient* client)
{
clients.push_back(client);
}
void MqttBroker::removeClient(MqttClient* remove)
{
for(auto it=clients.begin(); it!=clients.end(); it++)
{
auto client=*it;
if (client==remove)
{
clients.erase(it);
return;
}
}
Serial << "Error cannot remove client" << endl; // TODO should not occur
}
void MqttBroker::loop() void MqttBroker::loop()
{ {
WiFiClient client = server.available(); WiFiClient client = server.available();
if (client) if (client)
{ {
clients.push_back(new MqttCnx(this, client)); addClient(new MqttClient(this, client));
Serial << "New client (" << clients.size() << ')' << endl; Serial << "New client (" << clients.size() << ')' << endl;
} }
@@ -58,7 +86,7 @@ void MqttBroker::loop()
else else
{ {
Serial << "Client " << client->id().c_str() << " Disconnected" << endl; Serial << "Client " << client->id().c_str() << " Disconnected" << endl;
clients.erase(it); // Note: deleting a client not added by the broker itself will probably crash later.
delete client; delete client;
break; break;
} }
@@ -67,6 +95,7 @@ void MqttBroker::loop()
void MqttBroker::publish(const Topic& topic, MqttMessage& msg) void MqttBroker::publish(const Topic& topic, MqttMessage& msg)
{ {
Serial << " publish" << __LINE__ << endl;
for(auto client: clients) for(auto client: clients)
client->publish(topic, msg); client->publish(topic, msg);
} }
@@ -87,7 +116,7 @@ void MqttMessage::getString(char* &buffer, uint16_t& len)
buffer+=2; buffer+=2;
} }
void MqttCnx::clientAlive() void MqttClient::clientAlive()
{ {
if (keep_alive) if (keep_alive)
{ {
@@ -97,7 +126,7 @@ void MqttCnx::clientAlive()
alive=0; alive=0;
} }
void MqttCnx::loop() void MqttClient::loop()
{ {
if (alive && (millis() > alive)) if (alive && (millis() > alive))
{ {
@@ -115,7 +144,7 @@ void MqttCnx::loop()
} }
} }
void MqttCnx::processMessage() void MqttClient::processMessage()
{ {
std::string error; std::string error;
std::string s; std::string s;
@@ -186,7 +215,7 @@ void MqttCnx::processMessage()
message.getString(payload, len); // Topic message.getString(payload, len); // Topic
outstring("Subscribes", payload, len); outstring("Subscribes", payload, len);
subscriptions.insert(Topic(payload, len)); subscribe(Topic(payload, len));
bclose = false; bclose = false;
// TODO SUBACK // TODO SUBACK
break; break;
@@ -241,15 +270,41 @@ bool Topic::matches(const Topic& topic) const
return false; return false;
} }
void MqttCnx::publish(const Topic& topic, MqttMessage& msg) // publish from local client to a broker
void MqttClient::publish(const Topic& topic, const char* payload, size_t pay_length)
{ {
Serial << " publish" << __LINE__ << endl;
message.create(MqttMessage::Publish);
message.add(topic);
message.add(payload, pay_length);
if (parent)
parent->publish(topic, message);
else if (client)
publish(topic, message);
else
Serial << " Should not happen" << endl;
}
// republish a received publish if it matches any in subscriptions
void MqttClient::publish(const Topic& topic, MqttMessage& msg)
{
Serial << " publish " << topic.c_str() << __LINE__ << endl;
for(const auto& subscription: subscriptions) for(const auto& subscription: subscriptions)
{ {
Serial << " check " << subscription.c_str() << __LINE__ << endl;
if (subscription.matches(topic)) if (subscription.matches(topic))
{
Serial << " matche !" << endl;
if (client)
{ {
// Serial << "Republishing " << topic.str().c_str() << " to " << clientId.c_str() << endl; // Serial << "Republishing " << topic.str().c_str() << " to " << clientId.c_str() << endl;
msg.sendTo(this); msg.sendTo(this);
} }
else if (callback)
{
callback(topic, nullptr, 0); // TODO
}
}
} }
} }
@@ -311,6 +366,11 @@ void MqttMessage::incoming(char in_byte)
} }
} }
void MqttMessage::add(const char* p, size_t len)
{
while(len--) incoming(*p);
}
void MqttMessage::encodeLength(char* msb, int length) void MqttMessage::encodeLength(char* msb, int length)
{ {
do do
@@ -322,7 +382,7 @@ void MqttMessage::encodeLength(char* msb, int length)
} while (length); } while (length);
}; };
void MqttMessage::sendTo(MqttCnx* client) void MqttMessage::sendTo(MqttClient* client)
{ {
if (curr-buffer-2 >= 0) if (curr-buffer-2 >= 0)
{ {

View File

@@ -11,11 +11,14 @@ class Topic : public IndexedString
public: public:
Topic(const char* s, uint8_t len) : IndexedString(s,len){} Topic(const char* s, uint8_t len) : IndexedString(s,len){}
Topic(const char* s) : Topic(s, strlen(s)) {} Topic(const char* s) : Topic(s, strlen(s)) {}
Topic(const std::string s) : Topic(s.c_str(), s.length()){};
const char* c_str() const { return str().c_str(); }
bool matches(const Topic&) const; bool matches(const Topic&) const;
}; };
class MqttCnx; class MqttClient;
class MqttMessage class MqttMessage
{ {
public: public:
@@ -45,6 +48,9 @@ class MqttMessage
MqttMessage(Type t) { create(t); } MqttMessage(Type t) { create(t); }
void incoming(char byte); void incoming(char byte);
void add(char byte) { incoming(byte); } void add(char byte) { incoming(byte); }
void add(const char* p, size_t len);
void add(const std::string& s) { add(s.c_str(), s.length()); }
void add(const Topic& t) { add(t.str()); }
char* getVHeader() const { return vheader; } char* getVHeader() const { return vheader; }
char* end() const { return curr; } char* end() const { return curr; }
uint16_t length() const { return curr-buffer; } uint16_t length() const { return curr-buffer; }
@@ -69,7 +75,7 @@ class MqttMessage
size=0; size=0;
state=Create; state=Create;
} }
void sendTo(MqttCnx*); void sendTo(MqttClient*);
void hexdump(const char* prefix=nullptr) const; void hexdump(const char* prefix=nullptr) const;
private: private:
@@ -83,8 +89,9 @@ class MqttMessage
}; };
class MqttBroker; class MqttBroker;
class MqttCnx class MqttClient
{ {
using CallBack = void (*)(const Topic& topic, const char* payload, size_t payload_length);
enum Flags enum Flags
{ {
FlagUserName = 128, FlagUserName = 128,
@@ -96,11 +103,11 @@ class MqttCnx
FlagReserved = 1 FlagReserved = 1
}; };
public: public:
MqttCnx(MqttBroker* parent, WiFiClient& client); MqttClient(MqttBroker*);
~MqttCnx(); ~MqttClient();
bool connected() { return client && client->connected(); } bool connected() { return client==nullptr || client->connected(); }
void write(const char* buf, size_t length) void write(const char* buf, size_t length)
{ if (client) client->write(buf, length); } { if (client) client->write(buf, length); }
@@ -108,9 +115,22 @@ class MqttCnx
void loop(); void loop();
void close(); void close();
void publish(const Topic& topic, MqttMessage& msg); void setCallback(CallBack fun) {callback=fun; };
// Publish from client to the world
void publish(const Topic&, const char* payload, size_t pay_length);
void publish(const Topic& t, const std::string& s) { publish(t,s.c_str(),s.length());}
void publish(const Topic& t) { publish(t, nullptr, 0);};
void subscribe(Topic topic) { subscriptions.insert(topic); }
void unsubscribe(Topic& topic);
private: private:
friend class MqttBroker;
MqttClient(MqttBroker* parent, WiFiClient& client);
// republish a received publish if topic matches any in subscriptions
void publish(const Topic& topic, MqttMessage& msg);
void clientAlive(); void clientAlive();
void processMessage(); void processMessage();
@@ -118,20 +138,12 @@ class MqttCnx
uint32_t keep_alive; uint32_t keep_alive;
uint32_t alive; uint32_t alive;
bool mqtt_connected; bool mqtt_connected;
WiFiClient* client; WiFiClient* client; // nullptr if this client is local
MqttMessage message; MqttMessage message;
MqttBroker* parent; MqttBroker* parent;
std::set<Topic> subscriptions; std::set<Topic> subscriptions;
std::string clientId; std::string clientId;
}; CallBack callback;
class MqttClient
{
public:
MqttClient(IPAddress broker) : broker_ip(broker) {}
protected:
IPAddress broker_ip;
}; };
class MqttBroker class MqttBroker
@@ -142,17 +154,26 @@ class MqttBroker
void begin() { server.begin(); } void begin() { server.begin(); }
void loop(); void loop();
uint8_t port() const { return server.port(); }
private:
friend class MqttClient;
bool checkUser(const char* user, uint8_t len) const bool checkUser(const char* user, uint8_t len) const
{ return compareString(auth_user, user, len); } { return compareString(auth_user, user, len); }
bool checkPassword(const char* password, uint8_t len) const bool checkPassword(const char* password, uint8_t len) const
{ return compareString(auth_password, password, len); } { return compareString(auth_password, password, len); }
void publish(const Topic& topic, MqttMessage& msg); void publish(const Topic& topic, MqttMessage& msg);
private: // For clients that are added not by the broker itself
void addClient(MqttClient* client);
void removeClient(MqttClient* client);
bool compareString(const char* good, const char* str, uint8_t str_len) const; bool compareString(const char* good, const char* str, uint8_t str_len) const;
std::vector<MqttCnx*> clients; std::vector<MqttClient*> clients;
WiFiServer server; WiFiServer server;
const char* auth_user = "guest"; const char* auth_user = "guest";