Initial commit, getting all the stuff from PlatformIO
This commit is contained in:
46
.pio/libdeps/esp32dev/PicoMQTT/.github/workflows/ci.yml
vendored
Normal file
46
.pio/libdeps/esp32dev/PicoMQTT/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: CI
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: arduino/arduino-lint-action@v1
|
||||
with:
|
||||
compliance: strict
|
||||
library-manager: update
|
||||
project-type: library
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- board: d1_mini
|
||||
platform_override:
|
||||
- board: esp32dev
|
||||
platform_override: espressif32@6.9.0
|
||||
- board: esp32dev
|
||||
platform_override: https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Install jq
|
||||
run: sudo apt-get install -yq jq
|
||||
- name: Install PlatformIO Core
|
||||
run: pip install --upgrade platformio
|
||||
- name: Build examples
|
||||
run: PLATFORM_OVERRIDE=${{ matrix.platform_override }} ./build_examples.sh ${{ matrix.board }}
|
||||
5
.pio/libdeps/esp32dev/PicoMQTT/.gitignore
vendored
Normal file
5
.pio/libdeps/esp32dev/PicoMQTT/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
*.orig
|
||||
*.tar.gz
|
||||
/examples.build
|
||||
/config.h
|
||||
1
.pio/libdeps/esp32dev/PicoMQTT/.piopm
Normal file
1
.pio/libdeps/esp32dev/PicoMQTT/.piopm
Normal file
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "PicoMQTT", "version": "1.3.0", "spec": {"owner": "mlesniew", "id": 15378, "name": "PicoMQTT", "requirements": null, "uri": null}}
|
||||
165
.pio/libdeps/esp32dev/PicoMQTT/LICENSE
Normal file
165
.pio/libdeps/esp32dev/PicoMQTT/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
427
.pio/libdeps/esp32dev/PicoMQTT/README.md
Normal file
427
.pio/libdeps/esp32dev/PicoMQTT/README.md
Normal file
@@ -0,0 +1,427 @@
|
||||
# PicoMQTT
|
||||
|
||||
This is a lightweight and easy to use MQTT library for ESP8266 and ESP32 devices.
|
||||
|
||||
 
