Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82c5b971e9 | ||
|
|
01998e74ec | ||
|
|
5f46fd304c | ||
|
|
213d637eaf | ||
|
|
3bb2dd5a81 | ||
|
|
7d9ab6381d |
25
.github/workflows/superlinter.yml
vendored
Normal file
25
.github/workflows/superlinter.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Super-Linter
|
||||
|
||||
# Run this workflow every time a new commit pushed to your repository
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# Set the job key. The key is displayed as the job name
|
||||
# when a job name is not provided
|
||||
super-lint:
|
||||
# Name the Job
|
||||
name: Lint code base
|
||||
# Set the type of machine to run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checks out a copy of your repository on the ubuntu-latest machine
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Runs the Super-Linter action
|
||||
- name: Run Super-Linter
|
||||
uses: github/super-linter@v3
|
||||
env:
|
||||
DEFAULT_BRANCH: main
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -16,11 +16,6 @@
|
||||
* cons - Takes more memory
|
||||
* - a bit hard to understand
|
||||
*
|
||||
* This sounds crazy: a mqtt mqtt that do not need a broker !
|
||||
* The use case arise when one ESP wants to publish topics and subscribe to them at the same time.
|
||||
* Without broker, the ESP won't react to its own topics.
|
||||
*
|
||||
* TinyMqtt mqtt allows this use case to work.
|
||||
*/
|
||||
|
||||
#include <my_credentials.h>
|
||||
@@ -28,18 +23,26 @@
|
||||
std::string topic="sensor/temperature";
|
||||
|
||||
void onPublish(const MqttClient* srce, const Topic& topic, const char* payload, size_t length)
|
||||
{ Serial << "--> " << srce->id().c_str() << ": ======> received " << topic.c_str() << endl; }
|
||||
{
|
||||
Serial << "--> " << srce->id().c_str() << ": ======> received " << topic.c_str();
|
||||
if (payload) Serial << ", payload[" << length << "]=[";
|
||||
while(length--)
|
||||
{
|
||||
const char c=*payload++;
|
||||
if (c!=10 and c!=13 and c <32) Serial << '?';
|
||||
Serial << *payload++;
|
||||
}
|
||||
Serial<< endl;
|
||||
}
|
||||
|
||||
std::map<std::string, MqttClient*> clients;
|
||||
std::map<std::string, MqttBroker*> brokers;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(500);
|
||||
Serial << endl << endl << endl
|
||||
<< "Demo started. Type help for more..." << endl
|
||||
<< "Connecting to '" << ssid << "' ";
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
@@ -49,6 +52,7 @@ void setup()
|
||||
{ Serial << '-'; delay(500); }
|
||||
|
||||
Serial << "Connected to " << ssid << "IP address: " << WiFi.localIP() << endl;
|
||||
Serial << "Type help for more..." << endl;
|
||||
|
||||
MqttBroker* broker = new MqttBroker(1883);
|
||||
broker->begin();
|
||||
@@ -92,7 +96,6 @@ std::string getip(std::string& str, const char* if_empty=nullptr, char sep=' ')
|
||||
std::string addr=getword(str, if_empty, sep);
|
||||
std::string ip=addr;
|
||||
std::vector<std::string> build;
|
||||
bool ok=true;
|
||||
while(ip.length())
|
||||
{
|
||||
std::string b=getword(ip,nullptr,'.');
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/hsaturn/TinyMqtt.git"
|
||||
},
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"exclude": "",
|
||||
"examples": "examples/*/*.ino",
|
||||
"frameworks": "arduino",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name=TinyMqtt
|
||||
version=0.6.0
|
||||
version=0.7.0
|
||||
author=Francois BIOT, HSaturn, <hsaturn@gmail.com>
|
||||
maintainer=Francois BIOT, HSaturn, <hsaturn@gmail.com>
|
||||
sentence=A tiny broker and client library for MQTT messaging.
|
||||
|
||||
@@ -104,7 +104,7 @@ class IndexedString
|
||||
|
||||
const std::string& str() const { return StringIndexer::str(index); }
|
||||
|
||||
const StringIndexer::index_t getIndex() const { return index; }
|
||||
const StringIndexer::index_t& getIndex() const { return index; }
|
||||
|
||||
private:
|
||||
StringIndexer::index_t index;
|
||||
|
||||
@@ -41,14 +41,20 @@ MqttClient::~MqttClient()
|
||||
{
|
||||
close();
|
||||
delete client;
|
||||
Serial << "Client deleted" << endl;
|
||||
}
|
||||
|
||||
void MqttClient::close()
|
||||
void MqttClient::close(bool bSendDisconnect)
|
||||
{
|
||||
debug("close " << id().c_str());
|
||||
mqtt_connected = false;
|
||||
if (client)
|
||||
{
|
||||
if (bSendDisconnect and client->connected())
|
||||
{
|
||||
message.create(MqttMessage::Type::Disconnect);
|
||||
message.sendTo(this);
|
||||
}
|
||||
client->stop();
|
||||
}
|
||||
|
||||
@@ -120,7 +126,7 @@ void MqttBroker::loop()
|
||||
|
||||
// for(auto it=clients.begin(); it!=clients.end(); it++)
|
||||
// use index because size can change during the loop
|
||||
for(int i=0; i<clients.size(); i++)
|
||||
for(size_t i=0; i<clients.size(); i++)
|
||||
{
|
||||
auto client = clients[i];
|
||||
if (client->connected())
|
||||
@@ -447,11 +453,21 @@ if (message.type() != MqttMessage::Type::PingReq && message.type() != MqttMessag
|
||||
{
|
||||
callback(this, published, nullptr, 0); // TODO send the real payload
|
||||
}
|
||||
// TODO should send PUBACK
|
||||
message.create(MqttMessage::Type::PubAck);
|
||||
// TODO re-add packet identifier if any
|
||||
message.sendTo(this);
|
||||
bclose = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case MqttMessage::Type::Disconnect:
|
||||
// TODO should discard any will message
|
||||
if (!mqtt_connected) break;
|
||||
mqtt_connected = false;
|
||||
close(false);
|
||||
bclose=false;
|
||||
break;
|
||||
|
||||
default:
|
||||
bclose=true;
|
||||
break;
|
||||
@@ -602,7 +618,7 @@ void MqttMessage::encodeLength(char* msb, int length)
|
||||
|
||||
MqttError MqttMessage::sendTo(MqttClient* client)
|
||||
{
|
||||
if (buffer.size()>2)
|
||||
if (buffer.size())
|
||||
{
|
||||
debug("sending " << buffer.size() << " bytes");
|
||||
encodeLength(&buffer[1], buffer.size()-2);
|
||||
|
||||
@@ -49,6 +49,7 @@ class MqttMessage
|
||||
UnSubscribe = 0xA0,
|
||||
PingReq = 0xC0,
|
||||
PingResp = 0xD0,
|
||||
Disconnect = 0xE0
|
||||
};
|
||||
enum State
|
||||
{
|
||||
@@ -120,6 +121,7 @@ class MqttClient
|
||||
};
|
||||
public:
|
||||
MqttClient(MqttBroker*);
|
||||
MqttClient(MqttBroker* brk, const std::string& id) : MqttClient(brk) { clientId=id; }
|
||||
MqttClient() : MqttClient(nullptr) {};
|
||||
|
||||
~MqttClient();
|
||||
@@ -137,7 +139,7 @@ class MqttClient
|
||||
void id(std::string& new_id) { clientId = new_id; }
|
||||
|
||||
void loop();
|
||||
void close();
|
||||
void close(bool bSendDisconnect=true);
|
||||
void setCallback(CallBack fun) {callback=fun; };
|
||||
|
||||
// Publish from client to the world
|
||||
@@ -159,7 +161,7 @@ class MqttClient
|
||||
Serial << "MqttClient (" << clientId.c_str() << ") p=" << (int32_t) parent
|
||||
<< " c=" << (int32_t)client << (connected() ? " ON " : " OFF");
|
||||
Serial << ", alive=" << (uint32_t)alive << '/' << ms << ", ka=" << keep_alive;
|
||||
Serial << " cnx " << (client && client->connected());
|
||||
Serial << (client && client->connected() ? "" : "dis") << "connected";
|
||||
message.hexdump("entrant msg");
|
||||
bool c=false;
|
||||
Serial << " [";
|
||||
@@ -224,6 +226,8 @@ class MqttBroker
|
||||
void connect(std::string host, uint32_t port=1883);
|
||||
bool connected() const { return state == Connected; }
|
||||
|
||||
size_t clientsCount() const { return clients.size(); }
|
||||
|
||||
void dump()
|
||||
{
|
||||
Serial << clients.size() << " client/s" << endl;
|
||||
|
||||
138
tests/tinymqtt-tests/tinymqtt-tests.ino
Normal file
138
tests/tinymqtt-tests/tinymqtt-tests.ino
Normal file
@@ -0,0 +1,138 @@
|
||||
#include <AUnit.h>
|
||||
#include <TinyMqtt.h>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* TinyMqtt local unit tests.
|
||||
*
|
||||
* No wifi connection unit tests.
|
||||
**/
|
||||
|
||||
using namespace std;
|
||||
|
||||
MqttBroker broker(1883);
|
||||
|
||||
std::map<std::string, std::map<Topic, int>> published; // map[client_id] => map[topic] = count
|
||||
|
||||
void onPublish(const MqttClient* srce, const Topic& topic, const char* payload, size_t length)
|
||||
{
|
||||
if (srce)
|
||||
published[srce->id()][topic]++;
|
||||
}
|
||||
|
||||
test(local_client_should_unregister_when_destroyed)
|
||||
{
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
{
|
||||
MqttClient client(&broker);
|
||||
assertEqual(broker.clientsCount(), (size_t)1);
|
||||
}
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
}
|
||||
|
||||
test(local_connect)
|
||||
{
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
|
||||
MqttClient client(&broker);
|
||||
assertTrue(client.connected());
|
||||
assertEqual(broker.clientsCount(), (size_t)1);
|
||||
}
|
||||
|
||||
test(local_publish_should_be_dispatched)
|
||||
{
|
||||
published.clear();
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
|
||||
MqttClient subscriber(&broker);
|
||||
subscriber.subscribe("a/b");
|
||||
subscriber.subscribe("a/c");
|
||||
subscriber.setCallback(onPublish);
|
||||
|
||||
MqttClient publisher(&broker);
|
||||
publisher.publish("a/b");
|
||||
publisher.publish("a/c");
|
||||
publisher.publish("a/c");
|
||||
|
||||
assertEqual(published.size(), (size_t)1); // 1 client has received something
|
||||
assertTrue(published[""]["a/b"] == 1);
|
||||
assertTrue(published[""]["a/c"] == 2);
|
||||
}
|
||||
|
||||
test(local_publish_should_be_dispatched_to_local_clients)
|
||||
{
|
||||
published.clear();
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
|
||||
MqttClient subscriber_a(&broker, "A");
|
||||
subscriber_a.setCallback(onPublish);
|
||||
subscriber_a.subscribe("a/b");
|
||||
subscriber_a.subscribe("a/c");
|
||||
|
||||
MqttClient subscriber_b(&broker, "B");
|
||||
subscriber_b.setCallback(onPublish);
|
||||
subscriber_b.subscribe("a/b");
|
||||
|
||||
MqttClient publisher(&broker);
|
||||
publisher.publish("a/b");
|
||||
publisher.publish("a/c");
|
||||
|
||||
assertEqual(published.size(), (size_t)2); // 2 clients have received something
|
||||
assertTrue(published["A"]["a/b"] == 1);
|
||||
assertTrue(published["A"]["a/c"] == 1);
|
||||
assertTrue(published["B"]["a/b"] == 1);
|
||||
assertTrue(published["B"]["a/c"] == 0);
|
||||
}
|
||||
|
||||
test(local_unsubscribe)
|
||||
{
|
||||
published.clear();
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
|
||||
MqttClient subscriber(&broker);
|
||||
subscriber.setCallback(onPublish);
|
||||
subscriber.subscribe("a/b");
|
||||
|
||||
MqttClient publisher(&broker);
|
||||
publisher.publish("a/b");
|
||||
|
||||
// subscriber.unsubscribe("a/b"); TODO not yet implemented
|
||||
|
||||
publisher.publish("a/b");
|
||||
|
||||
assertTrue(published[""]["a/b"] == 1); // Only one publish has been received
|
||||
}
|
||||
|
||||
test(local_nocallback_when_destroyed)
|
||||
{
|
||||
published.clear();
|
||||
assertEqual(broker.clientsCount(), (size_t)0);
|
||||
|
||||
{
|
||||
MqttClient subscriber(&broker);
|
||||
subscriber.setCallback(onPublish);
|
||||
subscriber.subscribe("a/b");
|
||||
}
|
||||
|
||||
MqttClient publisher(&broker);
|
||||
publisher.publish("a/b");
|
||||
|
||||
assertEqual(published.size(), (size_t)0); // Only one publish has been received
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// setup() and loop()
|
||||
void setup() {
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
while(!Serial);
|
||||
|
||||
Serial.println("=============[ NO WIFI CONNECTION TinyMqtt TESTS ]========================");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
aunit::TestRunner::run();
|
||||
|
||||
if (Serial.available()) ESP.reset();
|
||||
}
|
||||
Reference in New Issue
Block a user