Compare commits

...

3 Commits

Author SHA1 Message Date
Francois BIOT
e41452edf0 [fixes] Timeout and broker to broker modifications 2023-03-23 13:42:09 +01:00
Francois BIOT
bf84e29831 retain is coming git status! 2023-03-22 00:29:55 +01:00
Francois BIOT
0c7c830a26 Release 0.9.19 2023-03-11 18:51:08 +01:00
5 changed files with 115 additions and 42 deletions

View File

@@ -8,7 +8,7 @@
}, },
"dependencies": "dependencies":
{ "hsaturn/TinyConsole" : "*" }, { "hsaturn/TinyConsole" : "*" },
"version": "0.9.18", "version": "0.9.19",
"exclude": "", "exclude": "",
"examples": "examples/*/*.ino", "examples": "examples/*/*.ino",
"frameworks": "arduino", "frameworks": "arduino",

View File

@@ -1,5 +1,5 @@
name=TinyMqtt name=TinyMqtt
version=0.9.18 version=0.9.19
author=Francois BIOT, HSaturn, <hsaturn@gmail.com> author=Francois BIOT, HSaturn, <hsaturn@gmail.com>
maintainer=Francois BIOT <hsaturn@gmail.com> maintainer=Francois BIOT <hsaturn@gmail.com>
sentence=A tiny broker and client library for MQTT messaging. sentence=A tiny broker and client library for MQTT messaging.

View File