|
||||
|
||||
[](https://www.ardu-badge.com/PicoMQTT) [](https://registry.platformio.org/libraries/mlesniew/PicoMQTT)
|
||||
|
||||
[](https://www.espressif.com/en/products/socs/esp8266) [](https://www.espressif.com/en/products/socs/esp32)
|
||||
|
||||
Features:
|
||||
* Works in client and broker mode
|
||||
* Implements [MQTT 3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html)
|
||||
* Supports publishing and consuming of [arbitrary sized messages](#arbitrary-sized-messages)
|
||||
* High performance -- the broker can deliver thousands of messages per second -- [see benchmarks](#benchmarks)
|
||||
* Works on [WiFi, Ethernet and more](#custom-server-and-client-types)
|
||||
* Supports connections over [websockets](#websocket-support)
|
||||
* Easy integration with the [ArduinoJson](https://arduinojson.org/) library to publish and consume JSON messages -- [see examples](#json)
|
||||
* Intuitive API
|
||||
* Low memory usage
|
||||
|
||||
Limitations:
|
||||
* Client only supports MQTT QoS levels 0 and 1
|
||||
* Broker only supports MQTT QoS level 0, ignores will and retained messages.
|
||||
* Currently only ESP8266 and ESP32 boards are supported
|
||||
|
||||
|
||||
## Installation instructions
|
||||
|
||||
* [Arduino IDE](https://www.ardu-badge.com/PicoMQTT)
|
||||
* [PlatformIO](https://registry.platformio.org/libraries/mlesniew/PicoMQTT/installation)
|
||||
|
||||
Additionally, PicoMQTT requires a recent version of the board core:
|
||||
* For ESP8266 core version 3.1 or later
|
||||
* For ESP32 core version 2.0.7 or later
|
||||
|
||||
|
||||
## Quickstart
|
||||
|
||||
To get started, try compiling and running the code below or explore [examples](examples).
|
||||
|
||||
### Client
|
||||
|
||||
```
|
||||
#include <Arduino.h>
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
void setup() {
|
||||
// Usual setup
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin("MyWiFi", "password");
|
||||
|
||||
// Subscribe to a topic pattern and attach a callback
|
||||
mqtt.subscribe("#", [](const char * topic, const char * payload) {
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
// Start the client
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// This will automatically reconnect the client if needed. Re-subscribing to topics is never required.
|
||||
mqtt.loop();
|
||||
|
||||
if (random(1000) == 0)
|
||||
mqtt.publish("picomqtt/welcome", "Hello from PicoMQTT!");
|
||||
}
|
||||
```
|
||||
|
||||
### Broker
|
||||
|
||||
```
|
||||
#include <Arduino.h>
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
// Usual setup
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin("MyWiFi", "password");
|
||||
|
||||
// Subscribe to a topic pattern and attach a callback
|
||||
mqtt.subscribe("#", [](const char * topic, const char * payload) {
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
// Start the broker
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// This will automatically handle client connections. By default, all clients are accepted.
|
||||
mqtt.loop();
|
||||
|
||||
if (random(1000) == 0)
|
||||
mqtt.publish("picomqtt/welcome", "Hello from PicoMQTT!");
|
||||
}
|
||||
```
|
||||
|
||||
## Publishing messages
|
||||
|
||||
To publish messages, the `publish` and `publish_P` methods can be used. The client and the broker have both the same
|
||||
API for publishing.
|
||||
|
||||
```
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com"); // or PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() { /* ... */ }
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
mqtt.publish("picomqtt/simple_publish", "Message");
|
||||
mqtt.publish("picomqtt/another_simple_publish", F("Message"));
|
||||
|
||||
const char binary_payload[] = "This string could contain binary data including a zero byte";
|
||||
size_t binary_payload_size = strlen(binary_payload);
|
||||
mqtt.publish("picomqtt/binary_payload", (const void *) binary_payload, binary_payload_size);
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
* It's not required to check if the client is connected before publishing. Calls to `publish()` will have no effect and will return immediately in such cases.
|
||||
* More examples available [here](examples/advanced_publish/advanced_publish.ino)
|
||||
|
||||
|
||||
## Subscribing and consuming messages
|
||||
|
||||
The `subscribe` methods can be used with client and broker to set up callbacks for specific topic patterns.
|
||||
|
||||
```
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com"); // or PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
/* ... */
|
||||
|
||||
mqtt.subscribe("picomqtt/foo", [](const char * payload) { /* handle message here */ });
|
||||
mqtt.subscribe("picomqtt/bar", [](const char * topic, const char * payload) { /* handle message here */ });
|
||||
mqtt.subscribe("picomqtt/baz", [](const char * topic, const void * payload, size_t payload_size) { /* handle message here */ });
|
||||
|
||||
// Pattern subscriptions
|
||||
mqtt.subscribe("picomqtt/+/foo/#", [](const char * topic, const char * payload) {
|
||||
// To extract individual elements from the topic use:
|
||||
String wildcard_value = mqtt.get_topic_element(topic, 1); // second parameter is the index (zero based)
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
* New subscriptions can be added at any point, not just in the `setup()` function.
|
||||
* All strings (`const char *` parameters) are guaranteed to have a null terminator. It's safe to treat them as strings.
|
||||
* Message payloads can be binary, which means they can contain a zero byte in the middle. To handle binary data, use a callback with a `size_t` parameter to know the exact size of the message.
|
||||
* The topic and the payload are both buffers allocated on the stack. They will become invalid after the callback returns. If you need to store the payload for later, make sure to copy it to a separate buffer.
|
||||
* By default, the maximum topic and payload sizes are is 128 and 1024 bytes respectively. This can be tuned by using `#define` directives to override values from [config.h](src/PicoMQTT/config.h). Consider using the advanced API described in the later sections to handle bigger messages.
|
||||
* If a received message's topic matches more than one pattern, then only one of the callbacks will be fired.
|
||||
* Try to return from message handlers quickly. Don't call functions which may block (like reading from serial or network connections), don't use the `delay()` function.
|
||||
* More examples available [here](examples/advanced_consume/advanced_consume.ino)
|
||||
|
||||
### Delivery of messages published on the broker
|
||||
|
||||
`PicoMQTT::Server` will not deliver published messages locally. This means that setting up a `PicoMQTT::Server` and using `subscribe`, will fire callbacks only when messages from clients are received. Messages published locally, on the same device will not trigger the callback.
|
||||
|
||||
`PicoMQTT::ServerLocalSubscribe` can be used as a drop-in replacement for `PicoMQTT::Server` to get around this limitation. This variant of the broker works just the same, but it fires subscription callbacks for messages published locally using the `publish` and `publish_P` methods. Note that publishing using `begin_publish` will work as in `PicoMQTT::Server` (so it will not fire local callbacks).
|
||||
|
||||
`PicoMQTT::ServerLocalSubscribe` has slightly worse performance and can be memory intensive, especially if large messages are published and subscribed to locally. Therefore, it should only be used when really needed. Moreover, it has one additional limitation: it's *subscription callbacks must not publish any messages* or it may cause a crash.
|
||||
|
||||
Example available [here](examples/server_local_subscribe/server_local_subscribe.ino).
|
||||
|
||||
|
||||
## Last Will Testament messages
|
||||
|
||||
Clients can be configured with a will message (aka LWT). This can be configured by changing elements of the client's `will` structure:
|
||||
|
||||
```
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com"); // or PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
/* ... */
|
||||
|
||||
mqtt.will.topic = "picomqtt/lwt";
|
||||
mqtt.will.payload = "bye bye";
|
||||
mqtt.will.qos = 1;
|
||||
mqtt.will.retain = true;
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
* Will messages will only be active if `will.topic` is not empty.
|
||||
* The `will` structure can be modified at any time, even when a connection is active. However, it changes will take effect only after the client reconnects (after connection loss or after calling `mqtt.disconnect()`).
|
||||
* Default values of `will.qos` and `will.retain` are `0` and `false` respectively.
|
||||
|
||||
## Connect and disconnect callbacks
|
||||
|
||||
The client can be configured to fire callbacks after connecting and disconnecting to a server. This is useful if a message needs to be sent as soon as the connection is established:
|
||||
|
||||
```
|
||||
#include <Arduino.h>
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com"); // or PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
/* ... */
|
||||
|
||||
mqtt.connected_callback = [] {
|
||||
Serial.println("MQTT connected");
|
||||
}
|
||||
|
||||
mqtt.disconnected_callback = [] {
|
||||
Serial.println("MQTT disconnected");
|
||||
}
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
* It's safe to set or change the callbacks at any time.
|
||||
* It is not guaranteed that the connect callback will fire immediately after the connection is established. Messages may sometimes be delivered first (to handlers configured using `subscribe`).
|
||||
|
||||
## Arbitrary sized messages
|
||||
|
||||
It is possible to send and handle messages of arbitrary size, even if they are significantly bigger than the available
|
||||
memory.
|
||||
|
||||
### Publishing
|
||||
|
||||
```
|
||||
auto publish = mqtt.begin_publish(
|
||||
"picomqtt/advanced", // topic
|
||||
1000000 // payload size
|
||||
);
|
||||
|
||||
// The returned publish is a Print subclass, so all Print's functions will work:
|
||||
publish.println("Hello MQTT");
|
||||
publish.println(2023, HEX);
|
||||
publish.write('c');
|
||||
publish.write((const uint8_t *) "1234567890", 10);
|
||||
|
||||
// We can always check how much space is left
|
||||
size_t remaining_size = publish.get_remaining_size();
|
||||
|
||||
// ...
|
||||
|
||||
// Once all data is written, we have to send the message
|
||||
publish.send();
|
||||
```
|
||||
|
||||
### Consuming
|
||||
|
||||
```
|
||||
mqtt.subscribe("picomqtt/advanced", [](const char * topic, PicoMQTT::IncomingPacket & packet) {
|
||||
// at any point we can check the remaining payload size
|
||||
size_t payload_size = packet.get_remaining_size();
|
||||
|
||||
// packet is a Stram object, so we can use its methods
|
||||
int val1 = packet.read();
|
||||
|
||||
char buf[100];
|
||||
packet.read(buf, 100);
|
||||
|
||||
// it's OK to not read the whole content of the message
|
||||
});
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* When consuming or producing a message using the advanced API, don't call other MQTT methods. Don't try to publish multiple messages at a time or publish a message while consuming another.
|
||||
* Even with this API, the topic size is still limited. The limit can be increased by overriding values from [config.h](src/PicoMQTT/config.h).
|
||||
|
||||
## Json
|
||||
|
||||
It's easy to publish and subscribe to JSON messages by integrating with [ArduinoJson](https://arduinojson.org/). Of course, you can always simply use `serializeJson` and `deserializeJson` with strings, but it's much more efficient to use the advanced API for this. Check the examples below or try the [arduinojson.ino](examples/arduinojson/arduinojson.ino) example.
|
||||
|
||||
### Subscribing
|
||||
|
||||
```
|
||||
mqtt.subscribe("picomqtt/json/#", [](const char * topic, Stream & stream) {
|
||||
JsonDocument json;
|
||||
|
||||
// Deserialize straight from the Stream object
|
||||
if (deserializeJson(json, stream)) {
|
||||
// don't forget to check for errors
|
||||
Serial.println("Json parsing failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// work with the object as usual
|
||||
int value = json["foo"].as<int>();
|
||||
});
|
||||
```
|
||||
|
||||
### Publishing
|
||||
|
||||
```
|
||||
JsonDocument json;
|
||||
json["foo"] = "bar";
|
||||
json["millis"] = millis();
|
||||
|
||||
// publish using begin_publish()/send() API
|
||||
auto publish = mqtt.begin_publish(topic, measureJson(json));
|
||||
serializeJson(json, publish);
|
||||
publish.send();
|
||||
```
|
||||
|
||||
## Custom server and client types
|
||||
|
||||
By default, PicoMQTT will use the built-in WiFi interface of the device (using `WiFiClient` or `WiFiServer` objects internally). You can, however, tell it to use custom classes instead. This is useful to use PicoMQTT with TLS (e.g. using `WiFiClientSecure`) or with an Ethernet board.
|
||||
|
||||
### Clients
|
||||
|
||||
A PicoMQTT client can be used with any subclass of the standard Arduino `Client`. To create an instance with a custom client object, pass it as the first argument to the `PicoMQTT::Client` constructor (the remaining parameters are the usual ones). For example:
|
||||
|
||||
```
|
||||
EthernetClient client;
|
||||
PicoMQTT::Client mqtt(client, "broker.hivemq.com");
|
||||
```
|
||||
|
||||
The `mqtt` instance can be used as usual. Additional `client` setup can be done in the `setup()` function, before the `mqtt.begin()` call.
|
||||
|
||||
Full example available [here](examples/w5500_client/w5500_client.ino).
|
||||
|
||||
### Servers
|
||||
|
||||
A PicoMQTT server can be used with any class that has an interface roughly similar to other servers, e.g. `WiFiServer` or `EtherenetServer`. The only required methods are `begin()` and `accept()`. To set up a broker with a custom server, pass it as the first argument to the `PicoMQTT::Server` constructor, for example:
|
||||
|
||||
```
|
||||
EthernetServer server(1883);
|
||||
PicoMQTT::Server mqtt(server);
|
||||
```
|
||||
|
||||
The `mqtt` instance can be used as usual. Additional `server` setup can be done in the `setup()` function, before the `mqtt.begin()` call.
|
||||
|
||||
Full example available [here](examples/w5500_server/w5500_server.ino).
|
||||
|
||||
### Multiserver
|
||||
|
||||
Sometimes it's useful to run a broker, which can handle connections from different interfaces (e.g. WiFi and Ethernet). This is also possible -- just pass multiple servers as parameters to the constructor:
|
||||
|
||||
```
|
||||
WiFiServer wifi_server(1883);
|
||||
EthernetServer eth_server(1883);
|
||||
PicoMQTT::Server mqtt(wifi_server, eth_server);
|
||||
```
|
||||
|
||||
With this setup, the `mqtt` instance will accept connections from both servers and will be able to route messages between them.
|
||||
|
||||
Full example available [here](examples/multi_server/multi_server.ino).
|
||||
|
||||
## Websockets support
|
||||
|
||||
PicoMQTT supports connections over WebSockets with the [PicoWebsocket](https://github.com/mlesniew/Picowebsocket) library. With this dependency installed, broker and client set up is the same as with other custom sockets:
|
||||
|
||||
```
|
||||
#include <PicoMQTT.h>
|
||||
#include <PicoWebsocket.h>
|
||||
|
||||
// Create a server -- most other servers can be used here too (e.g. EthernetServer)
|
||||
WiFiServer server(80);
|
||||
|
||||
// Create a websocket instance which uses the server
|
||||
PicoWebsocket::Server<::WiFiServer> websocket_server(server);
|
||||
|
||||
// Create a MQTT server
|
||||
PicoMQTT::Server mqtt(websocket_server);
|
||||
```
|
||||
|
||||
Full example available [here](examples/websocket_server/websocket_server.ino).
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Charts in this section show PicoMQTT how many messages a broker running on the ESP8266 and ESP32 was able to deliver per second per client depending on the payload size and the number of subscribed clients.
|
||||
* Test were executed using the library version from commit 406e879e8b25b84c1488c1e2789e4b3719dd1496
|
||||
* The library was using default configuration values (as defined in [config.h](src/PicoMQTT/config.h))
|
||||
* Measurements were done on a PC using scripts in [benchmark/](benchmark/)
|
||||
* The broker was configured to do nothing but forward the messages to subscribed clients, see [benchmark.ino](benchmark/benchmark.ino)
|
||||
* The ESPs were connecting to a router just next to them to avoid interference. The test PC was connected to the same router using an Ethernet cable.
|
||||
|
||||
### ESP8266
|
||||
|
||||

|
||||
|
||||
[Get CSV](doc/benchmark/esp8266.csv)
|
||||
|
||||
### ESP32
|
||||
|
||||

|
||||
|
||||
[Get CSV](doc/benchmark/esp32.csv)
|
||||
|
||||
## Special thanks
|
||||
|
||||
Many thanks to [Michael Haberler](https://github.com/mhaberler) for his support with the MQTT over WebSocket feature.
|
||||
|
||||
## License
|
||||
|
||||
This library is open-source software licensed under GNU LGPLv3.
|
||||
19
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.ino
Normal file
19
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.ino
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <Arduino.h>
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
// Usual setup
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin("wifiname", "password");
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// This will automatically handle client connections. By default, all clients are accepted.
|
||||
mqtt.loop();
|
||||
}
|
||||
85
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.py
Executable file
85
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.py
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import time
|
||||
import multiprocessing
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
|
||||
def consumer(barrier, conn, host, expected_messages, timeout):
|
||||
pid = multiprocessing.current_process().pid
|
||||
client = mqtt.Client(f"consumer_{pid}")
|
||||
# client.username_pw_set("username", "password")
|
||||
|
||||
total_messages = 0
|
||||
first_message_time = None
|
||||
last_message_time = None
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
nonlocal total_messages, first_message_time, last_message_time
|
||||
total_messages += 1
|
||||
last_message_time = time.time()
|
||||
first_message_time = first_message_time or last_message_time
|
||||
|
||||
def on_subscribe(client, userdata, mid, granted_qos):
|
||||
barrier.wait()
|
||||
|
||||
client.on_message = on_message
|
||||
client.on_subscribe = on_subscribe
|
||||
|
||||
client.connect(host)
|
||||
client.loop_start()
|
||||
client.subscribe("benchmark")
|
||||
while total_messages < expected_messages:
|
||||
if first_message_time and total_messages >= 2:
|
||||
elapsed_time = time.time() - first_message_time
|
||||
if elapsed_time >= timeout:
|
||||
break
|
||||
time.sleep(1)
|
||||
client.loop_stop()
|
||||
|
||||
elapsed_time = last_message_time - first_message_time
|
||||
rate = (total_messages - 1) / elapsed_time
|
||||
conn.send(rate)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("host")
|
||||
parser.add_argument("--consumers", type=int, default=1)
|
||||
parser.add_argument("--messages", type=int, default=1000)
|
||||
parser.add_argument("--size", type=int, default=1)
|
||||
parser.add_argument("--timeout", type=int, default=10)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
client = mqtt.Client("producer")
|
||||
client.connect(args.host)
|
||||
|
||||
barrier = multiprocessing.Barrier(args.consumers + 1)
|
||||
pipe = multiprocessing.Pipe(False)
|
||||
consumers = [
|
||||
multiprocessing.Process(
|
||||
target=consumer, args=(barrier, pipe[1], args.host, args.messages, args.timeout)
|
||||
)
|
||||
for _ in range(args.consumers)
|
||||
]
|
||||
|
||||
message = "0" * args.size
|
||||
|
||||
for consumer in consumers:
|
||||
consumer.start()
|
||||
|
||||
# wait for all processes to connect
|
||||
barrier.wait()
|
||||
|
||||
# fire messages
|
||||
while any(p.is_alive() for p in consumers):
|
||||
client.publish("benchmark", message)
|
||||
|
||||
# wait for consumers
|
||||
for consumer in consumers:
|
||||
consumer.join()
|
||||
|
||||
# collect results
|
||||
rate = sum(pipe[0].recv() for _ in consumers) / len(consumers)
|
||||
print(f"{rate:.1f}")
|
||||
37
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.sh
Executable file
37
.pio/libdeps/esp32dev/PicoMQTT/benchmark/benchmark.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Usage:
|
||||
# ./benchmark.sh <ESP IP>
|
||||
|
||||
export LC_NUMERIC=C
|
||||
|
||||
HOST="$1"
|
||||
REPEAT=5
|
||||
CONSUMER_COUNTS="12 10 5 1"
|
||||
SIZES="10000 5000 1000 500 100 50 10 5 1"
|
||||
|
||||
printf "message size\t"
|
||||
for CONSUMERS in $CONSUMER_COUNTS
|
||||
do
|
||||
printf "%i consumers\t" $CONSUMERS
|
||||
done
|
||||
printf "\n"
|
||||
|
||||
for SIZE in $SIZES
|
||||
do
|
||||
printf "%i\t" $SIZE
|
||||
for CONSUMERS in $CONSUMER_COUNTS
|
||||
do
|
||||
RESULT=$({
|
||||
for ITERATION in $(seq $REPEAT)
|
||||
do
|
||||
./benchmark.py --size=$SIZE --consumers=$CONSUMERS "$HOST"
|
||||
# potential interference may go away by itself if we wait
|
||||
sleep 1
|
||||
done
|
||||
} | awk '{ sum += $1; count += 1 } END { print sum / count }')
|
||||
|
||||
printf "%.1f\t" $RESULT
|
||||
done
|
||||
printf "\n"
|
||||
done
|
||||
24
.pio/libdeps/esp32dev/PicoMQTT/benchmark/chart.py
Executable file
24
.pio/libdeps/esp32dev/PicoMQTT/benchmark/chart.py
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
import csv
|
||||
import sys
|
||||
|
||||
import pygal
|
||||
|
||||
data = list(csv.DictReader(sys.stdin, dialect="excel-tab"))
|
||||
X = "message size"
|
||||
SERIES = [e for e in data[0].keys() if e and e != X]
|
||||
|
||||
chart = pygal.XY(
|
||||
legend_at_bottom=True,
|
||||
logarithmic=True,
|
||||
x_title="payload size [B]",
|
||||
y_title="messages delivery rate [1/s]",
|
||||
pretty_print=True,
|
||||
stroke_style={"width": 50},
|
||||
x_label_rotation=-90,
|
||||
)
|
||||
|
||||
for name in SERIES:
|
||||
chart.add(name, [(float(e[X]), float(e[name])) for e in data])
|
||||
|
||||
sys.stdout.write(chart.render().decode("utf-8"))
|
||||
79
.pio/libdeps/esp32dev/PicoMQTT/build_examples.sh
Executable file
79
.pio/libdeps/esp32dev/PicoMQTT/build_examples.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$#" -ne 1 ]
|
||||
then
|
||||
echo "Usage: $0 <pio-board-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
mkdir -p examples.build
|
||||
ROOT_DIR=$PWD
|
||||
|
||||
BOARD="$1"
|
||||
PLATFORM="$(pio boards --json-output | jq -r ".[] | select(.id == \"$BOARD\") | .platform")"
|
||||
|
||||
if [ -z "$PLATFORM" ]
|
||||
then
|
||||
echo "Unknown board $BOARD"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
find examples -mindepth 1 -type d | cut -d/ -f2 | sort | while read -r EXAMPLE
|
||||
do
|
||||
REQUIRED_PLATFORM="$(grep -hoiE 'platform:.*' "$ROOT_DIR/examples/$EXAMPLE/"* | cut -d: -f2- | head -n1 | tr -d ' ')"
|
||||
if [ -z "$REQUIRED_PLATFORM" ]
|
||||
then
|
||||
COMPATIBLE_PLATFORMS="$(grep -hoiE 'platform compatibility:.*' "$ROOT_DIR/examples/$EXAMPLE/"* | cut -d: -f2- | head -n1)"
|
||||
else
|
||||
COMPATIBLE_PLATFORMS="$(echo $REQUIRED_PLATFORM | cut -d'@' -f1)"
|
||||
fi
|
||||
FILESYSTEM="$(grep -hoiE 'filesystem:.*' "$ROOT_DIR/examples/$EXAMPLE/"* | cut -d: -f2- | head -n1)"
|
||||
DEPENDENCIES="$(grep -hoiE 'dependencies:.*' "$ROOT_DIR/examples/$EXAMPLE/"* | cut -d: -f2- | head -n1 | xargs -r -n1 printf '\n %s')"
|
||||
echo "$EXAMPLE:$COMPATIBLE_PLATFORMS"
|
||||
if [ -n "$COMPATIBLE_PLATFORMS" ] && ! echo "$COMPATIBLE_PLATFORMS" | grep -qFiw "$PLATFORM"
|
||||
then
|
||||
echo "$EXAMPLE: This example is not compatible with this platform"
|
||||
continue
|
||||
fi
|
||||
|
||||
mkdir -p examples.build/$BOARD/$EXAMPLE
|
||||
pushd examples.build/$BOARD/$EXAMPLE
|
||||
if [ ! -f platformio.ini ]
|
||||
then
|
||||
pio init --board=$BOARD
|
||||
echo "monitor_speed = 115200" >> platformio.ini
|
||||
echo "upload_speed = 921600" >> platformio.ini
|
||||
if [ -n "$DEPENDENCIES" ]
|
||||
then
|
||||
echo "lib_deps = $DEPENDENCIES" >> platformio.ini
|
||||
fi
|
||||
if [ -n "$PLATFORM_OVERRIDE" ]
|
||||
then
|
||||
sed -E -i "s#^platform *=.*#platform = $PLATFORM_OVERRIDE#" platformio.ini
|
||||
elif [ -n "$REQUIRED_PLATFORM" ]
|
||||
then
|
||||
sed -E -i "s#^platform *=.*#platform = $REQUIRED_PLATFORM#" platformio.ini
|
||||
fi
|
||||
if [ -n "$FILESYSTEM" ]
|
||||
then
|
||||
echo "board_build.filesystem = $FILESYSTEM" >> platformio.ini
|
||||
fi
|
||||
fi
|
||||
|
||||
ln -s -f -t src/ "$ROOT_DIR/examples/$EXAMPLE/"*
|
||||
ln -s -f -t lib/ "$ROOT_DIR"
|
||||
if [ -e "$ROOT_DIR/config.h" ]
|
||||
then
|
||||
ln -s -f -t src/ "$ROOT_DIR/config.h"
|
||||
fi
|
||||
if [ -d "$ROOT_DIR/examples/$EXAMPLE/data" ]
|
||||
then
|
||||
ln -s -f -t ./ "$ROOT_DIR/examples/$EXAMPLE/data"
|
||||
fi
|
||||
pio run
|
||||
popd
|
||||
done
|
||||
@@ -0,0 +1,121 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
// The example will also work with a broker -- just replace the line above with:
|
||||
// PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
|
||||
mqtt.subscribe("picomqtt/string_payload", [](const char * payload) {
|
||||
// Use this type of handler to work with messages which should be strings. PicoMQTT will always add an extra
|
||||
// zero terminator at the end of the payload, so it's safe to treat it as a string.
|
||||
// Note however, that in the general case, payload can be binary (so it can contain any data, including zero
|
||||
// bytes).
|
||||
Serial.printf("Received message in topic 'picomqtt/string_payload': %s\n", payload);
|
||||
});
|
||||
|
||||
|
||||
mqtt.subscribe("picomqtt/binary_payload", [](const void * payload, size_t payload_size) {
|
||||
// Here the payload is in a void * buffer and payload_size tells us its size.
|
||||
Serial.printf("Received message in topic 'picomqtt/binary_payload', size: %u\n", payload_size);
|
||||
});
|
||||
|
||||
|
||||
mqtt.subscribe("picomqtt/string_payload/+/wildcard", [](const char * topic, const char * payload) {
|
||||
// Use this type of callback to capture both the topic and the payload as string
|
||||
|
||||
// If the topic contains wildcards, their values can be extracted easily too
|
||||
String wildcard_value = mqtt.get_topic_element(topic, 2);
|
||||
|
||||
Serial.printf("Received message in topic '%s' (wildcard = '%s'): %s\n", topic, wildcard_value.c_str(), payload);
|
||||
});
|
||||
|
||||
|
||||
mqtt.subscribe("picomqtt/binary_payload/+/wildcard", [](const char * topic, const void * payload, size_t payload_size) {
|
||||
// Here the payload is in a void * buffer and payload_size tells us its size.
|
||||
// If the topic contains wildcards, their values can be extracted easily too
|
||||
String wildcard_value = mqtt.get_topic_element(topic, 2);
|
||||
|
||||
Serial.printf("Received message in topic '%s' (wildcard = '%s'), size: %u\n",
|
||||
topic, wildcard_value.c_str(), payload_size);
|
||||
|
||||
});
|
||||
|
||||
|
||||
mqtt.subscribe("picomqtt/big_message", [](const char * topic, PicoMQTT::IncomingPacket & packet) {
|
||||
// This is the most advanced handler type. The packet parameter is a subclass of Stream and can be read from
|
||||
// in small chunks using Stream's typical API. This is useful for handling messages too big to fit in RAM.
|
||||
|
||||
// Retrieve payload size
|
||||
size_t payload_size = packet.get_remaining_size();
|
||||
|
||||
size_t digit_count = 0;
|
||||
while (payload_size--) {
|
||||
int val = packet.read();
|
||||
if ((val >= '0') && (val <= '9')) {
|
||||
// the byte is a digit!
|
||||
++digit_count;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that it's OK to not read the entire content of the message. Reading beyond the end of the packet
|
||||
// is an error.
|
||||
Serial.printf("Received message in topic '%s', it contained %u digits\n", topic, digit_count);
|
||||
});
|
||||
|
||||
// Arduino Strings can be used instead of const char *. Note that this can be inefficient, especially with big
|
||||
// payloads.
|
||||
mqtt.subscribe("picomqtt/arduino_string/payload", [](const String & payload) {
|
||||
Serial.printf("Received message in topic 'picomqtt/arduino_string/payload': %s\n", payload.c_str());
|
||||
});
|
||||
|
||||
mqtt.subscribe("picomqtt/arduino_string/+/topic_and_payload", [](const String & topic, const String & payload) {
|
||||
// If the topic contains wildcards, their values can be extracted easily too
|
||||
String wildcard_value = mqtt.get_topic_element(topic, 2);
|
||||
|
||||
Serial.printf("Received message in topic '%s' (wildcard = '%s'): %s\n", topic.c_str(), wildcard_value.c_str(), payload.c_str());
|
||||
});
|
||||
|
||||
// Different types of strings can be mixed too
|
||||
mqtt.subscribe("picomqtt/arduino_string/mix/1", [](const String & topic, const char * payload) {
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic.c_str(), payload);
|
||||
});
|
||||
|
||||
mqtt.subscribe("picomqtt/arduino_string/mix/2", [](const char * topic, const String & payload) {
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload.c_str());
|
||||
});
|
||||
|
||||
// const and reference can be skipped too
|
||||
mqtt.subscribe("picomqtt/arduino_string/mix/3", [](char * topic, String payload) {
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload.c_str());
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
// The example will also work with a broker -- just replace the line above with:
|
||||
// PicoMQTT::Server mqtt;
|
||||
|
||||
unsigned long last_publish_time = 0;
|
||||
|
||||
static const char flash_string[] PROGMEM = "This is a string stored in flash.";
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Publish a greeting message every 10 seconds.
|
||||
if (millis() - last_publish_time >= 10000) {
|
||||
// We're publishing to a topic, which we're subscribed too.
|
||||
// The broker should deliver the messages back to us.
|
||||
|
||||
// publish a literal flash string
|
||||
mqtt.publish("picomqtt/flash_string/literal", F("Literal PGM string"));
|
||||
|
||||
// publish a string from a PGM global
|
||||
mqtt.publish_P("picomqtt/flash_string/global", flash_string);
|
||||
|
||||
// The topic can be an F-string too:
|
||||
mqtt.publish_P(F("picomqtt/flash_string/global"), flash_string);
|
||||
|
||||
// publish binary data
|
||||
const char binary_payload[] = "This string could contain binary data including a zero byte";
|
||||
size_t binary_payload_size = strlen(binary_payload);
|
||||
mqtt.publish("picomqtt/binary_payload", (const void *) binary_payload, binary_payload_size);
|
||||
|
||||
// Publish a big message in small chunks
|
||||
auto publish = mqtt.begin_publish("picomqtt/chunks", 1000);
|
||||
|
||||
// Here we're writing 10 bytes 100 times
|
||||
for (int i = 0; i < 1000; i += 10) {
|
||||
publish.write((const uint8_t *) "1234567890", 10);
|
||||
}
|
||||
|
||||
// In case of chunked published, an explicit call to send() is required.
|
||||
publish.send();
|
||||
|
||||
last_publish_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// dependencies: bblanchon/ArduinoJson
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
unsigned long last_publish_time = 0;
|
||||
int greeting_number = 1;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
// Subscribe to a topic and attach a callback
|
||||
mqtt.subscribe("picomqtt/json/#", [](const char * topic, Stream & stream) {
|
||||
Serial.printf("Received message in topic '%s':\n", topic);
|
||||
JsonDocument json;
|
||||
if (deserializeJson(json, stream)) {
|
||||
Serial.println("Json parsing failed.");
|
||||
return;
|
||||
}
|
||||
serializeJsonPretty(json, Serial);
|
||||
Serial.println();
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Publish a greeting message every 3 seconds.
|
||||
if (millis() - last_publish_time >= 3000) {
|
||||
String topic = "picomqtt/json/esp-" + WiFi.macAddress();
|
||||
Serial.printf("Publishing in topic '%s'...\n", topic.c_str());
|
||||
|
||||
// build JSON document
|
||||
JsonDocument json;
|
||||
json["foo"] = "bar";
|
||||
json["millis"] = millis();
|
||||
|
||||
// publish using begin_publish()/send() API
|
||||
auto publish = mqtt.begin_publish(topic, measureJson(json));
|
||||
serializeJson(json, publish);
|
||||
publish.send();
|
||||
|
||||
last_publish_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
unsigned long last_publish_time = 0;
|
||||
int greeting_number = 1;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
// Subscribe to a topic and attach a callback
|
||||
mqtt.subscribe("picomqtt/#", [](const char * topic, const char * payload) {
|
||||
// payload might be binary, but PicoMQTT guarantees that it's zero-terminated
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Publish a greeting message every 3 seconds.
|
||||
if (millis() - last_publish_time >= 3000) {
|
||||
// We're publishing to a topic, which we're subscribed too.
|
||||
// The broker should deliver the messages back to us.
|
||||
String topic = "picomqtt/esp-" + WiFi.macAddress();
|
||||
String message = "Hello #" + String(greeting_number++);
|
||||
Serial.printf("Publishing message in topic '%s': %s\n", topic.c_str(), message.c_str());
|
||||
mqtt.publish(topic, message);
|
||||
last_publish_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Server mqtt;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Client mqtt(
|
||||
"broker.hivemq.com", // broker address (or IP)
|
||||
1883, // broker port (defaults to 1883)
|
||||
"esp-client", // Client ID
|
||||
"username", // MQTT username
|
||||
"password" // MQTT password
|
||||
);
|
||||
|
||||
// PicoMQTT::Client mqtt; // This will work too, but configuration will have to be set later (e.g. in setup())
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
// MQTT settings can be changed or set here instead
|
||||
mqtt.host = "broker.hivemq.com";
|
||||
mqtt.port = 1883;
|
||||
mqtt.client_id = "esp-" + WiFi.macAddress();
|
||||
mqtt.username = "username";
|
||||
mqtt.password = "secret";
|
||||
|
||||
// Subscribe to a topic and attach a callback
|
||||
mqtt.subscribe("picomqtt/#", [](const char * topic, const char * payload) {
|
||||
// payload might be binary, but PicoMQTT guarantees that it's zero-terminated
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Changing host, port, client_id, username or password here is OK too. Changes will take effect after next
|
||||
// reconnect. To force reconnection, explicitly disconnect the client by calling mqtt.disconnect().
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// dependencies: mlesniew/PicoWebsocket
|
||||
|
||||
#include <PicoMQTT.h>
|
||||
#include <PicoWebsocket.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
// regular tcp server on port 1883
|
||||
::WiFiServer tcp_server(1883);
|
||||
|
||||
// websocket server on tcp port 80
|
||||
::WiFiServer websocket_underlying_server(80);
|
||||
PicoWebsocket::Server<::WiFiServer> websocket_server(websocket_underlying_server);
|
||||
|
||||
// MQTT server using the two server instances
|
||||
PicoMQTT::Server mqtt(tcp_server, websocket_server); // NOTE: this constructor can take any number of server parameters
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Client mqtt("broker.hivemq.com");
|
||||
|
||||
unsigned long last_subscribe_time = 0;
|
||||
int resubscribe = 1;
|
||||
|
||||
void handler_foo(const char * topic, const char * payload) {
|
||||
Serial.printf("Handler foo received message in topic '%s': %s\n", topic, payload);
|
||||
}
|
||||
|
||||
void handler_bar(const char * topic, const char * payload) {
|
||||
Serial.printf("Handler bar received message in topic '%s': %s\n", topic, payload);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.println("WiFi connected.");
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Resubscribe every 5 seconds
|
||||
if (millis() - last_subscribe_time >= 5000) {
|
||||
if (++resubscribe % 2 == 0) {
|
||||
mqtt.subscribe("picomqtt/#", handler_foo);
|
||||
} else {
|
||||
mqtt.subscribe("picomqtt/#", handler_bar);
|
||||
}
|
||||
last_subscribe_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
class MQTT: public PicoMQTT::Server {
|
||||
protected:
|
||||
PicoMQTT::ConnectReturnCode auth(const char * client_id, const char * username, const char * password) override {
|
||||
// only accept client IDs which are 3 chars or longer
|
||||
if (String(client_id).length() < 3) { // client_id is never NULL
|
||||
return PicoMQTT::CRC_IDENTIFIER_REJECTED;
|
||||
}
|
||||
|
||||
// only accept connections if username and password are provided
|
||||
if (!username || !password) { // username and password can be NULL
|
||||
// no username or password supplied
|
||||
return PicoMQTT::CRC_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
// accept two user/password combinations
|
||||
if (
|
||||
((String(username) == "alice") && (String(password) == "secret"))
|
||||
|| ((String(username) == "bob") && (String(password) == "password"))) {
|
||||
return PicoMQTT::CRC_ACCEPTED;
|
||||
}
|
||||
|
||||
// reject all other credentials
|
||||
return PicoMQTT::CRC_BAD_USERNAME_OR_PASSWORD;
|
||||
}
|
||||
} mqtt;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Server mqtt(9000); // listening port: TCP 9000
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::Server mqtt;
|
||||
|
||||
unsigned long last_publish_time = 0;
|
||||
int greeting_number = 1;
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
// Subscribe to a topic and attach a callback
|
||||
mqtt.subscribe("#", [](const char * topic, const char * payload) {
|
||||
// payload might be binary, but PicoMQTT guarantees that it's zero-terminated
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Publish a greeting message every 3 seconds.
|
||||
if (millis() - last_publish_time >= 3000) {
|
||||
// We're publishing to a topic, which we're subscribed too, but these message will *not* be delivered locally.
|
||||
String topic = "picomqtt/esp-" + WiFi.macAddress();
|
||||
String message = "Hello #" + String(greeting_number++);
|
||||
Serial.printf("Publishing message in topic '%s': %s\n", topic.c_str(), message.c_str());
|
||||
mqtt.publish(topic, message);
|
||||
last_publish_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
PicoMQTT::ServerLocalSubscribe mqtt;
|
||||
unsigned long last_publish;
|
||||
|
||||
static const char flash_string[] PROGMEM = "Hello from the broker's flash.";
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
|
||||
mqtt.subscribe("picomqtt/foo", [](const char * payload) {
|
||||
Serial.printf("Message received: %s\n", payload);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
if (millis() - last_publish >= 5000) {
|
||||
// this will publish the message to subscribed clients and fire the local callback
|
||||
mqtt.publish("picomqtt/foo", "hello from broker!");
|
||||
|
||||
// this will also work as usual and fire the callback
|
||||
mqtt.publish_P("picomqtt/foo", flash_string);
|
||||
|
||||
// begin_publish will publish to clients, but it will NOT fire the local callback
|
||||
auto publish = mqtt.begin_publish("picomqtt/foo", 33);
|
||||
publish.write((const uint8_t *) "Message delivered to clients only", 33);
|
||||
publish.send();
|
||||
|
||||
last_publish = millis();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Platform compatibility: espressif8266
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
|
||||
EthernetClient client;
|
||||
PicoMQTT::Client mqtt(client, "broker.hivemq.com");
|
||||
|
||||
unsigned long last_publish_time = 0;
|
||||
int greeting_number = 1;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("Connecting to network...");
|
||||
Ethernet.init(5); // ss pin
|
||||
while (!Ethernet.begin(mac)) {
|
||||
Serial.println("Failed, retrying...");
|
||||
}
|
||||
Serial.println(Ethernet.localIP());
|
||||
|
||||
// Subscribe to a topic and attach a callback
|
||||
mqtt.subscribe("picomqtt/#", [](const char * topic, const char * payload) {
|
||||
// payload might be binary, but PicoMQTT guarantees that it's zero-terminated
|
||||
Serial.printf("Received message in topic '%s': %s\n", topic, payload);
|
||||
});
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// Publish a greeting message every 3 seconds.
|
||||
if (millis() - last_publish_time >= 3000) {
|
||||
// We're publishing to a topic, which we're subscribed too.
|
||||
// The broker should deliver the messages back to us.
|
||||
String topic = "picomqtt/esp-" + WiFi.macAddress();
|
||||
String message = "Hello #" + String(greeting_number++);
|
||||
Serial.printf("Publishing message in topic '%s': %s\n", topic.c_str(), message.c_str());
|
||||
mqtt.publish(topic, message);
|
||||
last_publish_time = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Platform compatibility: espressif8266
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
#include <PicoMQTT.h>
|
||||
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
|
||||
EthernetServer server(1883);
|
||||
PicoMQTT::Server mqtt(server);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("Connecting to network...");
|
||||
Ethernet.init(5); // ss pin
|
||||
while (!Ethernet.begin(mac)) {
|
||||
Serial.println("Failed, retrying...");
|
||||
}
|
||||
Serial.println(Ethernet.localIP());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// dependencies: mlesniew/PicoWebsocket@1.2.0
|
||||
|
||||
#include <PicoMQTT.h>
|
||||
#include <PicoWebsocket.h>
|
||||
|
||||
#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#define WIFI_SSID "WiFi SSID"
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_PASSWORD
|
||||
#define WIFI_PASSWORD "password"
|
||||
#endif
|
||||
|
||||
WiFiServer server(80);
|
||||
PicoWebsocket::Server<::WiFiServer> websocket_server(server);
|
||||
PicoMQTT::Server mqtt(websocket_server);
|
||||
|
||||
void setup() {
|
||||
// Setup serial
|
||||
Serial.begin(115200);
|
||||
|
||||
// Connect to WiFi
|
||||
Serial.printf("Connecting to WiFi %s\n", WIFI_SSID);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) { delay(1000); }
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
|
||||
mqtt.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
}
|
||||
9
.pio/libdeps/esp32dev/PicoMQTT/library.properties
Normal file
9
.pio/libdeps/esp32dev/PicoMQTT/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=PicoMQTT
|
||||
version=1.3.0
|
||||
author=Michał Leśniewski
|
||||
maintainer=Michał Leśniewski <mlesniew@gmail.com>
|
||||
sentence=MQTT Broker and client
|
||||
paragraph=Easy to use MQTT broker and client library.
|
||||
category=Communication
|
||||
url=https://github.com/mlesniew/PicoMQTT
|
||||
architectures=esp8266,esp32
|
||||
7
.pio/libdeps/esp32dev/PicoMQTT/platformio.ini
Normal file
7
.pio/libdeps/esp32dev/PicoMQTT/platformio.ini
Normal file
@@ -0,0 +1,7 @@
|
||||
[env:wemos]
|
||||
platform = espressif8266
|
||||
board = d1_mini
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
upload_speed = 921600
|
||||
|
||||
20
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT.h
Normal file
20
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined(ESP8266)
|
||||
|
||||
#if (ARDUINO_ESP8266_MAJOR != 3) || (ARDUINO_ESP8266_MINOR < 1)
|
||||
#error PicoMQTT requires ESP8266 board core version >= 3.1
|
||||
#endif
|
||||
|
||||
#elif defined(ESP32)
|
||||
|
||||
#if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 7)
|
||||
#error PicoMQTT requires ESP32 board core version >= 2.0.7
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "PicoMQTT/client.h"
|
||||
#include "PicoMQTT/server.h"
|
||||
21
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/autoid.h
Normal file
21
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/autoid.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class AutoId {
|
||||
public:
|
||||
typedef unsigned int Id;
|
||||
|
||||
AutoId(): id(generate_id()) {}
|
||||
AutoId(const AutoId &) = default;
|
||||
|
||||
const Id id;
|
||||
|
||||
private:
|
||||
static Id generate_id() {
|
||||
static Id next_id = 1;
|
||||
return next_id++;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
290
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client.cpp
Normal file
290
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "client.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
BasicClient::BasicClient(::Client & client, unsigned long keep_alive_millis,
|
||||
unsigned long socket_timeout_millis)
|
||||
: Connection(client, keep_alive_millis, socket_timeout_millis) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
bool BasicClient::connect(
|
||||
const char * host,
|
||||
uint16_t port,
|
||||
const char * id,
|
||||
const char * user,
|
||||
const char * pass,
|
||||
const char * will_topic,
|
||||
const char * will_message,
|
||||
const size_t will_message_length,
|
||||
uint8_t will_qos,
|
||||
bool will_retain,
|
||||
const bool clean_session,
|
||||
ConnectReturnCode * connect_return_code) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
if (connect_return_code) {
|
||||
*connect_return_code = CRC_UNDEFINED;
|
||||
}
|
||||
|
||||
client.stop();
|
||||
|
||||
if (!client.connect(host, port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
message_id_generator.reset();
|
||||
|
||||
const bool will = will_topic && will_message;
|
||||
|
||||
const uint8_t connect_flags =
|
||||
(user ? 1 : 0) << 7
|
||||
| (user && pass ? 1 : 0) << 6
|
||||
| (will && will_retain ? 1 : 0) << 5
|
||||
| (will && will_qos ? 1 : 0) << 3
|
||||
| (will ? 1 : 0) << 2
|
||||
| (clean_session ? 1 : 0) << 1;
|
||||
|
||||
const size_t client_id_length = strlen(id);
|
||||
const size_t will_topic_length = (will && will_topic) ? strlen(will_topic) : 0;
|
||||
const size_t user_length = user ? strlen(user) : 0;
|
||||
const size_t pass_length = pass ? strlen(pass) : 0;
|
||||
|
||||
const size_t total_size = 6 // protocol name
|
||||
+ 1 // protocol level
|
||||
+ 1 // connect flags
|
||||
+ 2 // keep-alive
|
||||
+ client_id_length + 2
|
||||
+ (will ? will_topic_length + 2 : 0)
|
||||
+ (will ? will_message_length + 2 : 0)
|
||||
+ (user ? user_length + 2 : 0)
|
||||
+ (user && pass ? pass_length + 2 : 0);
|
||||
|
||||
auto packet = build_packet(Packet::CONNECT, 0, total_size);
|
||||
packet.write_string("MQTT", 4);
|
||||
packet.write_u8(4);
|
||||
packet.write_u8(connect_flags);
|
||||
packet.write_u16(keep_alive_millis / 1000);
|
||||
packet.write_string(id, client_id_length);
|
||||
|
||||
if (will) {
|
||||
packet.write_string(will_topic, will_topic_length);
|
||||
packet.write_string(will_message, will_message_length);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
packet.write_string(user, user_length);
|
||||
if (pass) {
|
||||
packet.write_string(pass, pass_length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!packet.send()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wait_for_reply(Packet::CONNACK, [this, connect_return_code](IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
if (packet.size != 2) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
/* const uint8_t connect_ack_flags = */ packet.read_u8();
|
||||
const uint8_t crc = packet.read_u8();
|
||||
|
||||
if (connect_return_code) {
|
||||
*connect_return_code = (ConnectReturnCode) crc;
|
||||
}
|
||||
|
||||
if (crc != 0) {
|
||||
// connection refused
|
||||
client.stop();
|
||||
}
|
||||
});
|
||||
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
void BasicClient::loop() {
|
||||
TRACE_FUNCTION
|
||||
|
||||
if (client.connected() && get_millis_since_last_write() >= keep_alive_millis) {
|
||||
// ping time!
|
||||
build_packet(Packet::PINGREQ).send();
|
||||
wait_for_reply(Packet::PINGRESP, [](IncomingPacket &) {});
|
||||
}
|
||||
|
||||
Connection::loop();
|
||||
}
|
||||
|
||||
Publisher::Publish BasicClient::begin_publish(const char * topic, const size_t payload_size,
|
||||
uint8_t qos, bool retain, uint16_t message_id) {
|
||||
TRACE_FUNCTION
|
||||
return Publish(
|
||||
*this,
|
||||
client.connected() ? client : PrintMux(),
|
||||
topic, payload_size,
|
||||
(qos >= 1) ? 1 : 0,
|
||||
retain,
|
||||
message_id, // dup if message_id is non-zero
|
||||
message_id ? message_id : message_id_generator.generate() // generate only if message_id == 0
|
||||
);
|
||||
}
|
||||
|
||||
bool BasicClient::on_publish_complete(const Publish & publish) {
|
||||
TRACE_FUNCTION
|
||||
if (publish.qos == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool confirmed = false;
|
||||
wait_for_reply(Packet::PUBACK, [&publish, &confirmed](IncomingPacket & puback) {
|
||||
confirmed |= (puback.read_u16() == publish.message_id);
|
||||
});
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
bool BasicClient::subscribe(const String & topic, uint8_t qos, uint8_t * qos_granted) {
|
||||
TRACE_FUNCTION
|
||||
if (qos > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t topic_size = topic.length();
|
||||
const uint16_t message_id = message_id_generator.generate();
|
||||
|
||||
auto packet = build_packet(Packet::SUBSCRIBE, 0b0010, 2 + 2 + topic_size + 1);
|
||||
packet.write_u16(message_id);
|
||||
packet.write_string(topic.c_str(), topic_size);
|
||||
packet.write_u8(qos);
|
||||
packet.send();
|
||||
|
||||
uint8_t code = 0x80;
|
||||
|
||||
wait_for_reply(Packet::SUBACK, [this, message_id, &code](IncomingPacket & packet) {
|
||||
if (packet.read_u16() != message_id) {
|
||||
on_protocol_violation();
|
||||
} else {
|
||||
code = packet.read_u8();
|
||||
}
|
||||
});
|
||||
|
||||
if (code == 0x80) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qos_granted) {
|
||||
*qos_granted = code;
|
||||
}
|
||||
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
bool BasicClient::unsubscribe(const String & topic) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
const size_t topic_size = topic.length();
|
||||
const uint16_t message_id = message_id_generator.generate();
|
||||
|
||||
auto packet = build_packet(Packet::UNSUBSCRIBE, 0b0010, 2 + 2 + topic_size);
|
||||
packet.write_u16(message_id);
|
||||
packet.write_string(topic.c_str(), topic_size);
|
||||
packet.send();
|
||||
|
||||
wait_for_reply(Packet::UNSUBACK, [this, message_id](IncomingPacket & packet) {
|
||||
if (packet.read_u16() != message_id) {
|
||||
on_protocol_violation();
|
||||
}
|
||||
});
|
||||
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
Client::Client(ClientSocketInterface * socket,
|
||||
const char * host, uint16_t port, const char * id, const char * user, const char * password,
|
||||
unsigned long reconnect_interval_millis, unsigned long keep_alive_millis, unsigned long socket_timeout_millis)
|
||||
: SocketOwner<std::unique_ptr<ClientSocketInterface>>(socket),
|
||||
BasicClient(this->socket->get_client(), keep_alive_millis, socket_timeout_millis),
|
||||
host(host), port(port), client_id(id), username(user), password(password),
|
||||
will({"", "", 0, false}),
|
||||
reconnect_interval_millis(reconnect_interval_millis),
|
||||
last_reconnect_attempt(millis() - reconnect_interval_millis) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
Client::SubscriptionId Client::subscribe(const String & topic_filter, MessageCallback callback) {
|
||||
TRACE_FUNCTION
|
||||
const auto ret = SubscribedMessageListener::subscribe(topic_filter, callback);
|
||||
BasicClient::subscribe(topic_filter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Client::unsubscribe(const String & topic_filter) {
|
||||
TRACE_FUNCTION
|
||||
BasicClient::unsubscribe(topic_filter);
|
||||
SubscribedMessageListener::unsubscribe(topic_filter);
|
||||
}
|
||||
|
||||
void Client::on_message(const char * topic, IncomingPacket & packet) {
|
||||
SubscribedMessageListener::fire_message_callbacks(topic, packet);
|
||||
}
|
||||
|
||||
void Client::loop() {
|
||||
TRACE_FUNCTION
|
||||
if (!client.connected()) {
|
||||
if (host.isEmpty() || !port) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (millis() - last_reconnect_attempt < reconnect_interval_millis) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool connection_established = connect(host.c_str(), port,
|
||||
client_id.isEmpty() ? "" : client_id.c_str(),
|
||||
username.isEmpty() ? nullptr : username.c_str(),
|
||||
password.isEmpty() ? nullptr : password.c_str(),
|
||||
will.topic.isEmpty() ? nullptr : will.topic.c_str(),
|
||||
will.payload.isEmpty() ? nullptr : will.payload.c_str(),
|
||||
will.payload.isEmpty() ? 0 : will.payload.length(),
|
||||
will.qos, will.retain);
|
||||
|
||||
last_reconnect_attempt = millis();
|
||||
|
||||
if (!connection_established) {
|
||||
if (connection_failure_callback) {
|
||||
connection_failure_callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto & kv : subscriptions) {
|
||||
BasicClient::subscribe(kv.first.c_str());
|
||||
}
|
||||
|
||||
on_connect();
|
||||
}
|
||||
|
||||
BasicClient::loop();
|
||||
}
|
||||
|
||||
void Client::on_connect() {
|
||||
TRACE_FUNCTION
|
||||
BasicClient::on_connect();
|
||||
if (connected_callback) {
|
||||
connected_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::on_disconnect() {
|
||||
TRACE_FUNCTION
|
||||
BasicClient::on_disconnect();
|
||||
if (disconnected_callback) {
|
||||
disconnected_callback();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
121
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client.h
Normal file
121
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "connection.h"
|
||||
#include "incoming_packet.h"
|
||||
#include "outgoing_packet.h"
|
||||
#include "pico_interface.h"
|
||||
#include "publisher.h"
|
||||
#include "subscriber.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class BasicClient: public PicoMQTTInterface, public Connection, public Publisher {
|
||||
public:
|
||||
BasicClient(::Client & client, unsigned long keep_alive_millis = 60 * 1000,
|
||||
unsigned long socket_timeout_millis = 10 * 1000);
|
||||
|
||||
bool connect(
|
||||
const char * host, uint16_t port = 1883,
|
||||
const char * id = "", const char * user = nullptr, const char * pass = nullptr,
|
||||
const char * will_topic = nullptr, const char * will_message = nullptr,
|
||||
const size_t will_message_length = 0, uint8_t willQos = 0, bool willRetain = false,
|
||||
const bool cleanSession = true,
|
||||
ConnectReturnCode * connect_return_code = nullptr);
|
||||
|
||||
using Publisher::begin_publish;
|
||||
virtual Publish begin_publish(const char * topic, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) override;
|
||||
|
||||
bool subscribe(const String & topic, uint8_t qos = 0, uint8_t * qos_granted = nullptr);
|
||||
bool unsubscribe(const String & topic);
|
||||
|
||||
void loop() override;
|
||||
|
||||
virtual void on_connect() {}
|
||||
|
||||
private:
|
||||
virtual bool on_publish_complete(const Publish & publish) override;
|
||||
};
|
||||
|
||||
class ClientSocketInterface {
|
||||
public:
|
||||
virtual ::Client & get_client() = 0;
|
||||
virtual ~ClientSocketInterface() {}
|
||||
};
|
||||
|
||||
class ClientSocketProxy: public ClientSocketInterface {
|
||||
public:
|
||||
ClientSocketProxy(::Client & client): client(client) {}
|
||||
virtual ::Client & get_client() override { return client; }
|
||||
::Client & client;
|
||||
};
|
||||
|
||||
template <typename ClientType>
|
||||
class ClientSocket: public ClientType, public ClientSocketInterface {
|
||||
public:
|
||||
using ClientType::ClientType;
|
||||
virtual ::Client & get_client() override { return *this; }
|
||||
};
|
||||
|
||||
class Client: public SocketOwner<std::unique_ptr<ClientSocketInterface>>, public BasicClient,
|
||||
public SubscribedMessageListener {
|
||||
public:
|
||||
Client(const char * host = nullptr, uint16_t port = 1883, const char * id = nullptr, const char * user = nullptr,
|
||||
const char * password = nullptr, unsigned long reconnect_interval_millis = 5 * 1000,
|
||||
unsigned long keep_alive_millis = 60 * 1000, unsigned long socket_timeout_millis = 10 * 1000)
|
||||
: Client(new ClientSocket<::WiFiClient>(), host, port, id, user, password, reconnect_interval_millis, keep_alive_millis,
|
||||
socket_timeout_millis) {
|
||||
}
|
||||
|
||||
template <typename ClientType>
|
||||
Client(ClientType & client, const char * host = nullptr, uint16_t port = 1883, const char * id = nullptr,
|
||||
const char * user = nullptr, const char * password = nullptr,
|
||||
unsigned long reconnect_interval_millis = 5 * 1000,
|
||||
unsigned long keep_alive_millis = 60 * 1000, unsigned long socket_timeout_millis = 10 * 1000)
|
||||
: Client(new ClientSocketProxy(client), host, port, id, user, password, reconnect_interval_millis, keep_alive_millis,
|
||||
socket_timeout_millis) {
|
||||
}
|
||||
|
||||
using SubscribedMessageListener::subscribe;
|
||||
virtual SubscriptionId subscribe(const String & topic_filter, MessageCallback callback) override;
|
||||
virtual void unsubscribe(const String & topic_filter) override;
|
||||
|
||||
virtual void loop() override;
|
||||
|
||||
String host;
|
||||
uint16_t port;
|
||||
|
||||
String client_id;
|
||||
String username;
|
||||
String password;
|
||||
|
||||
struct {
|
||||
String topic;
|
||||
String payload;
|
||||
uint8_t qos;
|
||||
bool retain;
|
||||
} will;
|
||||
|
||||
unsigned long reconnect_interval_millis;
|
||||
|
||||
std::function<void()> connected_callback;
|
||||
std::function<void()> disconnected_callback;
|
||||
std::function<void()> connection_failure_callback;
|
||||
|
||||
virtual void on_connect() override;
|
||||
virtual void on_disconnect() override;
|
||||
|
||||
protected:
|
||||
Client(ClientSocketInterface * client,
|
||||
const char * host, uint16_t port, const char * id, const char * user, const char * password,
|
||||
unsigned long reconnect_interval_millis, unsigned long keep_alive_millis, unsigned long socket_timeout_millis);
|
||||
|
||||
unsigned long last_reconnect_attempt;
|
||||
virtual void on_message(const char * topic, IncomingPacket & packet) override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
163
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client_wrapper.cpp
Normal file
163
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client_wrapper.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "client_wrapper.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
ClientWrapper::ClientWrapper(::Client & client, unsigned long socket_timeout_millis):
|
||||
socket_timeout_millis(socket_timeout_millis), client(client) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
// reads
|
||||
int ClientWrapper::available_wait(unsigned long timeout) {
|
||||
TRACE_FUNCTION
|
||||
const unsigned long start_millis = millis();
|
||||
while (true) {
|
||||
const int ret = available();
|
||||
if (ret > 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!connected()) {
|
||||
// A disconnected client might still have unread data waiting in buffers. Don't move this check earlier.
|
||||
return 0;
|
||||
}
|
||||
const unsigned long elapsed = millis() - start_millis;
|
||||
if (elapsed > timeout) {
|
||||
return 0;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
int ClientWrapper::read(uint8_t * buf, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
const unsigned long start_millis = millis();
|
||||
size_t ret = 0;
|
||||
|
||||
while (ret < size) {
|
||||
const unsigned long now_millis = millis();
|
||||
const unsigned long elapsed_millis = now_millis - start_millis;
|
||||
|
||||
if (elapsed_millis > socket_timeout_millis) {
|
||||
// timeout
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned long remaining_millis = socket_timeout_millis - elapsed_millis;
|
||||
|
||||
const int available_size = available_wait(remaining_millis);
|
||||
if (available_size <= 0) {
|
||||
// timeout
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
const int chunk_size = size - ret < (size_t) available_size ? size - ret : (size_t) available_size;
|
||||
|
||||
const int bytes_read = client.read(buf + ret, chunk_size);
|
||||
if (bytes_read <= 0) {
|
||||
// connection error
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
ret += bytes_read;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ClientWrapper::read() {
|
||||
TRACE_FUNCTION
|
||||
if (!available_wait(socket_timeout_millis)) {
|
||||
return -1;
|
||||
}
|
||||
return client.read();
|
||||
}
|
||||
|
||||
int ClientWrapper::peek() {
|
||||
TRACE_FUNCTION
|
||||
if (!available_wait(socket_timeout_millis)) {
|
||||
return -1;
|
||||
}
|
||||
return client.peek();
|
||||
}
|
||||
|
||||
// writes
|
||||
size_t ClientWrapper::write(const uint8_t * buffer, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
size_t ret = 0;
|
||||
|
||||
while (connected() && ret < size) {
|
||||
const int bytes_written = client.write(buffer + ret, size - ret);
|
||||
if (bytes_written <= 0) {
|
||||
// connection error
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret += bytes_written;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t ClientWrapper::write(uint8_t value) {
|
||||
TRACE_FUNCTION
|
||||
return write(&value, 1);
|
||||
}
|
||||
|
||||
// simple wrappers forwarding requests to this->client
|
||||
int ClientWrapper::connect(IPAddress ip, uint16_t port) {
|
||||
TRACE_FUNCTION
|
||||
return client.connect(ip, port);
|
||||
}
|
||||
|
||||
int ClientWrapper::connect(const char * host, uint16_t port) {
|
||||
TRACE_FUNCTION
|
||||
return client.connect(host, port);
|
||||
}
|
||||
|
||||
#ifdef PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
|
||||
int ClientWrapper::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
TRACE_FUNCTION
|
||||
return client.connect(ip, port, timeout);
|
||||
}
|
||||
|
||||
int ClientWrapper::connect(const char * host, uint16_t port, int32_t timeout) {
|
||||
TRACE_FUNCTION
|
||||
return client.connect(host, port, timeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int ClientWrapper::available() {
|
||||
TRACE_FUNCTION
|
||||
return client.available();
|
||||
}
|
||||
|
||||
void ClientWrapper::flush() {
|
||||
TRACE_FUNCTION
|
||||
client.flush();
|
||||
}
|
||||
|
||||
void ClientWrapper::stop() {
|
||||
TRACE_FUNCTION
|
||||
client.stop();
|
||||
}
|
||||
|
||||
uint8_t ClientWrapper::connected() {
|
||||
TRACE_FUNCTION
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
ClientWrapper::operator bool() {
|
||||
return bool(client);
|
||||
}
|
||||
|
||||
}
|
||||
46
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client_wrapper.h
Normal file
46
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/client_wrapper.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class ClientWrapper: public ::Client {
|
||||
public:
|
||||
ClientWrapper(::Client & client, unsigned long socket_timeout_millis);
|
||||
ClientWrapper(const ClientWrapper &) = default;
|
||||
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int read(uint8_t * buf, size_t size) override;
|
||||
virtual size_t write(const uint8_t * buffer, size_t size) override;
|
||||
virtual size_t write(uint8_t value) override final;
|
||||
|
||||
// all of the below call the corresponding method on this->client
|
||||
virtual int connect(IPAddress ip, uint16_t port) override;
|
||||
virtual int connect(const char * host, uint16_t port) override;
|
||||
#ifdef PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) override;
|
||||
virtual int connect(const char * host, uint16_t port, int32_t timeout) override;
|
||||
#endif
|
||||
virtual int available() override;
|
||||
virtual void flush() override;
|
||||
virtual void stop() override;
|
||||
virtual uint8_t connected() override;
|
||||
virtual operator bool() override;
|
||||
|
||||
const unsigned long socket_timeout_millis;
|
||||
|
||||
void abort() {
|
||||
// TODO: Use client.abort() if client is a WiFiClient on ESP8266?
|
||||
stop();
|
||||
}
|
||||
|
||||
protected:
|
||||
::Client & client;
|
||||
|
||||
int available_wait(unsigned long timeout);
|
||||
};
|
||||
|
||||
}
|
||||
37
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/config.h
Normal file
37
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/config.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifndef PICOMQTT_MAX_TOPIC_SIZE
|
||||
#define PICOMQTT_MAX_TOPIC_SIZE 256
|
||||
#endif
|
||||
|
||||
#ifndef PICOMQTT_MAX_MESSAGE_SIZE
|
||||
#define PICOMQTT_MAX_MESSAGE_SIZE 1024
|
||||
#endif
|
||||
|
||||
#ifndef PICOMQTT_MAX_CLIENT_ID_SIZE
|
||||
/*
|
||||
* The MQTT standard requires brokers to accept client ids that are
|
||||
* 1-23 chars long, but allows longer client IDs to be accepted too.
|
||||
*/
|
||||
#define PICOMQTT_MAX_CLIENT_ID_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef PICOMQTT_MAX_USERPASS_SIZE
|
||||
#define PICOMQTT_MAX_USERPASS_SIZE 256
|
||||
#endif
|
||||
|
||||
#ifndef PICOMQTT_OUTGOING_BUFFER_SIZE
|
||||
#define PICOMQTT_OUTGOING_BUFFER_SIZE 128
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
// Uncomment this define to make PicoMQTT compatible with framework variants
|
||||
// which have extra Client::connect methods which accept a timeout parameter.
|
||||
// #define PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
#endif
|
||||
|
||||
// #define PICOMQTT_DEBUG
|
||||
|
||||
// #define PICOMQTT_DEBUG_TRACE_FUNCTIONS
|
||||
168
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/connection.cpp
Normal file
168
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/connection.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "config.h"
|
||||
#include "connection.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
Connection::Connection(::Client & client, unsigned long keep_alive_millis, unsigned long socket_timeout_millis) :
|
||||
client(client, socket_timeout_millis),
|
||||
keep_alive_millis(keep_alive_millis),
|
||||
last_read(millis()), last_write(millis()) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
OutgoingPacket Connection::build_packet(Packet::Type type, uint8_t flags, size_t length) {
|
||||
TRACE_FUNCTION
|
||||
last_write = millis();
|
||||
auto ret = OutgoingPacket(client, type, flags, length);
|
||||
ret.write_header();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Connection::on_timeout() {
|
||||
TRACE_FUNCTION
|
||||
client.abort();
|
||||
on_disconnect();
|
||||
}
|
||||
|
||||
void Connection::on_protocol_violation() {
|
||||
TRACE_FUNCTION
|
||||
on_disconnect();
|
||||
}
|
||||
|
||||
void Connection::on_disconnect() {
|
||||
TRACE_FUNCTION
|
||||
client.stop();
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
TRACE_FUNCTION
|
||||
build_packet(Packet::DISCONNECT).send();
|
||||
client.stop();
|
||||
}
|
||||
|
||||
bool Connection::connected() {
|
||||
TRACE_FUNCTION
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
void Connection::wait_for_reply(Packet::Type type, std::function<void(IncomingPacket & packet)> handler) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
const unsigned long start = millis();
|
||||
|
||||
while (client.connected() && (millis() - start < client.socket_timeout_millis)) {
|
||||
|
||||
IncomingPacket packet(client);
|
||||
if (!packet) {
|
||||
break;
|
||||
}
|
||||
|
||||
last_read = millis();
|
||||
|
||||
if (packet.get_type() == type) {
|
||||
handler(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
handle_packet(packet);
|
||||
|
||||
}
|
||||
|
||||
if (client.connected()) {
|
||||
on_timeout();
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::send_ack(Packet::Type ack_type, uint16_t msg_id) {
|
||||
TRACE_FUNCTION
|
||||
auto ack = build_packet(ack_type, 0, 2);
|
||||
ack.write_u16(msg_id);
|
||||
ack.send();
|
||||
}
|
||||
|
||||
void Connection::handle_packet(IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
switch (packet.get_type()) {
|
||||
case Packet::PUBLISH: {
|
||||
const uint16_t topic_size = packet.read_u16();
|
||||
|
||||
// const bool dup = (packet.get_flags() >> 3) & 0b1;
|
||||
const uint8_t qos = (packet.get_flags() >> 1) & 0b11;
|
||||
// const bool retain = packet.get_flags() & 0b1;
|
||||
|
||||
uint16_t msg_id = 0;
|
||||
|
||||
if (topic_size > PICOMQTT_MAX_TOPIC_SIZE) {
|
||||
packet.ignore(topic_size);
|
||||
on_topic_too_long(packet);
|
||||
if (qos) {
|
||||
msg_id = packet.read_u16();
|
||||
}
|
||||
} else {
|
||||
char topic[topic_size + 1];
|
||||
if (!packet.read_string(topic, topic_size)) {
|
||||
// connection error
|
||||
return;
|
||||
}
|
||||
if (qos) {
|
||||
msg_id = packet.read_u16();
|
||||
}
|
||||
on_message(topic, packet);
|
||||
}
|
||||
|
||||
if (msg_id) {
|
||||
send_ack(qos == 1 ? Packet::PUBACK : Packet::PUBREC, msg_id);
|
||||
}
|
||||
|
||||
break;
|
||||
};
|
||||
|
||||
case Packet::PUBREC:
|
||||
send_ack(Packet::PUBREL, packet.read_u16());
|
||||
break;
|
||||
|
||||
case Packet::PUBREL:
|
||||
send_ack(Packet::PUBCOMP, packet.read_u16());
|
||||
break;
|
||||
|
||||
case Packet::PUBCOMP:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case Packet::DISCONNECT:
|
||||
on_disconnect();
|
||||
break;
|
||||
|
||||
default:
|
||||
on_protocol_violation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long Connection::get_millis_since_last_read() const {
|
||||
TRACE_FUNCTION
|
||||
return millis() - last_read;
|
||||
}
|
||||
|
||||
unsigned long Connection::get_millis_since_last_write() const {
|
||||
TRACE_FUNCTION
|
||||
return millis() - last_write;
|
||||
}
|
||||
|
||||
void Connection::loop() {
|
||||
TRACE_FUNCTION
|
||||
|
||||
// only handle 10 packets max in one go to not starve other connections
|
||||
for (unsigned int i = 0; (i < 10) && client.available(); ++i) {
|
||||
IncomingPacket packet(client);
|
||||
if (!packet.is_valid()) {
|
||||
return;
|
||||
}
|
||||
last_read = millis();
|
||||
handle_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
79
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/connection.h
Normal file
79
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/connection.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "client_wrapper.h"
|
||||
#include "incoming_packet.h"
|
||||
#include "outgoing_packet.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
enum ConnectReturnCode : uint8_t {
|
||||
CRC_ACCEPTED = 0,
|
||||
CRC_UNACCEPTABLE_PROTOCOL_VERSION = 1,
|
||||
CRC_IDENTIFIER_REJECTED = 2,
|
||||
CRC_SERVER_UNAVAILABLE = 3,
|
||||
CRC_BAD_USERNAME_OR_PASSWORD = 4,
|
||||
CRC_NOT_AUTHORIZED = 5,
|
||||
|
||||
// internal
|
||||
CRC_UNDEFINED = 255,
|
||||
};
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
Connection(::Client & client, unsigned long keep_alive_millis = 0,
|
||||
unsigned long socket_timeout_millis = 15 * 1000);
|
||||
|
||||
virtual ~Connection() {}
|
||||
|
||||
bool connected();
|
||||
void disconnect();
|
||||
|
||||
virtual void loop();
|
||||
|
||||
protected:
|
||||
class MessageIdGenerator {
|
||||
public:
|
||||
MessageIdGenerator(): value(0) {}
|
||||
uint16_t generate() {
|
||||
if (++value == 0) { value = 1; }
|
||||
return value;
|
||||
}
|
||||
|
||||
void reset() { value = 0; }
|
||||
|
||||
protected:
|
||||
uint16_t value;
|
||||
} message_id_generator;
|
||||
|
||||
OutgoingPacket build_packet(Packet::Type type, uint8_t flags = 0, size_t length = 0);
|
||||
|
||||
void wait_for_reply(Packet::Type type, std::function<void(IncomingPacket & packet)> handler);
|
||||
|
||||
virtual void on_topic_too_long(const IncomingPacket & packet) {}
|
||||
virtual void on_message(const char * topic, IncomingPacket & packet) {}
|
||||
|
||||
virtual void on_timeout();
|
||||
virtual void on_protocol_violation();
|
||||
virtual void on_disconnect();
|
||||
|
||||
ClientWrapper client;
|
||||
unsigned long keep_alive_millis;
|
||||
|
||||
virtual void handle_packet(IncomingPacket & packet);
|
||||
|
||||
protected:
|
||||
unsigned long get_millis_since_last_read() const;
|
||||
unsigned long get_millis_since_last_write() const;
|
||||
|
||||
private:
|
||||
unsigned long last_read;
|
||||
unsigned long last_write;
|
||||
void send_ack(Packet::Type ack_type, uint16_t msg_id);
|
||||
};
|
||||
|
||||
}
|
||||
50
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/debug.h
Normal file
50
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/debug.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef PICOMQTT_DEBUG_TRACE_FUNCTIONS
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class FunctionTracer {
|
||||
public:
|
||||
FunctionTracer(const char * function_name) : function_name(function_name) {
|
||||
indent(1);
|
||||
Serial.print(F("CALL "));
|
||||
Serial.println(function_name);
|
||||
}
|
||||
|
||||
~FunctionTracer() {
|
||||
indent(-1);
|
||||
Serial.print(F("RETURN "));
|
||||
Serial.println(function_name);
|
||||
}
|
||||
|
||||
const char * const function_name;
|
||||
|
||||
protected:
|
||||
void indent(int delta) {
|
||||
static int depth = 0;
|
||||
if (delta < 0) {
|
||||
depth += delta;
|
||||
}
|
||||
for (int i = 0; i < depth; ++i) {
|
||||
Serial.print(" ");
|
||||
}
|
||||
if (delta > 0) {
|
||||
depth += delta;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define TRACE_FUNCTION PicoMQTT::FunctionTracer _function_tracer(__PRETTY_FUNCTION__);
|
||||
|
||||
#else
|
||||
|
||||
#define TRACE_FUNCTION
|
||||
|
||||
#endif
|
||||
189
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/incoming_packet.cpp
Normal file
189
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/incoming_packet.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "incoming_packet.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
IncomingPacket::IncomingPacket(Client & client)
|
||||
: Packet(read_header(client)), client(client) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
IncomingPacket::IncomingPacket(IncomingPacket && other)
|
||||
: Packet(other), client(other.client) {
|
||||
TRACE_FUNCTION
|
||||
other.pos = size;
|
||||
}
|
||||
|
||||
IncomingPacket::IncomingPacket(const Type type, const uint8_t flags, const size_t size, Client & client)
|
||||
: Packet(type, flags, size), client(client) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
IncomingPacket::~IncomingPacket() {
|
||||
TRACE_FUNCTION
|
||||
#ifdef PICOMQTT_DEBUG
|
||||
if (pos != size) {
|
||||
Serial.print(F("IncomingPacket read incorrect number of bytes: "));
|
||||
Serial.print(pos);
|
||||
Serial.print(F("/"));
|
||||
Serial.println(size);
|
||||
}
|
||||
#endif
|
||||
// read and ignore remaining data
|
||||
while (get_remaining_size() && (read() >= 0));
|
||||
}
|
||||
|
||||
// disabled functions
|
||||
int IncomingPacket::connect(IPAddress ip, uint16_t port) {
|
||||
TRACE_FUNCTION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IncomingPacket::connect(const char * host, uint16_t port) {
|
||||
TRACE_FUNCTION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
int IncomingPacket::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
TRACE_FUNCTION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IncomingPacket::connect(const char * host, uint16_t port, int32_t timeout) {
|
||||
TRACE_FUNCTION;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t IncomingPacket::write(const uint8_t * buffer, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t IncomingPacket::write(uint8_t value) {
|
||||
TRACE_FUNCTION
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IncomingPacket::flush() {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
void IncomingPacket::stop() {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
|
||||
// extended functions
|
||||
int IncomingPacket::available() {
|
||||
TRACE_FUNCTION;
|
||||
return get_remaining_size();
|
||||
}
|
||||
|
||||
int IncomingPacket::peek() {
|
||||
TRACE_FUNCTION
|
||||
if (!get_remaining_size()) {
|
||||
#if PICOMQTT_DEBUG
|
||||
Serial.println(F("Attempt to peek beyond end of IncomingPacket."));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return client.peek();
|
||||
}
|
||||
|
||||
int IncomingPacket::read() {
|
||||
TRACE_FUNCTION
|
||||
if (!get_remaining_size()) {
|
||||
#if PICOMQTT_DEBUG
|
||||
Serial.println(F("Attempt to read beyond end of IncomingPacket."));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
const int ret = client.read();
|
||||
if (ret >= 0) {
|
||||
++pos;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int IncomingPacket::read(uint8_t * buf, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
const size_t remaining = get_remaining_size();
|
||||
const size_t read_size = remaining < size ? remaining : size;
|
||||
#if PICOMQTT_DEBUG
|
||||
if (size > remaining) {
|
||||
Serial.println(F("Attempt to read buf beyond end of IncomingPacket."));
|
||||
}
|
||||
#endif
|
||||
const int ret = client.read(buf, read_size);
|
||||
if (ret > 0) {
|
||||
pos += ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
IncomingPacket::operator bool() {
|
||||
TRACE_FUNCTION
|
||||
return is_valid() && bool(client);
|
||||
}
|
||||
|
||||
uint8_t IncomingPacket::connected() {
|
||||
TRACE_FUNCTION
|
||||
return is_valid() && client.connected();
|
||||
}
|
||||
|
||||
// extra functions
|
||||
uint8_t IncomingPacket::read_u8() {
|
||||
TRACE_FUNCTION;
|
||||
return get_remaining_size() ? read() : 0;
|
||||
}
|
||||
|
||||
uint16_t IncomingPacket::read_u16() {
|
||||
TRACE_FUNCTION;
|
||||
return ((uint16_t) read_u8()) << 8 | ((uint16_t) read_u8());
|
||||
}
|
||||
|
||||
bool IncomingPacket::read_string(char * buffer, size_t len) {
|
||||
if (read((uint8_t *) buffer, len) != (int) len) {
|
||||
return false;
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
void IncomingPacket::ignore(size_t len) {
|
||||
while (len--) {
|
||||
read();
|
||||
}
|
||||
}
|
||||
|
||||
Packet IncomingPacket::read_header(Client & client) {
|
||||
TRACE_FUNCTION
|
||||
const int head = client.read();
|
||||
if (head <= 0) {
|
||||
return Packet();
|
||||
}
|
||||
|
||||
uint32_t size = 0;
|
||||
for (size_t length_size = 0; ; ++length_size) {
|
||||
if (length_size >= 5) {
|
||||
return Packet();
|
||||
}
|
||||
const int digit = client.read();
|
||||
if (digit < 0) {
|
||||
return Packet();
|
||||
}
|
||||
|
||||
size |= (digit & 0x7f) << (7 * length_size);
|
||||
|
||||
if (!(digit & 0x80)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Packet(head, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Client.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "packet.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class IncomingPacket: public Packet, public Client {
|
||||
public:
|
||||
IncomingPacket(Client & client);
|
||||
IncomingPacket(const Type type, const uint8_t flags, const size_t size, Client & client);
|
||||
IncomingPacket(IncomingPacket &&);
|
||||
|
||||
IncomingPacket(const IncomingPacket &) = delete;
|
||||
const IncomingPacket & operator=(const IncomingPacket &) = delete;
|
||||
|
||||
~IncomingPacket();
|
||||
|
||||
virtual int available() override;
|
||||
virtual int connect(IPAddress ip, uint16_t port) override;
|
||||
virtual int connect(const char * host, uint16_t port) override;
|
||||
#ifdef PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) override;
|
||||
virtual int connect(const char * host, uint16_t port, int32_t timeout) override;
|
||||
#endif
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int read(uint8_t * buf, size_t size) override;
|
||||
// This operator is not marked explicit in the Client base class. Still, we're marking it explicit here
|
||||
// to block implicit conversions to integer types.
|
||||
virtual explicit operator bool() override;
|
||||
virtual size_t write(const uint8_t * buffer, size_t size) override;
|
||||
virtual size_t write(uint8_t value) override final;
|
||||
virtual uint8_t connected() override;
|
||||
virtual void flush() override;
|
||||
virtual void stop() override;
|
||||
|
||||
uint8_t read_u8();
|
||||
uint16_t read_u16();
|
||||
bool read_string(char * buffer, size_t len);
|
||||
void ignore(size_t len);
|
||||
|
||||
protected:
|
||||
static Packet read_header(Client & client);
|
||||
|
||||
Client & client;
|
||||
};
|
||||
|
||||
}
|
||||
225
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/outgoing_packet.cpp
Normal file
225
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/outgoing_packet.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include <Client.h>
|
||||
#include <Print.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "outgoing_packet.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
OutgoingPacket::OutgoingPacket(Print & print, Packet::Type type, uint8_t flags, size_t payload_size)
|
||||
: Packet(type, flags, payload_size), print(print),
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
buffer_position(0),
|
||||
#endif
|
||||
state(State::ok) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
OutgoingPacket::OutgoingPacket(OutgoingPacket && other)
|
||||
: OutgoingPacket(other) {
|
||||
TRACE_FUNCTION
|
||||
other.state = State::dead;
|
||||
}
|
||||
|
||||
OutgoingPacket::~OutgoingPacket() {
|
||||
TRACE_FUNCTION
|
||||
#ifdef PICOMQTT_DEBUG
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
if (buffer_position) {
|
||||
Serial.printf("OutgoingPacket has unsent data in the buffer (pos=%u)\n", buffer_position);
|
||||
}
|
||||
#endif
|
||||
switch (state) {
|
||||
case State::ok:
|
||||
Serial.println(F("Unsent OutgoingPacket"));
|
||||
break;
|
||||
case State::sent:
|
||||
if (pos != size) {
|
||||
Serial.print(F("OutgoingPacket sent incorrect number of bytes: "));
|
||||
Serial.print(pos);
|
||||
Serial.print(F("/"));
|
||||
Serial.println(size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_from_client(::Client & client, size_t length) {
|
||||
TRACE_FUNCTION
|
||||
size_t written = 0;
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
while (written < length) {
|
||||
const size_t remaining = length - written;
|
||||
const size_t remaining_buffer_space = PICOMQTT_OUTGOING_BUFFER_SIZE - buffer_position;
|
||||
const size_t chunk_size = remaining < remaining_buffer_space ? remaining : remaining_buffer_space;
|
||||
|
||||
const int read_size = client.read(buffer + buffer_position, chunk_size);
|
||||
if (read_size <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_position += (size_t) read_size;
|
||||
written += (size_t) read_size;
|
||||
|
||||
if (buffer_position >= PICOMQTT_OUTGOING_BUFFER_SIZE) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
#else
|
||||
uint8_t buffer[128] __attribute__((aligned(4)));
|
||||
while (written < length) {
|
||||
const size_t remain = length - written;
|
||||
const size_t chunk_size = sizeof(buffer) < remain ? sizeof(buffer) : remain;
|
||||
const int read_size = client.read(buffer, chunk_size);
|
||||
if (read_size <= 0) {
|
||||
break;
|
||||
}
|
||||
const size_t write_size = print.write(buffer, read_size);
|
||||
written += write_size;
|
||||
if (!write_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pos += written;
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_zero(size_t length) {
|
||||
TRACE_FUNCTION
|
||||
for (size_t written = 0; written < length; ++written) {
|
||||
write_u8('0');
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
size_t OutgoingPacket::write(const void * data, size_t remaining, void * (*memcpy_fn)(void *, const void *, size_t n)) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
const char * src = (const char *) data;
|
||||
|
||||
while (remaining) {
|
||||
const size_t remaining_buffer_space = PICOMQTT_OUTGOING_BUFFER_SIZE - buffer_position;
|
||||
const size_t chunk_size = remaining < remaining_buffer_space ? remaining : remaining_buffer_space;
|
||||
|
||||
memcpy_fn(buffer + buffer_position, src, chunk_size);
|
||||
|
||||
buffer_position += chunk_size;
|
||||
src += chunk_size;
|
||||
remaining -= chunk_size;
|
||||
|
||||
if (buffer_position >= PICOMQTT_OUTGOING_BUFFER_SIZE) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
const size_t written = src - (const char *) data;
|
||||
pos += written;
|
||||
return written;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t OutgoingPacket::write(const uint8_t * data, size_t length) {
|
||||
TRACE_FUNCTION
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
return write(data, length, memcpy);
|
||||
#else
|
||||
const size_t written = print.write(data, length);
|
||||
pos += written;
|
||||
return written;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_P(PGM_P data, size_t length) {
|
||||
TRACE_FUNCTION
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
return write(data, length, memcpy_P);
|
||||
#else
|
||||
// here we will need a buffer
|
||||
uint8_t buffer[128] __attribute__((aligned(4)));
|
||||
size_t written = 0;
|
||||
while (written < length) {
|
||||
const size_t remain = length - written;
|
||||
const size_t chunk_size = sizeof(buffer) < remain ? sizeof(buffer) : remain;
|
||||
memcpy_P(buffer, data, chunk_size);
|
||||
const size_t write_size = print.write(buffer, chunk_size);
|
||||
written += write_size;
|
||||
data += write_size;
|
||||
if (!write_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos += written;
|
||||
return written;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_u8(uint8_t c) {
|
||||
TRACE_FUNCTION
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_u16(uint16_t value) {
|
||||
TRACE_FUNCTION
|
||||
return write_u8(value >> 8) + write_u8(value & 0xff);
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_string(const char * string, uint16_t size) {
|
||||
TRACE_FUNCTION
|
||||
return write_u16(size) + write((const uint8_t *) string, size);
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_packet_length(size_t length) {
|
||||
TRACE_FUNCTION
|
||||
size_t ret = 0;
|
||||
do {
|
||||
const uint8_t digit = length & 127; // digit := length % 128
|
||||
length >>= 7; // length := length / 128
|
||||
ret += write_u8(digit | (length ? 0x80 : 0));
|
||||
} while (length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t OutgoingPacket::write_header() {
|
||||
TRACE_FUNCTION
|
||||
const size_t ret = write_u8(head) + write_packet_length(size);
|
||||
// we've just written the header, payload starts now
|
||||
pos = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OutgoingPacket::flush() {
|
||||
TRACE_FUNCTION
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
print.write(buffer, buffer_position);
|
||||
buffer_position = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OutgoingPacket::send() {
|
||||
TRACE_FUNCTION
|
||||
const size_t remaining_size = get_remaining_size();
|
||||
if (remaining_size) {
|
||||
#ifdef PICOMQTT_DEBUG
|
||||
Serial.printf("OutgoingPacket sent called on incomplete payload (%u / %u), filling with zeros.\n", pos, size);
|
||||
#endif
|
||||
write_zero(remaining_size);
|
||||
}
|
||||
flush();
|
||||
switch (state) {
|
||||
case State::ok:
|
||||
// print.flush();
|
||||
state = State::sent;
|
||||
__attribute__((fallthrough));
|
||||
case State::sent:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
// #define MQTT_OUTGOING_PACKET_DEBUG
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "packet.h"
|
||||
|
||||
class Print;
|
||||
class Client;
|
||||
|
||||
#if PICOMQTT_OUTGOING_BUFFER_SIZE == 0
|
||||
#define PICOMQTT_UNBUFFERED
|
||||
#endif
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class OutgoingPacket: public Packet, public Print {
|
||||
public:
|
||||
OutgoingPacket(Print & print, Type type, uint8_t flags, size_t payload_size);
|
||||
virtual ~OutgoingPacket();
|
||||
|
||||
const OutgoingPacket & operator=(const OutgoingPacket &) = delete;
|
||||
OutgoingPacket(OutgoingPacket && other);
|
||||
|
||||
virtual size_t write(const uint8_t * data, size_t length) override;
|
||||
virtual size_t write(uint8_t value) override final { return write(&value, 1); }
|
||||
|
||||
size_t write_P(PGM_P data, size_t length);
|
||||
size_t write_u8(uint8_t value);
|
||||
size_t write_u16(uint16_t value);
|
||||
size_t write_string(const char * string, uint16_t size);
|
||||
size_t write_header();
|
||||
|
||||
size_t write_from_client(::Client & c, size_t length);
|
||||
size_t write_zero(size_t count);
|
||||
|
||||
virtual void flush() override;
|
||||
virtual bool send();
|
||||
|
||||
protected:
|
||||
OutgoingPacket(const OutgoingPacket &) = default;
|
||||
|
||||
size_t write(const void * data, size_t length, void * (*memcpy_fn)(void *, const void *, size_t n));
|
||||
size_t write_packet_length(size_t length);
|
||||
|
||||
Print & print;
|
||||
|
||||
#ifndef PICOMQTT_UNBUFFERED
|
||||
uint8_t buffer[PICOMQTT_OUTGOING_BUFFER_SIZE] __attribute__((aligned(4)));
|
||||
|
||||
size_t buffer_position;
|
||||
#endif
|
||||
|
||||
enum class State {
|
||||
ok,
|
||||
sent,
|
||||
error,
|
||||
dead,
|
||||
} state;
|
||||
};
|
||||
|
||||
}
|
||||
49
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/packet.h
Normal file
49
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/packet.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
enum Type : uint8_t {
|
||||
ERROR = 0,
|
||||
CONNECT = 1 << 4, // Client request to connect to Server
|
||||
CONNACK = 2 << 4, // Connect Acknowledgment
|
||||
PUBLISH = 3 << 4, // Publish message
|
||||
PUBACK = 4 << 4, // Publish Acknowledgment
|
||||
PUBREC = 5 << 4, // Publish Received (assured delivery part 1)
|
||||
PUBREL = 6 << 4, // Publish Release (assured delivery part 2)
|
||||
PUBCOMP = 7 << 4, // Publish Complete (assured delivery part 3)
|
||||
SUBSCRIBE = 8 << 4, // Client Subscribe request
|
||||
SUBACK = 9 << 4, // Subscribe Acknowledgment
|
||||
UNSUBSCRIBE = 10 << 4, // Client Unsubscribe request
|
||||
UNSUBACK = 11 << 4, // Unsubscribe Acknowledgment
|
||||
PINGREQ = 12 << 4, // PING Request
|
||||
PINGRESP = 13 << 4, // PING Response
|
||||
DISCONNECT = 14 << 4, // Client is Disconnecting
|
||||
};
|
||||
|
||||
Packet(uint8_t head, size_t size)
|
||||
: head(head), size(size), pos(0) {}
|
||||
|
||||
Packet(Type type = ERROR, const uint8_t flags = 0, size_t size = 0)
|
||||
: Packet((uint8_t) type | (flags & 0xf), size) {
|
||||
}
|
||||
|
||||
virtual ~Packet() {}
|
||||
|
||||
Type get_type() const { return Type(head & 0xf0); }
|
||||
uint8_t get_flags() const { return head & 0x0f; }
|
||||
|
||||
bool is_valid() { return get_type() != ERROR; }
|
||||
size_t get_remaining_size() const { return pos < size ? size - pos : 0; }
|
||||
|
||||
const uint8_t head;
|
||||
const size_t size;
|
||||
|
||||
protected:
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
}
|
||||
13
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/pico_interface.h
Normal file
13
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/pico_interface.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class PicoMQTTInterface {
|
||||
public:
|
||||
virtual ~PicoMQTTInterface() {}
|
||||
virtual void begin() {}
|
||||
virtual void stop() {}
|
||||
virtual void loop() {}
|
||||
};
|
||||
|
||||
}
|
||||
29
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/print_mux.cpp
Normal file
29
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/print_mux.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "print_mux.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
size_t PrintMux::write(uint8_t value) {
|
||||
TRACE_FUNCTION
|
||||
for (auto print_ptr : prints) {
|
||||
print_ptr->write(value);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t PrintMux::write(const uint8_t * buffer, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
for (auto print_ptr : prints) {
|
||||
print_ptr->write(buffer, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void PrintMux::flush() {
|
||||
TRACE_FUNCTION
|
||||
for (auto print_ptr : prints) {
|
||||
print_ptr->flush();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
29
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/print_mux.h
Normal file
29
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/print_mux.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class PrintMux: public ::Print {
|
||||
public:
|
||||
PrintMux() {}
|
||||
|
||||
PrintMux(Print & print) : prints({&print}) {}
|
||||
|
||||
void add(Print & print) {
|
||||
prints.push_back(&print);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t) override;
|
||||
virtual size_t write(const uint8_t * buffer, size_t size) override;
|
||||
virtual void flush();
|
||||
|
||||
size_t size() const { return prints.size(); }
|
||||
|
||||
protected:
|
||||
std::vector<Print *> prints;
|
||||
};
|
||||
|
||||
}
|
||||
56
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/publisher.cpp
Normal file
56
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/publisher.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "publisher.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
Publisher::Publish::Publish(Publisher & publisher, const PrintMux & print,
|
||||
uint8_t flags, size_t total_size,
|
||||
const char * topic, size_t topic_size,
|
||||
uint16_t message_id)
|
||||
:
|
||||
OutgoingPacket(this->print, Packet::PUBLISH, flags, total_size),
|
||||
qos((flags >> 1) & 0b11),
|
||||
message_id(message_id),
|
||||
print(print),
|
||||
publisher(publisher) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
OutgoingPacket::write_header();
|
||||
write_string(topic, topic_size);
|
||||
if (qos) {
|
||||
write_u16(message_id);
|
||||
}
|
||||
}
|
||||
|
||||
Publisher::Publish::Publish(Publisher & publisher, const PrintMux & print,
|
||||
const char * topic, size_t topic_size, size_t payload_size,
|
||||
uint8_t qos, bool retain, bool dup, uint16_t message_id)
|
||||
: Publish(
|
||||
publisher, print,
|
||||
(dup ? 0b1000 : 0) | ((qos & 0b11) << 1) | (retain ? 1 : 0), // flags
|
||||
2 + topic_size + (qos ? 2 : 0) + payload_size, // total size
|
||||
topic, topic_size, // topic
|
||||
message_id) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
Publisher::Publish::Publish(Publisher & publisher, const PrintMux & print,
|
||||
const char * topic, size_t payload_size,
|
||||
uint8_t qos, bool retain, bool dup, uint16_t message_id)
|
||||
: Publish(
|
||||
publisher, print,
|
||||
topic, strlen(topic), payload_size,
|
||||
qos, retain, dup, message_id) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
Publisher::Publish::~Publish() {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
bool Publisher::Publish::send() {
|
||||
TRACE_FUNCTION
|
||||
return OutgoingPacket::send() && publisher.on_publish_complete(*this);
|
||||
}
|
||||
|
||||
}
|
||||
103
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/publisher.h
Normal file
103
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/publisher.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "outgoing_packet.h"
|
||||
#include "print_mux.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class Publisher {
|
||||
public:
|
||||
class Publish: public OutgoingPacket {
|
||||
private:
|
||||
Publish(Publisher & publisher, const PrintMux & print,
|
||||
uint8_t flags, size_t total_size,
|
||||
const char * topic, size_t topic_size,
|
||||
uint16_t message_id);
|
||||
|
||||
public:
|
||||
Publish(Publisher & publisher, const PrintMux & print,
|
||||
const char * topic, size_t topic_size, size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, bool dup = false, uint16_t message_id = 0);
|
||||
|
||||
Publish(Publisher & publisher, const PrintMux & print,
|
||||
const char * topic, size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, bool dup = false, uint16_t message_id = 0);
|
||||
|
||||
~Publish();
|
||||
|
||||
virtual bool send() override;
|
||||
|
||||
const uint8_t qos;
|
||||
const uint16_t message_id;
|
||||
PrintMux print;
|
||||
Publisher & publisher;
|
||||
};
|
||||
|
||||
virtual Publish begin_publish(const char * topic, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) = 0;
|
||||
|
||||
Publish begin_publish(const String & topic, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
return begin_publish(topic.c_str(), payload_size, qos, retain, message_id);
|
||||
}
|
||||
|
||||
virtual bool publish(const char * topic, const void * payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
auto packet = begin_publish(get_c_str(topic), payload_size, qos, retain, message_id);
|
||||
packet.write((const uint8_t *) payload, payload_size);
|
||||
return packet.send();
|
||||
}
|
||||
|
||||
bool publish(const String & topic, const void * payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
return publish(topic.c_str(), payload, payload_size, qos, retain, message_id);
|
||||
}
|
||||
|
||||
template <typename TopicStringType, typename PayloadStringType>
|
||||
bool publish(TopicStringType topic, PayloadStringType payload,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
return publish(get_c_str(topic), (const void *) get_c_str(payload), get_c_str_len(payload),
|
||||
qos, retain, message_id);
|
||||
}
|
||||
|
||||
virtual bool publish_P(const char * topic, PGM_P payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
auto packet = begin_publish(topic, payload_size, qos, retain, message_id);
|
||||
packet.write_P(payload, payload_size);
|
||||
return packet.send();
|
||||
}
|
||||
|
||||
bool publish_P(const String & topic, PGM_P payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
return publish_P(topic.c_str(), payload, payload_size, qos, retain, message_id);
|
||||
}
|
||||
|
||||
template <typename TopicStringType>
|
||||
bool publish_P(TopicStringType topic, PGM_P payload,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) {
|
||||
TRACE_FUNCTION
|
||||
return publish_P(get_c_str(topic), payload, strlen_P(payload),
|
||||
qos, retain, message_id);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool on_publish_complete(const Publish & publish) { return true; }
|
||||
|
||||
static const char * get_c_str(const char * string) { return string; }
|
||||
static const char * get_c_str(const String & string) { return string.c_str(); }
|
||||
static size_t get_c_str_len(const char * string) { return strlen(string); }
|
||||
static size_t get_c_str_len(const String & string) { return string.length(); }
|
||||
};
|
||||
|
||||
}
|
||||
431
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/server.cpp
Normal file
431
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/server.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "server.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class BufferClient: public ::Client {
|
||||
public:
|
||||
BufferClient(const void * ptr): ptr((const char *) ptr) { TRACE_FUNCTION }
|
||||
|
||||
// these methods are nop dummies
|
||||
virtual int connect(IPAddress ip, uint16_t port) override final { TRACE_FUNCTION return 0; }
|
||||
virtual int connect(const char * host, uint16_t port) override final { TRACE_FUNCTION return 0; }
|
||||
#ifdef PICOMQTT_EXTRA_CONNECT_METHODS
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) override final { TRACE_FUNCTION return 0; }
|
||||
virtual int connect(const char * host, uint16_t port, int32_t timeout) override final { TRACE_FUNCTION return 0; }
|
||||
#endif
|
||||
virtual size_t write(const uint8_t * buffer, size_t size) override final { TRACE_FUNCTION return 0; }
|
||||
virtual size_t write(uint8_t value) override final { TRACE_FUNCTION return 0; }
|
||||
virtual void flush() override final { TRACE_FUNCTION }
|
||||
virtual void stop() override final { TRACE_FUNCTION }
|
||||
|
||||
// these methods are in jasager mode
|
||||
virtual int available() override final { TRACE_FUNCTION return std::numeric_limits<int>::max(); }
|
||||
virtual operator bool() override final { TRACE_FUNCTION return true; }
|
||||
virtual uint8_t connected() override final { TRACE_FUNCTION return true; }
|
||||
|
||||
// actual reads implemented here
|
||||
virtual int read(uint8_t * buf, size_t size) override {
|
||||
memcpy(buf, ptr, size);
|
||||
ptr += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
virtual int read() override final {
|
||||
TRACE_FUNCTION
|
||||
uint8_t ret;
|
||||
read(&ret, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual int peek() override final {
|
||||
TRACE_FUNCTION
|
||||
const int ret = read();
|
||||
--ptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char * ptr;
|
||||
};
|
||||
|
||||
class BufferClientP: public BufferClient {
|
||||
public:
|
||||
using BufferClient::BufferClient;
|
||||
|
||||
virtual int read(uint8_t * buf, size_t size) override {
|
||||
memcpy_P(buf, ptr, size);
|
||||
ptr += size;
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
Server::Client::Client(Server & server, ::Client * client)
|
||||
:
|
||||
SocketOwner(client),
|
||||
Connection(*socket, 0, server.socket_timeout_millis), server(server), client_id("<unknown>") {
|
||||
TRACE_FUNCTION
|
||||
wait_for_reply(Packet::CONNECT, [this](IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
auto connack = [this](ConnectReturnCode crc) {
|
||||
TRACE_FUNCTION
|
||||
auto connack = build_packet(Packet::CONNACK, 0, 2);
|
||||
connack.write_u8(0); /* session present always set to zero */
|
||||
connack.write_u8(crc);
|
||||
connack.send();
|
||||
if (crc != CRC_ACCEPTED) {
|
||||
Connection::client.stop();
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
// MQTT protocol identifier
|
||||
char buf[4];
|
||||
|
||||
if (packet.read_u16() != 4) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
packet.read((uint8_t *) buf, 4);
|
||||
|
||||
if (memcmp(buf, "MQTT", 4) != 0) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t protocol_level = packet.read_u8();
|
||||
if (protocol_level != 4) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t connect_flags = packet.read_u8();
|
||||
const bool has_user = connect_flags & (1 << 7);
|
||||
const bool has_pass = connect_flags & (1 << 6);
|
||||
const bool will_retain = connect_flags & (1 << 5);
|
||||
const uint8_t will_qos = (connect_flags >> 3) & 0b11;
|
||||
const bool has_will = connect_flags & (1 << 2);
|
||||
/* const bool clean_session = connect_flags & (1 << 1); */
|
||||
|
||||
if ((has_pass && !has_user)
|
||||
|| (will_qos > 2)
|
||||
|| (!has_will && ((will_qos > 0) || will_retain))) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned long keep_alive_seconds = packet.read_u16();
|
||||
keep_alive_millis = keep_alive_seconds ? (keep_alive_seconds * 1000 + this->server.keep_alive_tolerance_millis) : 0;
|
||||
|
||||
{
|
||||
const size_t client_id_size = packet.read_u16();
|
||||
if (client_id_size > PICOMQTT_MAX_CLIENT_ID_SIZE) {
|
||||
connack(CRC_IDENTIFIER_REJECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
char client_id_buffer[client_id_size + 1];
|
||||
packet.read_string(client_id_buffer, client_id_size);
|
||||
client_id = client_id_buffer;
|
||||
}
|
||||
|
||||
if (client_id.isEmpty()) {
|
||||
client_id = String((unsigned int)(this), HEX);
|
||||
}
|
||||
|
||||
if (has_will) {
|
||||
packet.ignore(packet.read_u16()); // will topic
|
||||
packet.ignore(packet.read_u16()); // will payload
|
||||
}
|
||||
|
||||
// read username
|
||||
const size_t user_size = has_user ? packet.read_u16() : 0;
|
||||
if (user_size > PICOMQTT_MAX_USERPASS_SIZE) {
|
||||
connack(CRC_BAD_USERNAME_OR_PASSWORD);
|
||||
return;
|
||||
}
|
||||
char user[user_size + 1];
|
||||
if (user_size && !packet.read_string(user, user_size)) {
|
||||
on_timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
// read password
|
||||
const size_t pass_size = has_pass ? packet.read_u16() : 0;
|
||||
if (pass_size > PICOMQTT_MAX_USERPASS_SIZE) {
|
||||
connack(CRC_BAD_USERNAME_OR_PASSWORD);
|
||||
return;
|
||||
}
|
||||
char pass[pass_size + 1];
|
||||
if (pass_size && !packet.read_string(pass, pass_size)) {
|
||||
on_timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto connect_return_code = this->server.auth(
|
||||
client_id.c_str(),
|
||||
has_user ? user : nullptr, has_pass ? pass : nullptr);
|
||||
|
||||
connack(connect_return_code);
|
||||
});
|
||||
}
|
||||
|
||||
void Server::Client::on_message(const char * topic, IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
const size_t payload_size = packet.get_remaining_size();
|
||||
auto publish = server.begin_publish(topic, payload_size);
|
||||
|
||||
// Always notify the server about the message
|
||||
{
|
||||
IncomingPublish incoming_publish(packet, publish);
|
||||
server.on_message(topic, incoming_publish);
|
||||
}
|
||||
|
||||
publish.send();
|
||||
}
|
||||
|
||||
void Server::Client::on_subscribe(IncomingPacket & subscribe) {
|
||||
TRACE_FUNCTION
|
||||
const uint16_t message_id = subscribe.read_u16();
|
||||
|
||||
if ((subscribe.get_flags() != 0b0010) || !message_id) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
std::list<uint8_t> suback_codes;
|
||||
|
||||
while (subscribe.get_remaining_size()) {
|
||||
const size_t topic_size = subscribe.read_u16();
|
||||
if (topic_size > PICOMQTT_MAX_TOPIC_SIZE) {
|
||||
subscribe.ignore(topic_size);
|
||||
subscribe.read_u8();
|
||||
suback_codes.push_back(0x80);
|
||||
} else {
|
||||
char topic[topic_size + 1];
|
||||
if (!subscribe.read_string(topic, topic_size)) {
|
||||
// connection error
|
||||
return;
|
||||
}
|
||||
uint8_t qos = subscribe.read_u8();
|
||||
if (qos > 2) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
this->subscribe(topic);
|
||||
server.on_subscribe(client_id.c_str(), topic);
|
||||
suback_codes.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
auto suback = build_packet(Packet::SUBACK, 0, 2 + suback_codes.size());
|
||||
suback.write_u16(message_id);
|
||||
for (uint8_t code : suback_codes) {
|
||||
suback.write_u8(code);
|
||||
}
|
||||
suback.send();
|
||||
}
|
||||
|
||||
void Server::Client::on_unsubscribe(IncomingPacket & unsubscribe) {
|
||||
TRACE_FUNCTION
|
||||
const uint16_t message_id = unsubscribe.read_u16();
|
||||
|
||||
if ((unsubscribe.get_flags() != 0b0010) || !message_id) {
|
||||
on_protocol_violation();
|
||||
return;
|
||||
}
|
||||
|
||||
while (unsubscribe.get_remaining_size()) {
|
||||
const size_t topic_size = unsubscribe.read_u16();
|
||||
if (topic_size > PICOMQTT_MAX_TOPIC_SIZE) {
|
||||
unsubscribe.ignore(topic_size);
|
||||
} else {
|
||||
char topic[topic_size + 1];
|
||||
if (!unsubscribe.read_string(topic, topic_size)) {
|
||||
// connection error
|
||||
return;
|
||||
}
|
||||
server.on_unsubscribe(client_id.c_str(), topic);
|
||||
this->unsubscribe(topic);
|
||||
}
|
||||
}
|
||||
|
||||
auto unsuback = build_packet(Packet::UNSUBACK, 0, 2);
|
||||
unsuback.write_u16(message_id);
|
||||
unsuback.send();
|
||||
}
|
||||
|
||||
const char * Server::Client::get_subscription_pattern(Server::Client::SubscriptionId id) const {
|
||||
for (const auto & pattern : subscriptions)
|
||||
if (pattern.id == id) {
|
||||
return pattern.c_str();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Server::Client::SubscriptionId Server::Client::get_subscription(const char * topic) const {
|
||||
TRACE_FUNCTION
|
||||
for (const auto & pattern : subscriptions)
|
||||
if (topic_matches(pattern.c_str(), topic)) {
|
||||
return pattern.id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Server::Client::SubscriptionId Server::Client::subscribe(const String & topic_filter) {
|
||||
TRACE_FUNCTION
|
||||
const Subscription subscription(topic_filter.c_str());
|
||||
subscriptions.insert(subscription);
|
||||
return subscription.id;
|
||||
}
|
||||
|
||||
void Server::Client::unsubscribe(const String & topic_filter) {
|
||||
TRACE_FUNCTION
|
||||
subscriptions.erase(topic_filter.c_str());
|
||||
}
|
||||
|
||||
void Server::Client::handle_packet(IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
|
||||
switch (packet.get_type()) {
|
||||
case Packet::PINGREQ:
|
||||
build_packet(Packet::PINGRESP).send();
|
||||
return;
|
||||
|
||||
case Packet::SUBSCRIBE:
|
||||
on_subscribe(packet);
|
||||
return;
|
||||
|
||||
case Packet::UNSUBSCRIBE:
|
||||
on_unsubscribe(packet);
|
||||
return;
|
||||
|
||||
default:
|
||||
Connection::handle_packet(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::Client::loop() {
|
||||
TRACE_FUNCTION
|
||||
if (keep_alive_millis && (get_millis_since_last_read() > keep_alive_millis)) {
|
||||
// ping timeout
|
||||
on_timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
Connection::loop();
|
||||
}
|
||||
|
||||
Server::IncomingPublish::IncomingPublish(IncomingPacket & packet, Publish & publish)
|
||||
: IncomingPacket(std::move(packet)), publish(publish) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
Server::IncomingPublish::~IncomingPublish() {
|
||||
TRACE_FUNCTION
|
||||
pos += publish.write_from_client(client, get_remaining_size());
|
||||
}
|
||||
|
||||
int Server::IncomingPublish::read(uint8_t * buf, size_t size) {
|
||||
TRACE_FUNCTION
|
||||
const int ret = IncomingPacket::read(buf, size);
|
||||
if (ret > 0) {
|
||||
publish.write(buf, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Server::IncomingPublish::read() {
|
||||
TRACE_FUNCTION
|
||||
const int ret = IncomingPacket::read();
|
||||
if (ret >= 0) {
|
||||
publish.write(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Server::Server(std::unique_ptr<ServerSocketInterface> server)
|
||||
: keep_alive_tolerance_millis(10 * 1000), socket_timeout_millis(5 * 1000), server(std::move(server)) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
void Server::begin() {
|
||||
TRACE_FUNCTION
|
||||
server->begin();
|
||||
}
|
||||
|
||||
void Server::loop() {
|
||||
TRACE_FUNCTION
|
||||
|
||||
::Client * client_ptr = server->accept_client();
|
||||
if (client_ptr) {
|
||||
clients.push_back(std::unique_ptr<Client>(new Client(*this, client_ptr)));
|
||||
on_connected(clients.back()->get_client_id());
|
||||
}
|
||||
|
||||
for (auto it = clients.begin(); it != clients.end();) {
|
||||
Client & client = **it;
|
||||
client.loop();
|
||||
|
||||
if (!client.connected()) {
|
||||
on_disconnected(client.get_client_id());
|
||||
clients.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintMux Server::get_subscribed(const char * topic) {
|
||||
TRACE_FUNCTION
|
||||
PrintMux ret;
|
||||
for (auto & client_ptr : clients) {
|
||||
if (client_ptr->get_subscription(topic)) {
|
||||
ret.add(client_ptr->get_print());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Publisher::Publish Server::begin_publish(const char * topic, const size_t payload_size,
|
||||
uint8_t, bool, uint16_t) {
|
||||
TRACE_FUNCTION
|
||||
return Publish(*this, get_subscribed(topic), topic, payload_size);
|
||||
}
|
||||
|
||||
void Server::on_message(const char * topic, IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
fire_message_callbacks(topic, packet);
|
||||
}
|
||||
|
||||
bool ServerLocalSubscribe::publish(const char * topic, const void * payload, const size_t payload_size,
|
||||
uint8_t qos, bool retain, uint16_t message_id) {
|
||||
TRACE_FUNCTION
|
||||
const bool ret = Server::publish(topic, payload, payload_size, qos, retain, message_id);
|
||||
BufferClient buffer(payload);
|
||||
IncomingPacket packet(IncomingPacket::PUBLISH, 0, payload_size, buffer);
|
||||
fire_message_callbacks(topic, packet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ServerLocalSubscribe::publish_P(const char * topic, PGM_P payload, const size_t payload_size,
|
||||
uint8_t qos, bool retain, uint16_t message_id) {
|
||||
TRACE_FUNCTION
|
||||
const bool ret = Server::publish_P(topic, payload, payload_size, qos, retain, message_id);
|
||||
BufferClientP buffer((void *) payload);
|
||||
IncomingPacket packet(IncomingPacket::PUBLISH, 0, payload_size, buffer);
|
||||
fire_message_callbacks(topic, packet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
230
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/server.h
Normal file
230
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/server.h
Normal file
@@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#error "This board is not supported."
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "incoming_packet.h"
|
||||
#include "connection.h"
|
||||
#include "publisher.h"
|
||||
#include "subscriber.h"
|
||||
#include "pico_interface.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class ServerSocketInterface {
|
||||
public:
|
||||
ServerSocketInterface() {}
|
||||
virtual ~ServerSocketInterface() {}
|
||||
|
||||
ServerSocketInterface(const ServerSocketInterface &) = delete;
|
||||
const ServerSocketInterface & operator=(const ServerSocketInterface &) = delete;
|
||||
|
||||
virtual void begin() = 0;
|
||||
virtual ::Client * accept_client() = 0;
|
||||
};
|
||||
|
||||
template <typename Server>
|
||||
class ServerSocket: public ServerSocketInterface, public Server {
|
||||
public:
|
||||
using Server::Server;
|
||||
|
||||
virtual ::Client * accept_client() override {
|
||||
TRACE_FUNCTION
|
||||
auto client = Server::accept();
|
||||
if (!client) {
|
||||
// no connection
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new decltype(client)(client);
|
||||
};
|
||||
|
||||
virtual void begin() override {
|
||||
TRACE_FUNCTION
|
||||
Server::begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Server>
|
||||
class ServerSocketProxy: public ServerSocketInterface {
|
||||
public:
|
||||
Server & server;
|
||||
|
||||
ServerSocketProxy(Server & server): server(server) {}
|
||||
|
||||
virtual ::Client * accept_client() override {
|
||||
TRACE_FUNCTION
|
||||
auto client = server.accept();
|
||||
if (!client) {
|
||||
// no connection
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new decltype(client)(client);
|
||||
};
|
||||
|
||||
virtual void begin() override {
|
||||
TRACE_FUNCTION
|
||||
server.begin();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ServerSocketMux: public ServerSocketInterface {
|
||||
public:
|
||||
template <typename... Targs>
|
||||
ServerSocketMux(Targs & ... Fargs) {
|
||||
add(Fargs...);
|
||||
}
|
||||
|
||||
virtual ::Client * accept_client() override {
|
||||
TRACE_FUNCTION
|
||||
for (auto & server : servers) {
|
||||
auto client = server->accept_client();
|
||||
if (client) {
|
||||
// no connection
|
||||
return client;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
virtual void begin() override {
|
||||
TRACE_FUNCTION
|
||||
for (auto & server : servers) {
|
||||
server->begin();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename Server>
|
||||
void add(Server & server) {
|
||||
servers.push_back(std::unique_ptr<ServerSocketInterface>(new ServerSocketProxy<Server>(server)));
|
||||
}
|
||||
|
||||
template <typename Server, typename... Targs>
|
||||
void add(Server & server, Targs & ... Fargs) {
|
||||
add(server);
|
||||
add(Fargs...);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ServerSocketInterface>> servers;
|
||||
};
|
||||
|
||||
class Server: public PicoMQTTInterface, public Publisher, public SubscribedMessageListener {
|
||||
public:
|
||||
class Client: public SocketOwner<std::unique_ptr<::Client>>, public Connection, public Subscriber {
|
||||
public:
|
||||
Client(Server & server, ::Client * client);
|
||||
|
||||
void on_message(const char * topic, IncomingPacket & packet) override;
|
||||
|
||||
Print & get_print() { return Connection::client; }
|
||||
const char * get_client_id() const { return client_id.c_str(); }
|
||||
|
||||
virtual void loop() override;
|
||||
|
||||
virtual const char * get_subscription_pattern(SubscriptionId id) const override;
|
||||
virtual SubscriptionId get_subscription(const char * topic) const override;
|
||||
virtual SubscriptionId subscribe(const String & topic_filter) override;
|
||||
virtual void unsubscribe(const String & topic_filter) override;
|
||||
|
||||
protected:
|
||||
Server & server;
|
||||
String client_id;
|
||||
std::set<Subscription> subscriptions;
|
||||
|
||||
virtual void on_subscribe(IncomingPacket & packet);
|
||||
virtual void on_unsubscribe(IncomingPacket & packet);
|
||||
|
||||
virtual void handle_packet(IncomingPacket & packet) override;
|
||||
};
|
||||
|
||||
class IncomingPublish: public IncomingPacket {
|
||||
public:
|
||||
IncomingPublish(IncomingPacket & packet, Publish & publish);
|
||||
IncomingPublish(const IncomingPublish &) = delete;
|
||||
~IncomingPublish();
|
||||
|
||||
virtual int read(uint8_t * buf, size_t size) override;
|
||||
virtual int read() override;
|
||||
|
||||
protected:
|
||||
Publish & publish;
|
||||
};
|
||||
|
||||
Server(std::unique_ptr<ServerSocketInterface> socket);
|
||||
|
||||
Server(uint16_t port = 1883)
|
||||
: Server(new ServerSocket<::WiFiServer>(port)) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
template <typename ServerType>
|
||||
Server(ServerType & server)
|
||||
: Server(new ServerSocketProxy<ServerType>(server)) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
template <typename ServerType, typename... Targs>
|
||||
Server(ServerType & server, Targs & ... Fargs): Server(new ServerSocketMux(server, Fargs...)) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
void begin() override;
|
||||
void loop() override;
|
||||
|
||||
using Publisher::begin_publish;
|
||||
virtual Publish begin_publish(const char * topic, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) override;
|
||||
|
||||
unsigned long keep_alive_tolerance_millis;
|
||||
unsigned long socket_timeout_millis;
|
||||
|
||||
protected:
|
||||
Server(ServerSocketInterface * socket)
|
||||
: Server(std::unique_ptr<ServerSocketInterface>(socket)) {
|
||||
TRACE_FUNCTION
|
||||
}
|
||||
|
||||
virtual void on_message(const char * topic, IncomingPacket & packet);
|
||||
virtual ConnectReturnCode auth(const char * client_id, const char * username, const char * password) { return CRC_ACCEPTED; }
|
||||
|
||||
virtual void on_connected(const char * client_id) {}
|
||||
virtual void on_disconnected(const char * client_id) {}
|
||||
|
||||
virtual void on_subscribe(const char * client_id, const char * topic) {}
|
||||
virtual void on_unsubscribe(const char * client_id, const char * topic) {}
|
||||
|
||||
virtual PrintMux get_subscribed(const char * topic);
|
||||
|
||||
std::unique_ptr<ServerSocketInterface> server;
|
||||
std::list<std::unique_ptr<Client>> clients;
|
||||
};
|
||||
|
||||
class ServerLocalSubscribe: public Server {
|
||||
public:
|
||||
using Server::Server;
|
||||
using Server::publish;
|
||||
using Server::publish_P;
|
||||
|
||||
virtual bool publish(const char * topic, const void * payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) override;
|
||||
|
||||
virtual bool publish_P(const char * topic, PGM_P payload, const size_t payload_size,
|
||||
uint8_t qos = 0, bool retain = false, uint16_t message_id = 0) override;
|
||||
};
|
||||
|
||||
}
|
||||
161
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/subscriber.cpp
Normal file
161
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/subscriber.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "subscriber.h"
|
||||
#include "incoming_packet.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
String Subscriber::get_topic_element(const char * topic, size_t index) {
|
||||
|
||||
while (index && topic[0]) {
|
||||
if (topic++[0] == '/') {
|
||||
--index;
|
||||
}
|
||||
}
|
||||
|
||||
if (!topic[0]) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const char * end = topic;
|
||||
while (*end && *end != '/') {
|
||||
++end;
|
||||
}
|
||||
|
||||
String ret;
|
||||
ret.concat(topic, end - topic);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Subscriber::get_topic_element(const String & topic, size_t index) {
|
||||
TRACE_FUNCTION
|
||||
return get_topic_element(topic.c_str(), index);
|
||||
}
|
||||
|
||||
bool Subscriber::topic_matches(const char * p, const char * t) {
|
||||
TRACE_FUNCTION
|
||||
// TODO: Special handling of the $ prefix
|
||||
while (true) {
|
||||
switch (*p) {
|
||||
case '\0':
|
||||
// end of pattern reached
|
||||
// TODO: check for '/#' suffix
|
||||
return (*t == '\0');
|
||||
|
||||
case '#':
|
||||
// multilevel wildcard
|
||||
if (*t == '\0') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case '+':
|
||||
// single level wildcard
|
||||
while (*t && *t != '/') {
|
||||
++t;
|
||||
}
|
||||
++p;
|
||||
break;
|
||||
|
||||
default:
|
||||
// regular match
|
||||
if (*p != *t) {
|
||||
return false;
|
||||
}
|
||||
++p;
|
||||
++t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char * SubscribedMessageListener::get_subscription_pattern(SubscriptionId id) const {
|
||||
TRACE_FUNCTION
|
||||
for (const auto & kv : subscriptions) {
|
||||
if (kv.first.id == id) {
|
||||
return kv.first.c_str();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::get_subscription(const char * topic) const {
|
||||
TRACE_FUNCTION
|
||||
for (const auto & kv : subscriptions) {
|
||||
if (topic_matches(kv.first.c_str(), topic)) {
|
||||
return kv.first.id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter) {
|
||||
TRACE_FUNCTION
|
||||
return subscribe(topic_filter, [this](const char * topic, IncomingPacket & packet) { on_extra_message(topic, packet); });
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter, MessageCallback callback) {
|
||||
TRACE_FUNCTION
|
||||
unsubscribe(topic_filter);
|
||||
auto pair = subscriptions.emplace(std::make_pair(Subscription(topic_filter), callback));
|
||||
return pair.first->first.id;
|
||||
}
|
||||
|
||||
void SubscribedMessageListener::unsubscribe(const String & topic_filter) {
|
||||
TRACE_FUNCTION
|
||||
subscriptions.erase(topic_filter);
|
||||
}
|
||||
|
||||
void SubscribedMessageListener::fire_message_callbacks(const char * topic, IncomingPacket & packet) {
|
||||
TRACE_FUNCTION
|
||||
for (const auto & kv : subscriptions) {
|
||||
if (topic_matches(kv.first.c_str(), topic)) {
|
||||
kv.second((char *) topic, packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
on_extra_message(topic, packet);
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter,
|
||||
std::function<void(char *, void *, size_t)> callback, size_t max_size) {
|
||||
TRACE_FUNCTION
|
||||
return subscribe(topic_filter, [this, callback, max_size](char * topic, IncomingPacket & packet) {
|
||||
const size_t payload_size = packet.get_remaining_size();
|
||||
if (payload_size >= max_size) {
|
||||
on_message_too_big(topic, packet);
|
||||
return;
|
||||
}
|
||||
char payload[payload_size + 1];
|
||||
if (packet.read((uint8_t *) payload, payload_size) != (int) payload_size) {
|
||||
// connection error, ignore
|
||||
return;
|
||||
}
|
||||
payload[payload_size] = '\0';
|
||||
callback(topic, payload, payload_size);
|
||||
});
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter,
|
||||
std::function<void(char *, char *)> callback, size_t max_size) {
|
||||
TRACE_FUNCTION
|
||||
return subscribe(topic_filter, [callback](char * topic, void * payload, size_t payload_size) {
|
||||
callback(topic, (char *) payload);
|
||||
}, max_size);
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter,
|
||||
std::function<void(char *)> callback, size_t max_size) {
|
||||
TRACE_FUNCTION
|
||||
return subscribe(topic_filter, [callback](char * topic, void * payload, size_t payload_size) {
|
||||
callback((char *) payload);
|
||||
}, max_size);
|
||||
}
|
||||
|
||||
Subscriber::SubscriptionId SubscribedMessageListener::subscribe(const String & topic_filter,
|
||||
std::function<void(void *, size_t)> callback, size_t max_size) {
|
||||
TRACE_FUNCTION
|
||||
return subscribe(topic_filter, [callback](char * topic, void * payload, size_t payload_size) {
|
||||
callback(payload, payload_size);
|
||||
}, max_size);
|
||||
}
|
||||
|
||||
};
|
||||
73
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/subscriber.h
Normal file
73
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/subscriber.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "autoid.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
class IncomingPacket;
|
||||
|
||||
class Subscriber {
|
||||
public:
|
||||
typedef AutoId::Id SubscriptionId;
|
||||
|
||||
static bool topic_matches(const char * topic_filter, const char * topic);
|
||||
static String get_topic_element(const char * topic, size_t index);
|
||||
static String get_topic_element(const String & topic, size_t index);
|
||||
|
||||
virtual const char * get_subscription_pattern(SubscriptionId id) const = 0;
|
||||
virtual SubscriptionId get_subscription(const char * topic) const = 0;
|
||||
|
||||
virtual SubscriptionId subscribe(const String & topic_filter) = 0;
|
||||
|
||||
virtual void unsubscribe(const String & topic_filter) = 0;
|
||||
void unsubscribe(SubscriptionId id) { unsubscribe(get_subscription_pattern(id)); }
|
||||
|
||||
protected:
|
||||
class Subscription: public String, public AutoId {
|
||||
public:
|
||||
using String::String;
|
||||
Subscription(const String & str): Subscription(str.c_str()) {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class SubscribedMessageListener: public Subscriber {
|
||||
public:
|
||||
// NOTE: None of the callback functions use const arguments for wider compatibility. It's still OK (and
|
||||
// recommended) to use callbacks which take const arguments. Similarly with Strings.
|
||||
typedef std::function<void(char * topic, IncomingPacket & packet)> MessageCallback;
|
||||
|
||||
virtual const char * get_subscription_pattern(SubscriptionId id) const override;
|
||||
virtual SubscriptionId get_subscription(const char * topic) const override;
|
||||
|
||||
virtual SubscriptionId subscribe(const String & topic_filter) override;
|
||||
virtual SubscriptionId subscribe(const String & topic_filter, MessageCallback callback);
|
||||
|
||||
SubscriptionId subscribe(const String & topic_filter, std::function<void(char *, void *, size_t)> callback,
|
||||
size_t max_size = PICOMQTT_MAX_MESSAGE_SIZE);
|
||||
|
||||
SubscriptionId subscribe(const String & topic_filter, std::function<void(char *, char *)> callback,
|
||||
size_t max_size = PICOMQTT_MAX_MESSAGE_SIZE);
|
||||
SubscriptionId subscribe(const String & topic_filter, std::function<void(void *, size_t)> callback,
|
||||
size_t max_size = PICOMQTT_MAX_MESSAGE_SIZE);
|
||||
SubscriptionId subscribe(const String & topic_filter, std::function<void(char *)> callback,
|
||||
size_t max_size = PICOMQTT_MAX_MESSAGE_SIZE);
|
||||
|
||||
virtual void unsubscribe(const String & topic_filter) override;
|
||||
|
||||
virtual void on_extra_message(const char * topic, IncomingPacket & packet) {}
|
||||
virtual void on_message_too_big(const char * topic, IncomingPacket & packet) {}
|
||||
|
||||
protected:
|
||||
void fire_message_callbacks(const char * topic, IncomingPacket & packet);
|
||||
|
||||
std::map<Subscription, MessageCallback> subscriptions;
|
||||
};
|
||||
|
||||
}
|
||||
16
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/utils.h
Normal file
16
.pio/libdeps/esp32dev/PicoMQTT/src/PicoMQTT/utils.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace PicoMQTT {
|
||||
|
||||
template <typename T>
|
||||
struct SocketOwner {
|
||||
SocketOwner() {}
|
||||
template <typename ...Args>
|
||||
SocketOwner(Args && ...args): socket(std::forward<Args>(args)...) {}
|
||||
virtual ~SocketOwner() {}
|
||||
T socket;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user