@@ -107,6 +107,8 @@ class IndexedString
index = source.index; index = source.index;
} }
IndexedString(IndexedString&& i) : index(i.index) {}
IndexedString(const char* str, uint8_t len) IndexedString(const char* str, uint8_t len)
{ {
index=StringIndexer::strToIndex(str, len); index=StringIndexer::strToIndex(str, len);

View File

@@ -15,8 +15,10 @@ int TinyMqtt::debug=2;
std::map<MqttMessage::Type, int> MqttClient::counters; std::map<MqttMessage::Type, int> MqttClient::counters;
#endif #endif
MqttBroker::MqttBroker(uint16_t port) MqttBroker::MqttBroker(uint16_t port, uint8_t max_retain_size)
{ {
debug("New broker" << port);
retain_size = max_retain_size;
server = new TcpServer(port); server = new TcpServer(port);
#ifdef TINY_MQTT_ASYNC #ifdef TINY_MQTT_ASYNC
server->onClient(onClient, this); server->onClient(onClient, this);
@@ -142,6 +144,7 @@ void MqttBroker::connect(const string& host, uint16_t port)
if (remote_broker == nullptr) remote_broker = new MqttClient; if (remote_broker == nullptr) remote_broker = new MqttClient;
remote_broker->connect(host, port); remote_broker->connect(host, port);
remote_broker->local_broker = this; // Because connect removed the link remote_broker->local_broker = this; // Because connect removed the link
// TODO shouldn't we resubscribe to all client subscriptions ?
} }
void MqttBroker::removeClient(MqttClient* remove) void MqttBroker::removeClient(MqttClient* remove)
@@ -211,9 +214,19 @@ void MqttBroker::loop()
} }
} }
MqttError MqttBroker::subscribe(const Topic& topic, uint8_t qos) // Obvioulsy called when the broker is connected to another broker.
MqttError MqttBroker::subscribe(MqttClient* client, const Topic& topic, uint8_t qos)
{ {
debug("MqttBroker::subscribe"); debug("MqttBroker::subscribe to " << topic.str() << ", retained=" << retained.size() );
for(auto& [retained_topic, retain]: retained)
{
debug(" retained: " << retained_topic.str());
if (topic.matches(retained_topic))
{
debug(" -> sending");
client->publishIfSubscribed(retained_topic, retain.msg);
}
}
if (remote_broker && remote_broker->connected()) if (remote_broker && remote_broker->connected())
{ {
return remote_broker->subscribe(topic, qos); return remote_broker->subscribe(topic, qos);
@@ -221,10 +234,12 @@ MqttError MqttBroker::subscribe(const Topic& topic, uint8_t qos)
return MqttNowhereToSend; return MqttNowhereToSend;
} }
MqttError MqttBroker::publish(const MqttClient* source, const Topic& topic, MqttMessage& msg) const MqttError MqttBroker::publish(const MqttClient* source, const Topic& topic, MqttMessage& msg)
{ {
MqttError retval = MqttOk; MqttError retval = MqttOk;
retain(topic, msg);
debug("MqttBroker::publish"); debug("MqttBroker::publish");
int i=0; int i=0;
for(auto client: clients) for(auto client: clients)
@@ -294,25 +309,26 @@ void MqttClient::clientAlive(uint32_t more_seconds)
void MqttClient::loop() void MqttClient::loop()
{ {
if (keep_alive && (millis() >= alive)) if (keep_alive && (millis() >= alive - 5000))
{ {
if (local_broker) if (tcp_client && tcp_client->connected())
{
debug(red << "timeout client");
close();
debug(red << "closed");
}
else if (tcp_client && tcp_client->connected())
{ {
debug("pingreq"); debug("pingreq");
uint16_t pingreq = MqttMessage::Type::PingReq; static MqttMessage pingreq(MqttMessage::Type::PingReq);
tcp_client->write((const char*)(&pingreq), 2); pingreq.sendTo(this);
clientAlive(0); clientAlive(0);
// TODO when many MqttClient passes through a local broker // TODO when many MqttClient passes through a local broker
// there is no need to send one PingReq per instance. // there is no need to send one PingReq per instance.
} }
else if (local_broker)
{
debug(red << "timeout client");
close();
debug(red << "closed");
} }
}
#ifndef TINY_MQTT_ASYNC #ifndef TINY_MQTT_ASYNC
while(tcp_client && tcp_client->available()>0) while(tcp_client && tcp_client->available()>0)
{ {
@@ -391,13 +407,13 @@ MqttError MqttClient::subscribe(Topic topic, uint8_t qos)
subscriptions.insert(topic); subscriptions.insert(topic);
if (local_broker==nullptr) // remote broker if (local_broker==nullptr) // connected to a remote broker
{ {
return sendTopic(topic, MqttMessage::Type::Subscribe, qos); return sendTopic(topic, MqttMessage::Type::Subscribe, qos);
} }
else else
{ {
return local_broker->subscribe(topic, qos); return local_broker->subscribe(this, topic, qos);
} }
return ret; return ret;
} }
@@ -568,7 +584,7 @@ void MqttClient::processMessage(MqttMessage* mesg)
} }
else else
qoss.push_back(qos); qoss.push_back(qos);
subscriptions.insert(topic); subscribe(topic);
} }
else else
{ {
@@ -618,8 +634,8 @@ void MqttClient::processMessage(MqttMessage* mesg)
#if TINY_MQTT_DEBUG #if TINY_MQTT_DEBUG
if (TinyMqtt::debug >= 2) if (TinyMqtt::debug >= 2)
{ {
Console << (isSubscribedTo(published) ? "not" : "") << " subscribed.\n"; Console << (isSubscribedTo(published) ? "not" : "") << " subscribed.\r\n";
Console << "has " << (callback ? "" : "no ") << " callback.\n"; Console << "has " << (callback ? "" : "no ") << " callback.\r\n";
} }
#endif #endif
if (callback and isSubscribedTo(published)) if (callback and isSubscribedTo(published))
@@ -728,9 +744,9 @@ bool Topic::matches(const Topic& topic) const
// publish from local client // publish from local client
MqttError MqttClient::publish(const Topic& topic, const char* payload, size_t pay_length) MqttError MqttClient::publish(const Topic& topic, const char* payload, size_t pay_length, bool retain)
{ {
MqttMessage msg(MqttMessage::Publish); MqttMessage msg(MqttMessage::Publish, retain ? 1 : 0);
msg.add(topic); msg.add(topic);
msg.add(payload, pay_length, false); msg.add(payload, pay_length, false);
msg.complete(); msg.complete();
@@ -894,6 +910,35 @@ MqttError MqttMessage::sendTo(MqttClient* client)
return MqttOk; return MqttOk;
} }
void MqttBroker::retainDrop()
{
if (retained.size() >= retain_size)
{
std::map<Topic, Retain>::iterator oldest = retained.begin();
auto it = oldest;
while(++it != retained.end())
{
if (oldest->second.timestamp > it->second.timestamp)
oldest = it;
}
retained.erase(oldest);
}
}
void MqttBroker::retain(const Topic& topic, const MqttMessage& msg)
{
debug("MqttBroker::retain msg_type=" << _HEX(msg.type()));
if (retain_size==0 or msg.type() != MqttMessage::Publish) return;
if (msg.flags() & 1) // flag RETAIN
{
debug(" retaining " << topic.str());
if (retained.find(topic) == retained.end()) retainDrop();
// FIXME if payload size == 0 remove message from retained
Retain r(micros(), msg);
retained.insert({ topic, std::move(r)});
}
}
void MqttMessage::hexdump(const char* prefix) const void MqttMessage::hexdump(const char* prefix) const
{ {
(void)prefix; (void)prefix;

View File

@@ -121,7 +121,10 @@ class MqttMessage
return (*bun << 8) | bun[1]; } return (*bun << 8) | bun[1]; }
MqttMessage() { reset(); } MqttMessage() { reset(); }
MqttMessage(Type t, uint8_t bits_d3_d0=0) { create(t); buffer[0] |= bits_d3_d0; } MqttMessage(Type t, uint8_t bits_d3_d0=0) { create(t); buffer[0] |= (bits_d3_d0 & 0xF); }
MqttMessage(const MqttMessage& m)
: buffer(m.buffer), vheader(m.vheader), size(m.size), state(m.state) {}
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, bool addLength=true ); void add(const char* p, size_t len, bool addLength=true );
@@ -156,6 +159,15 @@ class MqttMessage
MqttError sendTo(MqttClient*); MqttError sendTo(MqttClient*);
void hexdump(const char* prefix=nullptr) const; void hexdump(const char* prefix=nullptr) const;
MqttMessage& operator = (MqttMessage&& m)
{
buffer = std::move(m.buffer);
vheader = m.vheader;
size = m.size;
state = m.state;
return *this;
}
private: private:
void encodeLength(); void encodeLength();
@@ -228,11 +240,11 @@ class MqttClient
}; };
// Publish from client to the world // Publish from client to the world
MqttError publish(const Topic&, const char* payload, size_t pay_length); MqttError publish(const Topic&, const char* payload, size_t pay_length, bool retain=false);
MqttError publish(const Topic& t, const char* payload) { return publish(t, payload, strlen(payload)); } MqttError publish(const Topic& t, const char* payload, bool retain=false) { return publish(t, payload, strlen(payload), retain); }
MqttError publish(const Topic& t, const String& s) { return publish(t, s.c_str(), s.length()); } MqttError publish(const Topic& t, const String& s, bool retain=false) { return publish(t, s.c_str(), s.length(), retain); }
MqttError publish(const Topic& t, const string& s) { return publish(t,s.c_str(),s.length());} MqttError publish(const Topic& t, const string& s, bool retain=false) { return publish(t,s.c_str(),s.length(), retain);}
MqttError publish(const Topic& t) { return publish(t, nullptr, 0);}; MqttError publish(const Topic& t, bool retain=false) { return publish(t, nullptr, 0, retain);};
MqttError subscribe(Topic topic, uint8_t qos=0); MqttError subscribe(Topic topic, uint8_t qos=0);
MqttError unsubscribe(Topic topic); MqttError unsubscribe(Topic topic);
@@ -317,15 +329,9 @@ class MqttClient
class MqttBroker class MqttBroker
{ {
enum __attribute__((packed)) State
{
Disconnected, // Also the initial state
Connecting, // connect and sends a fake publish to avoid circular cnx
Connected, // this->broker is connected and circular cnx avoided
};
public: public:
// TODO limit max number of clients // TODO limit max number of clients
MqttBroker(uint16_t port); MqttBroker(uint16_t port, uint8_t retain_size=0);
~MqttBroker(); ~MqttBroker();
void begin() { server->begin(); } void begin() { server->begin(); }
@@ -334,9 +340,10 @@ class MqttBroker
/** Connect the broker to a parent broker */ /** Connect the broker to a parent broker */
void connect(const string& host, uint16_t port=1883); void connect(const string& host, uint16_t port=1883);
/** returns true if connected to another broker */ /** returns true if connected to another broker */
bool connected() const { return state == Connected; } bool connected() const { return remote_broker ? remote_broker->connected() : false; }
size_t clientsCount() const { return clients.size(); } size_t clientsCount() const { return clients.size(); }
void retain(uint8_t size) { retain_size = size; }
void dump(string indent="") void dump(string indent="")
{ {
@@ -356,10 +363,9 @@ class MqttBroker
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); }
MqttError publish(const MqttClient* source, const Topic& topic, MqttMessage& msg);
MqttError publish(const MqttClient* source, const Topic& topic, MqttMessage& msg) const; MqttError subscribe(MqttClient*, const Topic& topic, uint8_t qos);
MqttError subscribe(const Topic& topic, uint8_t qos);
// For clients that are added not by the broker itself (local clients) // For clients that are added not by the broker itself (local clients)
void addClient(MqttClient* client); void addClient(MqttClient* client);
@@ -375,5 +381,25 @@ class MqttBroker
const char* auth_password = "guest"; const char* auth_password = "guest";
MqttClient* remote_broker = nullptr; MqttClient* remote_broker = nullptr;
State state = Disconnected; void retain(const Topic& topic, const MqttMessage& msg);
void retainDrop();
struct Retain
{
Retain(unsigned long ts, const MqttMessage& m) : timestamp(ts), msg(m) {}
Retain(Retain&& r) : timestamp(r.timestamp), msg(std::move(r.msg)) {}
Retain& operator=(Retain&& r)
{
timestamp = r.timestamp;
msg = std::move(r.msg);
return *this;
}
unsigned long timestamp;
MqttMessage msg;
};
std::map<Topic, Retain> retained;
uint8_t retain_size;
}; };