`.
18 |
19 | * **`clientId`**: Client ID
20 |
21 | #### AsyncMqttClient& setCleanSession(bool `cleanSession`)
22 |
23 | Whether or not to set the CleanSession flag. Defaults to `true`.
24 |
25 | * **`cleanSession`**: clean session wanted or not
26 |
27 | #### AsyncMqttClient& setMaxTopicLength(uint16_t `maxTopicLength`)
28 |
29 | Set the maximum allowed topic length to receive. If an MQTT packet is received
30 | with a topic longer than this maximum, the packet will be ignored. Defaults to `128`.
31 |
32 | * **`maxTopicLength`**: Maximum allowed topic length to receive
33 |
34 | #### AsyncMqttClient& setCredentials(const char\* `username`, const char\* `password` = nullptr)
35 |
36 | Set the username/password. Defaults to non-auth.
37 |
38 | * **`username`**: Username
39 | * **`password`**: Password
40 |
41 | #### AsyncMqttClient& setWill(const char\* `topic`, uint8_t `qos`, bool `retain`, const char\* `payload` = nullptr, size_t `length` = 0)
42 |
43 | Set the Last Will Testament. Defaults to none.
44 |
45 | * **`topic`**: Topic of the LWT
46 | * **`qos`**: QoS of the LWT
47 | * **`retain`**: Retain flag of the LWT
48 | * **`payload`**: Payload of the LWT. If unset, the payload will be empty
49 | * **`length`**: Payload length. If unset or set to 0, the payload will be considered as a string and its size will be calculated using `strlen(payload)`
50 |
51 | #### AsyncMqttClient& setServer(IPAddress `ip`, uint16_t `port`)
52 |
53 | Set the server.
54 |
55 | * **`ip`**: IP of the server
56 | * **`port`**: Port of the server
57 |
58 | #### AsyncMqttClient& setServer(const char\* `host`, uint16_t `port`)
59 |
60 | Set the server.
61 |
62 | * **`host`**: Host of the server
63 | * **`port`**: Port of the server
64 |
65 | #### AsyncMqttClient& setSecure(bool `secure`)
66 |
67 | Whether or not to use SSL. Defaults to `false`.
68 |
69 | * **`secure`**: SSL wanted or not.
70 |
71 | #### AsyncMqttClient& addServerFingerprint(const uint8_t\* `fingerprint`)
72 |
73 | Adds an acceptable server fingerprint (SHA1). This may be called multiple times to permit any one of the specified fingerprints. By default, if no fingerprint is added, any fingerprint is accepted.
74 |
75 | * **`fingerprint`**: Fingerprint to add
76 |
77 | ### Events handlers
78 |
79 | #### AsyncMqttClient& onConnect(AsyncMqttClientInternals::OnConnectUserCallback `callback`)
80 |
81 | Add a connect event handler.
82 |
83 | * **`callback`**: Function to call
84 |
85 | #### AsyncMqttClient& onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback `callback`)
86 |
87 | Add a disconnect event handler.
88 |
89 | * **`callback`**: Function to call
90 |
91 | #### AsyncMqttClient& onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback `callback`)
92 |
93 | Add a subscribe acknowledged event handler.
94 |
95 | * **`callback`**: Function to call
96 |
97 | #### AsyncMqttClient& onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback `callback`)
98 |
99 | Add an unsubscribe acknowledged event handler.
100 |
101 | * **`callback`**: Function to call
102 |
103 | #### AsyncMqttClient& onMessage(AsyncMqttClientInternals::OnMessageUserCallback `callback`)
104 |
105 | Add a publish received event handler.
106 |
107 | * **`callback`**: Function to call
108 |
109 | #### AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback `callback`)
110 |
111 | Add a publish acknowledged event handler.
112 |
113 | * **`callback`**: Function to call
114 |
115 | ### Operation functions
116 |
117 | #### bool connected()
118 |
119 | Return if the client is currently connected to the broker or not.
120 |
121 | #### void connect()
122 |
123 | Connect to the server.
124 |
125 | #### void disconnect(bool `force` = false)
126 |
127 | Disconnect from the server.
128 |
129 | * **`force`**: Whether to force the disconnection. Defaults to `false` (clean disconnection).
130 |
131 | #### uint16_t subscribe(const char\* `topic`, uint8_t `qos`)
132 |
133 | Subscribe to the given topic at the given QoS.
134 |
135 | Return the packet ID or 0 if failed.
136 |
137 | * **`topic`**: Topic
138 | * **`qos`**: QoS
139 |
140 | #### uint16_t unsubscribe(const char\* `topic`)
141 |
142 | Unsubscribe from the given topic.
143 |
144 | Return the packet ID or 0 if failed.
145 |
146 | * **`topic`**: Topic
147 |
148 | #### uint16_t publish(const char\* `topic`, uint8_t `qos`, bool `retain`, const char\* `payload` = nullptr, size_t `length` = 0, bool dup = false, uint16_t message_id = 0)
149 |
150 | Publish a packet.
151 |
152 | Return the packet ID (or 1 if QoS 0) or 0 if failed.
153 |
154 | * **`topic`**: Topic
155 | * **`qos`**: QoS
156 | * **`retain`**: Retain flag
157 | * **`payload`**: Payload. If unset, the payload will be empty
158 | * **`length`**: Payload length. If unset or set to 0, the payload will be considered as a string and its size will be calculated using `strlen(payload)`
159 | * **`dup`**: ~~Duplicate flag. If set or set to 1, the payload will be flagged as a duplicate~~ Setting is not used anymore
160 | * **`message_id`**: ~~The message ID. If unset or set to 0, the message ID will be automtaically assigned. Use this with the DUP flag to identify which message is being duplicated~~ Setting is not used anymore
161 |
162 | #### bool clearQueue()
163 |
164 | When disconnected, clears all queued messages
165 |
166 | Returns true on succes, false on failure (client is no disconnected)
167 |
--------------------------------------------------------------------------------
/docs/3.-Memory-management.md:
--------------------------------------------------------------------------------
1 | # Memory management
2 |
3 | AsyncMqttClient buffers outgoing messages in a queue. On sending data is copied to a raw TCP buffer. Received data is passed directly to the API.
4 |
5 | ## Outgoing messages
6 |
7 | You can send data as long as memory permits. A minimum amount of free memory is set at 4096 bytes. You can lower (or raise) this value by setting `MQTT_MIN_FREE_MEMORY` to your desired value.
8 | If the free memory was sufficient to send your packet, the `publish` method will return a packet ID indicating the packet was queued. Otherwise, a `0` will be returned, and it's your responsability to resend the packet with `publish`.
9 |
10 | ## Incoming messages
11 |
12 | No incoming data is buffered by this library. Messages received by the TCP library is passed directly to the API. The max receive size is about 1460 bytes per call to your onMessage callback but the amount of data you can receive is unlimited. If you receive, say, a 300kB payload (such as an OTA payload), then your `onMessage` callback will be called about 200 times, with the according len, index and total parameters. Keep in mind the library will call your `onMessage` callbacks with the same topic buffer, so if you change the buffer on one call, the buffer will remain changed on subsequent calls.
13 |
--------------------------------------------------------------------------------
/docs/4.-Limitations-and-known-issues.md:
--------------------------------------------------------------------------------
1 | # Limitations and known issues
2 |
3 | * The library is spec compliant with one limitation. In case of power loss the following is not honored:
4 |
5 | > Must be kept in memory:
6 | * All messages in a QoS 1 or 2 flow, which are not confirmed by the broker
7 | * All received QoS 2 messages, which are not yet confirmed to the broker
8 |
9 | This means retransmission is not honored in case of a power failure. This behaviour is like explained in point 4.1.1 of the MQTT specification v3.1.1
10 |
11 | * You cannot send payload larger that what can fit on RAM.
12 |
13 | ## SSL limitations
14 |
15 | * SSL requires the build flag -DASYNC_TCP_SSL_ENABLED=1
16 | * SSL only supports fingerprints for server validation.
17 | * If you do not specify one or more acceptable server fingerprints, the SSL connection will be vulnerable to man-in-the-middle attacks.
18 | * Some server certificate signature algorithms do not work. SHA1, SHA224, SHA256, and MD5 are working. SHA384, and SHA512 will cause a crash.
19 | * TLS1.2 is not supported.
20 |
--------------------------------------------------------------------------------
/docs/5.-Troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | * The payload of incoming messages contains **raw data**. You cannot just print out the data without formatting. This is because Arduino's `print` functions expect a C-string as input and a MQTT payload is not. A simple solution is to print each character of the payload:
4 |
5 | ```cpp
6 | for (size_t i = 0; i < len; ++i) {
7 | Serial.print(payload[i]);
8 | }
9 | ```
10 |
11 | Further reading: https://en.wikipedia.org/wiki/C_string_handling
12 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | AsyncMqttClient documentation
2 | =============================
3 |
4 | See [index.md](index.md) to view it locally, or http://marvinroger.viewdocs.io/async-mqtt-client/ to view it online.
5 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | Welcome to the AsyncMqttClient for ESP8266 docs.
2 |
3 | **This documentation is only valid for the AsyncMqttClient version in this repo/directory
**
4 |
5 | -----
6 |
7 | #### 1. [Getting started](1.-Getting-started.md)
8 | #### 2. [API reference](2.-API-reference.md)
9 | #### 3. [Memory management](3.-Memory-management.md)
10 | #### 4. [Limitations and known issues](4.-Limitations-and-known-issues.md)
11 | #### 5. [Troubleshooting](5.-Troubleshooting.md)
12 |
--------------------------------------------------------------------------------
/examples/FullyFeatured-ESP32/FullyFeatured-ESP32.ino:
--------------------------------------------------------------------------------
1 | /*
2 | This example uses FreeRTOS softwaretimers as there is no built-in Ticker library
3 | */
4 |
5 |
6 | #include
7 | extern "C" {
8 | #include "freertos/FreeRTOS.h"
9 | #include "freertos/timers.h"
10 | }
11 | #include
12 |
13 | #define WIFI_SSID "yourSSID"
14 | #define WIFI_PASSWORD "yourpass"
15 |
16 | #define MQTT_HOST IPAddress(192, 168, 1, 10)
17 | #define MQTT_PORT 1883
18 |
19 | AsyncMqttClient mqttClient;
20 | TimerHandle_t mqttReconnectTimer;
21 | TimerHandle_t wifiReconnectTimer;
22 |
23 | void connectToWifi() {
24 | Serial.println("Connecting to Wi-Fi...");
25 | WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
26 | }
27 |
28 | void connectToMqtt() {
29 | Serial.println("Connecting to MQTT...");
30 | mqttClient.connect();
31 | }
32 |
33 | void WiFiEvent(WiFiEvent_t event) {
34 | Serial.printf("[WiFi-event] event: %d\n", event);
35 | switch(event) {
36 | case SYSTEM_EVENT_STA_GOT_IP:
37 | Serial.println("WiFi connected");
38 | Serial.println("IP address: ");
39 | Serial.println(WiFi.localIP());
40 | connectToMqtt();
41 | break;
42 | case SYSTEM_EVENT_STA_DISCONNECTED:
43 | Serial.println("WiFi lost connection");
44 | xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
45 | xTimerStart(wifiReconnectTimer, 0);
46 | break;
47 | }
48 | }
49 |
50 | void onMqttConnect(bool sessionPresent) {
51 | Serial.println("Connected to MQTT.");
52 | Serial.print("Session present: ");
53 | Serial.println(sessionPresent);
54 | uint16_t packetIdSub = mqttClient.subscribe("test/lol", 2);
55 | Serial.print("Subscribing at QoS 2, packetId: ");
56 | Serial.println(packetIdSub);
57 | mqttClient.publish("test/lol", 0, true, "test 1");
58 | Serial.println("Publishing at QoS 0");
59 | uint16_t packetIdPub1 = mqttClient.publish("test/lol", 1, true, "test 2");
60 | Serial.print("Publishing at QoS 1, packetId: ");
61 | Serial.println(packetIdPub1);
62 | uint16_t packetIdPub2 = mqttClient.publish("test/lol", 2, true, "test 3");
63 | Serial.print("Publishing at QoS 2, packetId: ");
64 | Serial.println(packetIdPub2);
65 | }
66 |
67 | void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
68 | Serial.println("Disconnected from MQTT.");
69 |
70 | if (WiFi.isConnected()) {
71 | xTimerStart(mqttReconnectTimer, 0);
72 | }
73 | }
74 |
75 | void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
76 | Serial.println("Subscribe acknowledged.");
77 | Serial.print(" packetId: ");
78 | Serial.println(packetId);
79 | Serial.print(" qos: ");
80 | Serial.println(qos);
81 | }
82 |
83 | void onMqttUnsubscribe(uint16_t packetId) {
84 | Serial.println("Unsubscribe acknowledged.");
85 | Serial.print(" packetId: ");
86 | Serial.println(packetId);
87 | }
88 |
89 | void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
90 | Serial.println("Publish received.");
91 | Serial.print(" topic: ");
92 | Serial.println(topic);
93 | Serial.print(" qos: ");
94 | Serial.println(properties.qos);
95 | Serial.print(" dup: ");
96 | Serial.println(properties.dup);
97 | Serial.print(" retain: ");
98 | Serial.println(properties.retain);
99 | Serial.print(" len: ");
100 | Serial.println(len);
101 | Serial.print(" index: ");
102 | Serial.println(index);
103 | Serial.print(" total: ");
104 | Serial.println(total);
105 | }
106 |
107 | void onMqttPublish(uint16_t packetId) {
108 | Serial.println("Publish acknowledged.");
109 | Serial.print(" packetId: ");
110 | Serial.println(packetId);
111 | }
112 |
113 | void setup() {
114 | Serial.begin(115200);
115 | Serial.println();
116 | Serial.println();
117 |
118 | mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast(connectToMqtt));
119 | wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast(connectToWifi));
120 |
121 | WiFi.onEvent(WiFiEvent);
122 |
123 | mqttClient.onConnect(onMqttConnect);
124 | mqttClient.onDisconnect(onMqttDisconnect);
125 | mqttClient.onSubscribe(onMqttSubscribe);
126 | mqttClient.onUnsubscribe(onMqttUnsubscribe);
127 | mqttClient.onMessage(onMqttMessage);
128 | mqttClient.onPublish(onMqttPublish);
129 | mqttClient.setServer(MQTT_HOST, MQTT_PORT);
130 |
131 | connectToWifi();
132 | }
133 |
134 | void loop() {
135 | }
136 |
--------------------------------------------------------------------------------
/examples/FullyFeatured-ESP8266/FullyFeatured-ESP8266.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #define WIFI_SSID "My_Wi-Fi"
6 | #define WIFI_PASSWORD "my-awesome-password"
7 |
8 | #define MQTT_HOST IPAddress(192, 168, 1, 10)
9 | #define MQTT_PORT 1883
10 |
11 | AsyncMqttClient mqttClient;
12 | Ticker mqttReconnectTimer;
13 |
14 | WiFiEventHandler wifiConnectHandler;
15 | WiFiEventHandler wifiDisconnectHandler;
16 | Ticker wifiReconnectTimer;
17 |
18 | void connectToWifi() {
19 | Serial.println("Connecting to Wi-Fi...");
20 | WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
21 | }
22 |
23 | void connectToMqtt() {
24 | Serial.println("Connecting to MQTT...");
25 | mqttClient.connect();
26 | }
27 |
28 | void onWifiConnect(const WiFiEventStationModeGotIP& event) {
29 | Serial.println("Connected to Wi-Fi.");
30 | connectToMqtt();
31 | }
32 |
33 | void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
34 | Serial.println("Disconnected from Wi-Fi.");
35 | mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
36 | wifiReconnectTimer.once(2, connectToWifi);
37 | }
38 |
39 | void onMqttConnect(bool sessionPresent) {
40 | Serial.println("Connected to MQTT.");
41 | Serial.print("Session present: ");
42 | Serial.println(sessionPresent);
43 | uint16_t packetIdSub = mqttClient.subscribe("test/lol", 2);
44 | Serial.print("Subscribing at QoS 2, packetId: ");
45 | Serial.println(packetIdSub);
46 | mqttClient.publish("test/lol", 0, true, "test 1");
47 | Serial.println("Publishing at QoS 0");
48 | uint16_t packetIdPub1 = mqttClient.publish("test/lol", 1, true, "test 2");
49 | Serial.print("Publishing at QoS 1, packetId: ");
50 | Serial.println(packetIdPub1);
51 | uint16_t packetIdPub2 = mqttClient.publish("test/lol", 2, true, "test 3");
52 | Serial.print("Publishing at QoS 2, packetId: ");
53 | Serial.println(packetIdPub2);
54 | }
55 |
56 | void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
57 | Serial.println("Disconnected from MQTT.");
58 |
59 | if (WiFi.isConnected()) {
60 | mqttReconnectTimer.once(2, connectToMqtt);
61 | }
62 | }
63 |
64 | void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
65 | Serial.println("Subscribe acknowledged.");
66 | Serial.print(" packetId: ");
67 | Serial.println(packetId);
68 | Serial.print(" qos: ");
69 | Serial.println(qos);
70 | }
71 |
72 | void onMqttUnsubscribe(uint16_t packetId) {
73 | Serial.println("Unsubscribe acknowledged.");
74 | Serial.print(" packetId: ");
75 | Serial.println(packetId);
76 | }
77 |
78 | void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
79 | Serial.println("Publish received.");
80 | Serial.print(" topic: ");
81 | Serial.println(topic);
82 | Serial.print(" qos: ");
83 | Serial.println(properties.qos);
84 | Serial.print(" dup: ");
85 | Serial.println(properties.dup);
86 | Serial.print(" retain: ");
87 | Serial.println(properties.retain);
88 | Serial.print(" len: ");
89 | Serial.println(len);
90 | Serial.print(" index: ");
91 | Serial.println(index);
92 | Serial.print(" total: ");
93 | Serial.println(total);
94 | }
95 |
96 | void onMqttPublish(uint16_t packetId) {
97 | Serial.println("Publish acknowledged.");
98 | Serial.print(" packetId: ");
99 | Serial.println(packetId);
100 | }
101 |
102 | void setup() {
103 | Serial.begin(115200);
104 | Serial.println();
105 | Serial.println();
106 |
107 | wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
108 | wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
109 |
110 | mqttClient.onConnect(onMqttConnect);
111 | mqttClient.onDisconnect(onMqttDisconnect);
112 | mqttClient.onSubscribe(onMqttSubscribe);
113 | mqttClient.onUnsubscribe(onMqttUnsubscribe);
114 | mqttClient.onMessage(onMqttMessage);
115 | mqttClient.onPublish(onMqttPublish);
116 | mqttClient.setServer(MQTT_HOST, MQTT_PORT);
117 |
118 | connectToWifi();
119 | }
120 |
121 | void loop() {
122 | }
123 |
--------------------------------------------------------------------------------
/examples/FullyFeaturedSSL/platformio.ini:
--------------------------------------------------------------------------------
1 | #
2 | # Example PlatformIO configuration file for SSL and non-SSL builds.
3 | #
4 | # Before you will be able to build the SSL version of this project, you will
5 | # need to explicitly install the espressif8266_stage platform.
6 | #
7 | # To perform this installation, refer to step 1 of:
8 | # http://docs.platformio.org/en/latest/platforms/espressif8266.html#using-arduino-framework-with-staging-version
9 |
10 | [platformio]
11 | env_default = ssl
12 |
13 | [env:ssl]
14 | platform = espressif8266_stage
15 | framework = arduino
16 | board = esp01_1m
17 | build_flags = -DASYNC_TCP_SSL_ENABLED=1
18 | lib_deps = AsyncMqttClient
19 |
20 | [env:nossl]
21 | platform = espressif8266
22 | framework = arduino
23 | board = esp01_1m
24 | lib_deps = AsyncMqttClient
25 |
--------------------------------------------------------------------------------
/examples/FullyFeaturedSSL/src/main.cpp:
--------------------------------------------------------------------------------
1 |
2 | // Example project which can be built with SSL enabled or disabled.
3 | // The espressif8266_stage platform must be installed.
4 | // Refer to platformio.ini for the build configuration and platform installation.
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #define WIFI_SSID "My_Wi-Fi"
12 | #define WIFI_PASSWORD "my-awesome-password"
13 |
14 | #define MQTT_HOST IPAddress(192, 168, 1, 10)
15 |
16 | #if ASYNC_TCP_SSL_ENABLED
17 | #define MQTT_SECURE true
18 | #define MQTT_SERVER_FINGERPRINT {0x7e, 0x36, 0x22, 0x01, 0xf9, 0x7e, 0x99, 0x2f, 0xc5, 0xdb, 0x3d, 0xbe, 0xac, 0x48, 0x67, 0x5b, 0x5d, 0x47, 0x94, 0xd2}
19 | #define MQTT_PORT 8883
20 | #else
21 | #define MQTT_PORT 1883
22 | #endif
23 |
24 | AsyncMqttClient mqttClient;
25 | Ticker mqttReconnectTimer;
26 |
27 | WiFiEventHandler wifiConnectHandler;
28 | WiFiEventHandler wifiDisconnectHandler;
29 | Ticker wifiReconnectTimer;
30 |
31 | void connectToWifi() {
32 | Serial.println("Connecting to Wi-Fi...");
33 | WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
34 | }
35 |
36 | void connectToMqtt() {
37 | Serial.println("Connecting to MQTT...");
38 | mqttClient.connect();
39 | }
40 |
41 | void onWifiConnect(const WiFiEventStationModeGotIP& event) {
42 | Serial.println("Connected to Wi-Fi.");
43 | connectToMqtt();
44 | }
45 |
46 | void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
47 | Serial.println("Disconnected from Wi-Fi.");
48 | mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
49 | wifiReconnectTimer.once(2, connectToWifi);
50 | }
51 |
52 | void onMqttConnect(bool sessionPresent) {
53 | Serial.println("Connected to MQTT.");
54 | Serial.print("Session present: ");
55 | Serial.println(sessionPresent);
56 | uint16_t packetIdSub = mqttClient.subscribe("test/lol", 2);
57 | Serial.print("Subscribing at QoS 2, packetId: ");
58 | Serial.println(packetIdSub);
59 | mqttClient.publish("test/lol", 0, true, "test 1");
60 | Serial.println("Publishing at QoS 0");
61 | uint16_t packetIdPub1 = mqttClient.publish("test/lol", 1, true, "test 2");
62 | Serial.print("Publishing at QoS 1, packetId: ");
63 | Serial.println(packetIdPub1);
64 | uint16_t packetIdPub2 = mqttClient.publish("test/lol", 2, true, "test 3");
65 | Serial.print("Publishing at QoS 2, packetId: ");
66 | Serial.println(packetIdPub2);
67 | }
68 |
69 | void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
70 | Serial.println("Disconnected from MQTT.");
71 |
72 | if (reason == AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT) {
73 | Serial.println("Bad server fingerprint.");
74 | }
75 |
76 | if (WiFi.isConnected()) {
77 | mqttReconnectTimer.once(2, connectToMqtt);
78 | }
79 | }
80 |
81 | void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
82 | Serial.println("Subscribe acknowledged.");
83 | Serial.print(" packetId: ");
84 | Serial.println(packetId);
85 | Serial.print(" qos: ");
86 | Serial.println(qos);
87 | }
88 |
89 | void onMqttUnsubscribe(uint16_t packetId) {
90 | Serial.println("Unsubscribe acknowledged.");
91 | Serial.print(" packetId: ");
92 | Serial.println(packetId);
93 | }
94 |
95 | void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
96 | Serial.println("Publish received.");
97 | Serial.print(" topic: ");
98 | Serial.println(topic);
99 | Serial.print(" qos: ");
100 | Serial.println(properties.qos);
101 | Serial.print(" dup: ");
102 | Serial.println(properties.dup);
103 | Serial.print(" retain: ");
104 | Serial.println(properties.retain);
105 | Serial.print(" len: ");
106 | Serial.println(len);
107 | Serial.print(" index: ");
108 | Serial.println(index);
109 | Serial.print(" total: ");
110 | Serial.println(total);
111 | }
112 |
113 | void onMqttPublish(uint16_t packetId) {
114 | Serial.println("Publish acknowledged.");
115 | Serial.print(" packetId: ");
116 | Serial.println(packetId);
117 | }
118 |
119 | void setup() {
120 | Serial.begin(115200);
121 | Serial.println();
122 | Serial.println();
123 |
124 | wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
125 | wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
126 |
127 | mqttClient.onConnect(onMqttConnect);
128 | mqttClient.onDisconnect(onMqttDisconnect);
129 | mqttClient.onSubscribe(onMqttSubscribe);
130 | mqttClient.onUnsubscribe(onMqttUnsubscribe);
131 | mqttClient.onMessage(onMqttMessage);
132 | mqttClient.onPublish(onMqttPublish);
133 | mqttClient.setServer(MQTT_HOST, MQTT_PORT);
134 | #if ASYNC_TCP_SSL_ENABLED
135 | mqttClient.setSecure(MQTT_SECURE);
136 | if (MQTT_SECURE) {
137 | mqttClient.addServerFingerprint((const uint8_t[])MQTT_SERVER_FINGERPRINT);
138 | }
139 | #endif
140 |
141 | connectToWifi();
142 | }
143 |
144 | void loop() {
145 | }
146 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Datatypes (KEYWORD1)
3 | #######################################
4 |
5 | AsyncMqttClient KEYWORD1
6 | AsyncMqttClientDisconnectReason KEYWORD1
7 | AsyncMqttClientMessageProperties KEYWORD1
8 |
9 | #######################################
10 | # Methods and Functions (KEYWORD2)
11 | #######################################
12 |
13 | setKeepAlive KEYWORD2
14 | setClientId KEYWORD2
15 | setCleanSession KEYWORD2
16 | setMaxTopicLength KEYWORD2
17 | setCredentials KEYWORD2
18 | setWill KEYWORD2
19 | setServer KEYWORD2
20 | setSecure KEYWORD2
21 | addServerFingerprint KEYWORD2
22 |
23 | onConnect KEYWORD2
24 | onDisconnect KEYWORD2
25 | onSubscribe KEYWORD2
26 | onUnsubscribe KEYWORD2
27 | onMessage KEYWORD2
28 | onPublish KEYWORD2
29 |
30 | connected KEYWORD2
31 | connect KEYWORD2
32 | disconnect KEYWORD2
33 | subscribe KEYWORD2
34 | unsubscribe KEYWORD2
35 | publish KEYWORD2
36 | clearQueue KEYWORD2
37 |
38 | #######################################
39 | # Constants (LITERAL1)
40 | #######################################
41 |
42 | TCP_DISCONNECTED LITERAL1
43 |
44 | MQTT_UNACCEPTABLE_PROTOCOL_VERSION LITERAL1
45 | MQTT_IDENTIFIER_REJECTED LITERAL1
46 | MQTT_SERVER_UNAVAILABLE LITERAL1
47 | MQTT_MALFORMED_CREDENTIALS LITERAL1
48 | MQTT_NOT_AUTHORIZED LITERAL1
49 |
--------------------------------------------------------------------------------
/library.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "AsyncMqttClient",
3 | "keywords": "iot, home, automation, async, mqtt, client, esp8266",
4 | "description": "An Arduino for ESP8266 / ESP32 asynchronous MQTT client implementation",
5 | "authors":
6 | {
7 | "name": "Marvin ROGER",
8 | "url": "https://www.marvinroger.fr"
9 | },
10 | "repository":
11 | {
12 | "type": "git",
13 | "url": "https://github.com/marvinroger/async-mqtt-client.git"
14 | },
15 | "version": "0.9.0",
16 | "frameworks": "arduino",
17 | "platforms": ["espressif8266", "espressif32"],
18 | "dependencies": [
19 | {
20 | "name": "ESPAsyncTCP",
21 | "version": ">=1.2.2",
22 | "platforms": "espressif8266"
23 | },
24 | {
25 | "name": "AsyncTCP",
26 | "version": ">=1.1.1",
27 | "platforms": "espressif32"
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=AsyncMqttClient
2 | version=0.9.0
3 | author=Marvin ROGER
4 | maintainer=Marvin ROGER
5 | sentence=An Arduino for ESP8266 and ESP32 asynchronous MQTT client implementation
6 | paragraph=Like this project? Please star it on GitHub!
7 | category=Communication
8 | url=https://github.com/marvinroger/async-mqtt-client
9 | architectures=esp8266,esp32
10 |
--------------------------------------------------------------------------------
/scripts/CI/build_examples_pio.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #pip install -U platformio
4 | #platformio update
5 | platformio lib -g install AsyncTCP
6 | platformio lib -g install ESPAsyncTCP
7 |
8 | RED='\033[0;31m'
9 | GREEN='\033[0;32m'
10 | YELLOW='\033[0;33m'
11 | NC='\033[0m'
12 |
13 | lines=$(find ./examples/ -maxdepth 1 -mindepth 1 -type d)
14 | retval=0
15 | while read line; do
16 | if [[ "$line" != *ESP8266 && "$line" != *ESP32 ]]
17 | then
18 | echo -e "========================== BUILDING $line =========================="
19 | echo -e "${YELLOW}SKIPPING${NC}"
20 | continue
21 | fi
22 | echo -e "========================== BUILDING $line =========================="
23 | if [[ -e "$line/platformio.ini" ]]
24 | then
25 | # skipping
26 | #output=$(platformio ci --lib="." --project-conf="$line/platformio.ini" $line 2>&1)
27 | :
28 | else
29 | if [[ "$line" == *ESP8266 ]]
30 | then
31 | output=$(platformio ci --lib="." --project-conf="scripts/CI/platformio_esp8266.ini" $line 2>&1)
32 | else
33 | output=$(platformio ci --lib="." --project-conf="scripts/CI/platformio_esp32.ini" $line 2>&1)
34 | fi
35 | fi
36 | if [ $? -ne 0 ]; then
37 | echo "$output"
38 | echo -e "Building $line ${RED}FAILED${NC}"
39 | retval=1
40 | else
41 | echo -e "${GREEN}SUCCESS${NC}"
42 | fi
43 | done <<< "$lines"
44 |
45 | # cleanup
46 | platformio lib -g uninstall AsyncTCP
47 | platformio lib -g uninstall ESPAsyncTCP
48 |
49 | exit "$retval"
50 |
--------------------------------------------------------------------------------
/scripts/CI/platformio_esp32.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env:esp32]
12 | platform = espressif32
13 | board = esp32dev
14 | framework = arduino
15 | build_flags =
16 | -Wall
--------------------------------------------------------------------------------
/scripts/CI/platformio_esp8266.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env:esp8266]
12 | platform = espressif8266
13 | board = esp01_1m
14 | framework = arduino
15 | build_flags =
16 | -Wall
17 |
--------------------------------------------------------------------------------
/scripts/get-fingerprint/get-fingerprint.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | import ssl
5 | import hashlib
6 |
7 | parser = argparse.ArgumentParser(description='Compute SSL/TLS fingerprints.')
8 | parser.add_argument('--host', required=True)
9 | parser.add_argument('--port', default=8883)
10 |
11 | args = parser.parse_args()
12 | print(args.host)
13 |
14 | cert_pem = ssl.get_server_certificate((args.host, args.port))
15 | cert_der = ssl.PEM_cert_to_DER_cert(cert_pem)
16 |
17 | md5 = hashlib.md5(cert_der).hexdigest()
18 | sha1 = hashlib.sha1(cert_der).hexdigest()
19 | sha256 = hashlib.sha256(cert_der).hexdigest()
20 | print("MD5: " + md5)
21 | print("SHA1: " + sha1)
22 | print("SHA256: " + sha256)
23 |
24 | print("\nSHA1 as array initializer:")
25 | print("const uint8_t fingerprint[] = {0x" + ", 0x".join([sha1[i:i+2] for i in range(0, len(sha1), 2)]) + "};")
26 |
27 | print("\nSHA1 as function call:")
28 | print("mqttClient.addServerFingerprint((const uint8_t[]){0x" + ", 0x".join([sha1[i:i+2] for i in range(0, len(sha1), 2)]) + "});")
29 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient.cpp:
--------------------------------------------------------------------------------
1 | #include "AsyncMqttClient.hpp"
2 |
3 | AsyncMqttClient::AsyncMqttClient()
4 | : _client()
5 | , _head(nullptr)
6 | , _tail(nullptr)
7 | , _sent(0)
8 | , _state(DISCONNECTED)
9 | , _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED)
10 | , _lastClientActivity(0)
11 | , _lastServerActivity(0)
12 | , _lastPingRequestTime(0)
13 | , _generatedClientId{0}
14 | , _ip()
15 | , _host(nullptr)
16 | , _useIp(false)
17 | #if ASYNC_TCP_SSL_ENABLED
18 | , _secure(false)
19 | #endif
20 | , _port(0)
21 | , _keepAlive(15)
22 | , _cleanSession(true)
23 | , _clientId(nullptr)
24 | , _username(nullptr)
25 | , _password(nullptr)
26 | , _willTopic(nullptr)
27 | , _willPayload(nullptr)
28 | , _willPayloadLength(0)
29 | , _willQos(0)
30 | , _willRetain(false)
31 | #if ASYNC_TCP_SSL_ENABLED
32 | , _secureServerFingerprints()
33 | #endif
34 | , _onConnectUserCallbacks()
35 | , _onDisconnectUserCallbacks()
36 | , _onSubscribeUserCallbacks()
37 | , _onUnsubscribeUserCallbacks()
38 | , _onMessageUserCallbacks()
39 | , _onPublishUserCallbacks()
40 | , _parsingInformation { .bufferState = AsyncMqttClientInternals::BufferState::NONE }
41 | , _currentParsedPacket(nullptr)
42 | , _remainingLengthBufferPosition(0)
43 | , _remainingLengthBuffer{0}
44 | , _pendingPubRels() {
45 | _client.onConnect([](void* obj, AsyncClient* c) { (static_cast(obj))->_onConnect(); }, this);
46 | _client.onDisconnect([](void* obj, AsyncClient* c) { (static_cast(obj))->_onDisconnect(); }, this);
47 | // _client.onError([](void* obj, AsyncClient* c, int8_t error) { (static_cast(obj))->_onError(error); }, this);
48 | // _client.onTimeout([](void* obj, AsyncClient* c, uint32_t time) { (static_cast(obj))->_onTimeout(); }, this);
49 | _client.onAck([](void* obj, AsyncClient* c, size_t len, uint32_t time) { (static_cast(obj))->_onAck(len); }, this);
50 | _client.onData([](void* obj, AsyncClient* c, void* data, size_t len) { (static_cast(obj))->_onData(static_cast(data), len); }, this);
51 | _client.onPoll([](void* obj, AsyncClient* c) { (static_cast(obj))->_onPoll(); }, this);
52 | _client.setNoDelay(true); // send small packets immediately (PINGREQ/DISCONN are only 2 bytes)
53 | #ifdef ESP32
54 | sprintf(_generatedClientId, "esp32-%06llx", ESP.getEfuseMac());
55 | _xSemaphore = xSemaphoreCreateMutex();
56 | #elif defined(ESP8266)
57 | sprintf(_generatedClientId, "esp8266-%06x", ESP.getChipId());
58 | #endif
59 | _clientId = _generatedClientId;
60 |
61 | setMaxTopicLength(128);
62 | }
63 |
64 | AsyncMqttClient::~AsyncMqttClient() {
65 | delete _currentParsedPacket;
66 | delete[] _parsingInformation.topicBuffer;
67 | _clear();
68 | _pendingPubRels.clear();
69 | _pendingPubRels.shrink_to_fit();
70 | _clearQueue(false); // _clear() doesn't clear session data
71 | #ifdef ESP32
72 | vSemaphoreDelete(_xSemaphore);
73 | #endif
74 | }
75 |
76 | AsyncMqttClient& AsyncMqttClient::setKeepAlive(uint16_t keepAlive) {
77 | _keepAlive = keepAlive;
78 | return *this;
79 | }
80 |
81 | AsyncMqttClient& AsyncMqttClient::setClientId(const char* clientId) {
82 | _clientId = clientId;
83 | return *this;
84 | }
85 |
86 | AsyncMqttClient& AsyncMqttClient::setCleanSession(bool cleanSession) {
87 | _cleanSession = cleanSession;
88 | return *this;
89 | }
90 |
91 | AsyncMqttClient& AsyncMqttClient::setMaxTopicLength(uint16_t maxTopicLength) {
92 | _parsingInformation.maxTopicLength = maxTopicLength;
93 | delete[] _parsingInformation.topicBuffer;
94 | _parsingInformation.topicBuffer = new char[maxTopicLength + 1];
95 | return *this;
96 | }
97 |
98 | AsyncMqttClient& AsyncMqttClient::setCredentials(const char* username, const char* password) {
99 | _username = username;
100 | _password = password;
101 | return *this;
102 | }
103 |
104 | AsyncMqttClient& AsyncMqttClient::setWill(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length) {
105 | _willTopic = topic;
106 | _willQos = qos;
107 | _willRetain = retain;
108 | _willPayload = payload;
109 | _willPayloadLength = length;
110 | return *this;
111 | }
112 |
113 | AsyncMqttClient& AsyncMqttClient::setServer(IPAddress ip, uint16_t port) {
114 | _useIp = true;
115 | _ip = ip;
116 | _port = port;
117 | return *this;
118 | }
119 |
120 | AsyncMqttClient& AsyncMqttClient::setServer(const char* host, uint16_t port) {
121 | _useIp = false;
122 | _host = host;
123 | _port = port;
124 | return *this;
125 | }
126 |
127 | #if ASYNC_TCP_SSL_ENABLED
128 | AsyncMqttClient& AsyncMqttClient::setSecure(bool secure) {
129 | _secure = secure;
130 | return *this;
131 | }
132 |
133 | AsyncMqttClient& AsyncMqttClient::addServerFingerprint(const uint8_t* fingerprint) {
134 | std::array newFingerprint;
135 | memcpy(newFingerprint.data(), fingerprint, SHA1_SIZE);
136 | _secureServerFingerprints.push_back(newFingerprint);
137 | return *this;
138 | }
139 | #endif
140 |
141 | AsyncMqttClient& AsyncMqttClient::onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback) {
142 | _onConnectUserCallbacks.push_back(callback);
143 | return *this;
144 | }
145 |
146 | AsyncMqttClient& AsyncMqttClient::onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback) {
147 | _onDisconnectUserCallbacks.push_back(callback);
148 | return *this;
149 | }
150 |
151 | AsyncMqttClient& AsyncMqttClient::onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback) {
152 | _onSubscribeUserCallbacks.push_back(callback);
153 | return *this;
154 | }
155 |
156 | AsyncMqttClient& AsyncMqttClient::onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback) {
157 | _onUnsubscribeUserCallbacks.push_back(callback);
158 | return *this;
159 | }
160 |
161 | AsyncMqttClient& AsyncMqttClient::onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback) {
162 | _onMessageUserCallbacks.push_back(callback);
163 | return *this;
164 | }
165 |
166 | AsyncMqttClient& AsyncMqttClient::onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback) {
167 | _onPublishUserCallbacks.push_back(callback);
168 | return *this;
169 | }
170 |
171 | void AsyncMqttClient::_freeCurrentParsedPacket() {
172 | delete _currentParsedPacket;
173 | _currentParsedPacket = nullptr;
174 | }
175 |
176 | void AsyncMqttClient::_clear() {
177 | _lastPingRequestTime = 0;
178 | _freeCurrentParsedPacket();
179 | _clearQueue(true); // keep session data for now
180 |
181 | _parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE;
182 |
183 | _client.setRxTimeout(0);
184 | }
185 |
186 | /* TCP */
187 | void AsyncMqttClient::_onConnect() {
188 | log_i("TCP conn, MQTT CONNECT");
189 | #if ASYNC_TCP_SSL_ENABLED
190 | if (_secure && _secureServerFingerprints.size() > 0) {
191 | SSL* clientSsl = _client.getSSL();
192 |
193 | bool sslFoundFingerprint = false;
194 | for (std::array fingerprint : _secureServerFingerprints) {
195 | if (ssl_match_fingerprint(clientSsl, fingerprint.data()) == SSL_OK) {
196 | sslFoundFingerprint = true;
197 | break;
198 | }
199 | }
200 |
201 | if (!sslFoundFingerprint) {
202 | _disconnectReason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT;
203 | _client.close(true);
204 | return;
205 | }
206 | }
207 | #endif
208 | AsyncMqttClientInternals::OutPacket* msg =
209 | new AsyncMqttClientInternals::ConnectOutPacket(_cleanSession,
210 | _username,
211 | _password,
212 | _willTopic,
213 | _willRetain,
214 | _willQos,
215 | _willPayload,
216 | _willPayloadLength,
217 | _keepAlive,
218 | _clientId);
219 | _addFront(msg);
220 | _handleQueue();
221 | }
222 |
223 | void AsyncMqttClient::_onDisconnect() {
224 | log_i("TCP disconn");
225 | _state = DISCONNECTED;
226 |
227 | _clear();
228 |
229 | for (auto callback : _onDisconnectUserCallbacks) callback(_disconnectReason);
230 | }
231 |
232 | /*
233 | void AsyncMqttClient::_onError(int8_t error) {
234 | (void)error;
235 | // _onDisconnect called anyway
236 | }
237 |
238 | void AsyncMqttClient::_onTimeout() {
239 | // disconnection will be handled by ping/pong management
240 | }
241 | */
242 |
243 | void AsyncMqttClient::_onAck(size_t len) {
244 | log_i("ack %u", len);
245 | _handleQueue();
246 | }
247 |
248 | void AsyncMqttClient::_onData(char* data, size_t len) {
249 | log_i("data rcv (%u)", len);
250 | size_t currentBytePosition = 0;
251 | char currentByte;
252 | _lastServerActivity = millis();
253 | do {
254 | switch (_parsingInformation.bufferState) {
255 | case AsyncMqttClientInternals::BufferState::NONE:
256 | currentByte = data[currentBytePosition++];
257 | _parsingInformation.packetType = currentByte >> 4;
258 | _parsingInformation.packetFlags = (currentByte << 4) >> 4;
259 | _parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::REMAINING_LENGTH;
260 | switch (_parsingInformation.packetType) {
261 | case AsyncMqttClientInternals::PacketType.CONNACK:
262 | log_i("rcv CONNACK");
263 | _currentParsedPacket = new AsyncMqttClientInternals::ConnAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onConnAck, this, std::placeholders::_1, std::placeholders::_2));
264 | _client.setRxTimeout(0);
265 | break;
266 | case AsyncMqttClientInternals::PacketType.PINGRESP:
267 | log_i("rcv PINGRESP");
268 | _currentParsedPacket = new AsyncMqttClientInternals::PingRespPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPingResp, this));
269 | break;
270 | case AsyncMqttClientInternals::PacketType.SUBACK:
271 | log_i("rcv SUBACK");
272 | _currentParsedPacket = new AsyncMqttClientInternals::SubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onSubAck, this, std::placeholders::_1, std::placeholders::_2));
273 | break;
274 | case AsyncMqttClientInternals::PacketType.UNSUBACK:
275 | log_i("rcv UNSUBACK");
276 | _currentParsedPacket = new AsyncMqttClientInternals::UnsubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onUnsubAck, this, std::placeholders::_1));
277 | break;
278 | case AsyncMqttClientInternals::PacketType.PUBLISH:
279 | log_i("rcv PUBLISH");
280 | _currentParsedPacket = new AsyncMqttClientInternals::PublishPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), std::bind(&AsyncMqttClient::_onPublish, this, std::placeholders::_1, std::placeholders::_2));
281 | break;
282 | case AsyncMqttClientInternals::PacketType.PUBREL:
283 | log_i("rcv PUBREL");
284 | _currentParsedPacket = new AsyncMqttClientInternals::PubRelPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubRel, this, std::placeholders::_1));
285 | break;
286 | case AsyncMqttClientInternals::PacketType.PUBACK:
287 | log_i("rcv PUBACK");
288 | _currentParsedPacket = new AsyncMqttClientInternals::PubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubAck, this, std::placeholders::_1));
289 | break;
290 | case AsyncMqttClientInternals::PacketType.PUBREC:
291 | log_i("rcv PUBREC");
292 | _currentParsedPacket = new AsyncMqttClientInternals::PubRecPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubRec, this, std::placeholders::_1));
293 | break;
294 | case AsyncMqttClientInternals::PacketType.PUBCOMP:
295 | log_i("rcv PUBCOMP");
296 | _currentParsedPacket = new AsyncMqttClientInternals::PubCompPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubComp, this, std::placeholders::_1));
297 | break;
298 | default:
299 | log_i("rcv PROTOCOL VIOLATION");
300 | disconnect(true);
301 | break;
302 | }
303 | break;
304 | case AsyncMqttClientInternals::BufferState::REMAINING_LENGTH:
305 | currentByte = data[currentBytePosition++];
306 | _remainingLengthBuffer[_remainingLengthBufferPosition++] = currentByte;
307 | if (currentByte >> 7 == 0) {
308 | _parsingInformation.remainingLength = AsyncMqttClientInternals::Helpers::decodeRemainingLength(_remainingLengthBuffer);
309 | _remainingLengthBufferPosition = 0;
310 | if (_parsingInformation.remainingLength > 0) {
311 | _parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::VARIABLE_HEADER;
312 | } else {
313 | // PINGRESP is a special case where it has no variable header, so the packet ends right here
314 | _parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE;
315 | _onPingResp();
316 | }
317 | }
318 | break;
319 | case AsyncMqttClientInternals::BufferState::VARIABLE_HEADER:
320 | _currentParsedPacket->parseVariableHeader(data, len, ¤tBytePosition);
321 | break;
322 | case AsyncMqttClientInternals::BufferState::PAYLOAD:
323 | _currentParsedPacket->parsePayload(data, len, ¤tBytePosition);
324 | break;
325 | default:
326 | currentBytePosition = len;
327 | }
328 | } while (currentBytePosition != len);
329 | }
330 |
331 | void AsyncMqttClient::_onPoll() {
332 | // if there is too much time the client has sent a ping request without a response, disconnect client to avoid half open connections
333 | if (_lastPingRequestTime != 0 && (millis() - _lastPingRequestTime) >= (_keepAlive * 1000 * 2)) {
334 | log_w("PING t/o, disconnecting");
335 | disconnect(true);
336 | return;
337 | }
338 | // send ping to ensure the server will receive at least one message inside keepalive window
339 | if (_state == CONNECTED && _lastPingRequestTime == 0 && (millis() - _lastClientActivity) >= (_keepAlive * 1000 * 0.7)) {
340 | _sendPing();
341 | // send ping to verify if the server is still there (ensure this is not a half connection)
342 | } else if (_state == CONNECTED && _lastPingRequestTime == 0 && (millis() - _lastServerActivity) >= (_keepAlive * 1000 * 0.7)) {
343 | _sendPing();
344 | }
345 | _handleQueue();
346 | }
347 |
348 | /* QUEUE */
349 |
350 | void AsyncMqttClient::_insert(AsyncMqttClientInternals::OutPacket* packet) {
351 | // We only use this for QoS2 PUBREL so there must be a PUBLISH packet present.
352 | // The queue therefore cannot be empty and _head points to this PUBLISH packet.
353 | SEMAPHORE_TAKE();
354 | log_i("new insert #%u", packet->packetType());
355 | packet->next = _head->next;
356 | _head->next = packet;
357 | if (_head == _tail) { // PUB packet is the only one in the queue
358 | _tail = packet;
359 | }
360 | SEMAPHORE_GIVE();
361 | _handleQueue();
362 | }
363 |
364 | void AsyncMqttClient::_addFront(AsyncMqttClientInternals::OutPacket* packet) {
365 | // This is only used for the CONNECT packet, to be able to establish a connection
366 | // before anything else. The queue can be empty or has packets from the continued session.
367 | // In both cases, _head should always point to the CONNECT packet afterwards.
368 | SEMAPHORE_TAKE();
369 | log_i("new front #%u", packet->packetType());
370 | if (_head == nullptr) {
371 | _tail = packet;
372 | } else {
373 | packet->next = _head;
374 | }
375 | _head = packet;
376 | SEMAPHORE_GIVE();
377 | _handleQueue();
378 | }
379 |
380 | void AsyncMqttClient::_addBack(AsyncMqttClientInternals::OutPacket* packet) {
381 | SEMAPHORE_TAKE();
382 | log_i("new back #%u", packet->packetType());
383 | if (!_tail) {
384 | _head = packet;
385 | } else {
386 | _tail->next = packet;
387 | }
388 | _tail = packet;
389 | _tail->next = nullptr;
390 | SEMAPHORE_GIVE();
391 | _handleQueue();
392 | }
393 |
394 | void AsyncMqttClient::_handleQueue() {
395 | SEMAPHORE_TAKE();
396 | // On ESP32, onDisconnect is called within the close()-call. So we need to make sure we don't lock
397 | bool disconnect = false;
398 |
399 | while (_head && _client.space() > 10) { // safe but arbitrary value, send at least 10 bytes
400 | // 1. try to send
401 | if (_head->size() > _sent) {
402 | // On SSL the TCP library returns the total amount of bytes, not just the unencrypted payload length.
403 | // So we calculate the amount to be written ourselves.
404 | size_t willSend = std::min(_head->size() - _sent, _client.space());
405 | size_t realSent = _client.add(reinterpret_cast(_head->data(_sent)), willSend, ASYNC_WRITE_FLAG_COPY); // flag is set by LWIP anyway, added for clarity
406 | _sent += willSend;
407 | (void)realSent;
408 | _client.send();
409 | _lastClientActivity = millis();
410 | _lastPingRequestTime = 0;
411 | #if ASYNC_TCP_SSL_ENABLED
412 | log_i("snd #%u: (tls: %u) %u/%u", _head->packetType(), realSent, _sent, _head->size());
413 | #else
414 | log_i("snd #%u: %u/%u", _head->packetType(), _sent, _head->size());
415 | #endif
416 | if (_head->packetType() == AsyncMqttClientInternals::PacketType.DISCONNECT) {
417 | disconnect = true;
418 | }
419 | }
420 |
421 | // 2. stop processing when we have to wait for an MQTT acknowledgment
422 | if (_head->size() == _sent) {
423 | if (_head->released()) {
424 | log_i("p #%d rel", _head->packetType());
425 | AsyncMqttClientInternals::OutPacket* tmp = _head;
426 | _head = _head->next;
427 | if (!_head) _tail = nullptr;
428 | delete tmp;
429 | _sent = 0;
430 | } else {
431 | break; // sending is complete however send next only after mqtt confirmation
432 | }
433 | }
434 | }
435 |
436 | SEMAPHORE_GIVE();
437 | if (disconnect) {
438 | log_i("snd DISCONN, disconnecting");
439 | _client.close();
440 | }
441 | }
442 |
443 | void AsyncMqttClient::_clearQueue(bool keepSessionData) {
444 | SEMAPHORE_TAKE();
445 | AsyncMqttClientInternals::OutPacket* packet = _head;
446 | _head = nullptr;
447 | _tail = nullptr;
448 |
449 | while (packet) {
450 | /* MQTT spec 3.1.2.4 Clean Session:
451 | * - QoS 1 and QoS 2 messages which have been sent to the Server, but have not been completely acknowledged.
452 | * - QoS 2 messages which have been received from the Server, but have not been completely acknowledged.
453 | * + (unsent PUB messages with QoS > 0)
454 | *
455 | * To be kept:
456 | * - possibly first message (sent to server but not acked)
457 | * - PUBREC messages (QoS 2 PUB received but not acked)
458 | * - PUBCOMP messages (QoS 2 PUBREL received but not acked)
459 | */
460 | if (keepSessionData) {
461 | if (packet->qos() > 0 && packet->size() <= _sent) { // check for qos includes check for PUB-packet type
462 | reinterpret_cast(packet)->setDup();
463 | AsyncMqttClientInternals::OutPacket* next = packet->next;
464 | log_i("keep #%u", packet->packetType());
465 | SEMAPHORE_GIVE();
466 | _addBack(packet);
467 | SEMAPHORE_TAKE();
468 | packet = next;
469 | } else if (packet->qos() > 0 ||
470 | packet->packetType() == AsyncMqttClientInternals::PacketType.PUBREC ||
471 | packet->packetType() == AsyncMqttClientInternals::PacketType.PUBCOMP) {
472 | AsyncMqttClientInternals::OutPacket* next = packet->next;
473 | log_i("keep #%u", packet->packetType());
474 | SEMAPHORE_GIVE();
475 | _addBack(packet);
476 | SEMAPHORE_TAKE();
477 | packet = next;
478 | } else {
479 | AsyncMqttClientInternals::OutPacket* next = packet->next;
480 | delete packet;
481 | packet = next;
482 | }
483 | /* Delete everything when not keeping session data
484 | */
485 | } else {
486 | AsyncMqttClientInternals::OutPacket* next = packet->next;
487 | delete packet;
488 | packet = next;
489 | }
490 | }
491 | _sent = 0;
492 | SEMAPHORE_GIVE();
493 | }
494 |
495 | /* MQTT */
496 | void AsyncMqttClient::_onPingResp() {
497 | log_i("PINGRESP");
498 | _freeCurrentParsedPacket();
499 | _lastPingRequestTime = 0;
500 | }
501 |
502 | void AsyncMqttClient::_onConnAck(bool sessionPresent, uint8_t connectReturnCode) {
503 | log_i("CONNACK");
504 | _freeCurrentParsedPacket();
505 |
506 | if (!sessionPresent) {
507 | _pendingPubRels.clear();
508 | _pendingPubRels.shrink_to_fit();
509 | _clearQueue(false); // remove session data
510 | }
511 |
512 | if (connectReturnCode == 0) {
513 | _state = CONNECTED;
514 | for (auto callback : _onConnectUserCallbacks) callback(sessionPresent);
515 | } else {
516 | // Callbacks are handled by the onDisconnect function which is called from the AsyncTcp lib
517 | _disconnectReason = static_cast(connectReturnCode);
518 | return;
519 | }
520 | _handleQueue(); // send any remaining data from continued session
521 | }
522 |
523 | void AsyncMqttClient::_onSubAck(uint16_t packetId, char status) {
524 | log_i("SUBACK");
525 | _freeCurrentParsedPacket();
526 | SEMAPHORE_TAKE();
527 | if (_head && _head->packetId() == packetId) {
528 | _head->release();
529 | log_i("SUB released");
530 | }
531 | SEMAPHORE_GIVE();
532 |
533 | for (auto callback : _onSubscribeUserCallbacks) callback(packetId, status);
534 |
535 | _handleQueue(); // subscribe confirmed, ready to send next queued item
536 | }
537 |
538 | void AsyncMqttClient::_onUnsubAck(uint16_t packetId) {
539 | log_i("UNSUBACK");
540 | _freeCurrentParsedPacket();
541 | SEMAPHORE_TAKE();
542 | if (_head && _head->packetId() == packetId) {
543 | _head->release();
544 | log_i("UNSUB released");
545 | }
546 | SEMAPHORE_GIVE();
547 |
548 | for (auto callback : _onUnsubscribeUserCallbacks) callback(packetId);
549 |
550 | _handleQueue(); // unsubscribe confirmed, ready to send next queued item
551 | }
552 |
553 | void AsyncMqttClient::_onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId) {
554 | bool notifyPublish = true;
555 |
556 | if (qos == 2) {
557 | for (AsyncMqttClientInternals::PendingPubRel pendingPubRel : _pendingPubRels) {
558 | if (pendingPubRel.packetId == packetId) {
559 | notifyPublish = false;
560 | break;
561 | }
562 | }
563 | }
564 |
565 | if (notifyPublish) {
566 | AsyncMqttClientMessageProperties properties;
567 | properties.qos = qos;
568 | properties.dup = dup;
569 | properties.retain = retain;
570 |
571 | for (auto callback : _onMessageUserCallbacks) callback(topic, payload, properties, len, index, total);
572 | }
573 | }
574 |
575 | void AsyncMqttClient::_onPublish(uint16_t packetId, uint8_t qos) {
576 | AsyncMqttClientInternals::PendingAck pendingAck;
577 |
578 | if (qos == 1) {
579 | pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBACK;
580 | pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBACK_RESERVED;
581 | pendingAck.packetId = packetId;
582 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PubAckOutPacket(pendingAck);
583 | _addBack(msg);
584 | } else if (qos == 2) {
585 | pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBREC;
586 | pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBREC_RESERVED;
587 | pendingAck.packetId = packetId;
588 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PubAckOutPacket(pendingAck);
589 | _addBack(msg);
590 |
591 | bool pubRelAwaiting = false;
592 | for (AsyncMqttClientInternals::PendingPubRel pendingPubRel : _pendingPubRels) {
593 | if (pendingPubRel.packetId == packetId) {
594 | pubRelAwaiting = true;
595 | break;
596 | }
597 | }
598 |
599 | if (!pubRelAwaiting) {
600 | AsyncMqttClientInternals::PendingPubRel pendingPubRel;
601 | pendingPubRel.packetId = packetId;
602 | _pendingPubRels.push_back(pendingPubRel);
603 | }
604 | }
605 |
606 | _freeCurrentParsedPacket();
607 | }
608 |
609 | void AsyncMqttClient::_onPubRel(uint16_t packetId) {
610 | _freeCurrentParsedPacket();
611 |
612 | AsyncMqttClientInternals::PendingAck pendingAck;
613 | pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBCOMP;
614 | pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBCOMP_RESERVED;
615 | pendingAck.packetId = packetId;
616 | if (_head && _head->packetId() == packetId) {
617 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PubAckOutPacket(pendingAck);
618 | _head->release();
619 | _insert(msg);
620 | log_i("PUBREC released");
621 | }
622 |
623 | for (size_t i = 0; i < _pendingPubRels.size(); i++) {
624 | if (_pendingPubRels[i].packetId == packetId) {
625 | _pendingPubRels.erase(_pendingPubRels.begin() + i);
626 | _pendingPubRels.shrink_to_fit();
627 | }
628 | }
629 | }
630 |
631 | void AsyncMqttClient::_onPubAck(uint16_t packetId) {
632 | _freeCurrentParsedPacket();
633 | if (_head && _head->packetId() == packetId) {
634 | _head->release();
635 | log_i("PUB released");
636 | }
637 |
638 | for (auto callback : _onPublishUserCallbacks) callback(packetId);
639 | }
640 |
641 | void AsyncMqttClient::_onPubRec(uint16_t packetId) {
642 | _freeCurrentParsedPacket();
643 |
644 | // We will only be sending 1 QoS>0 PUB message at a time (to honor message
645 | // ordering). So no need to store ACKS in a separate container as it will
646 | // be stored in the outgoing queue until a PUBCOMP comes in.
647 | AsyncMqttClientInternals::PendingAck pendingAck;
648 | pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBREL;
649 | pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBREL_RESERVED;
650 | pendingAck.packetId = packetId;
651 | log_i("snd PUBREL");
652 |
653 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PubAckOutPacket(pendingAck);
654 | if (_head && _head->packetId() == packetId) {
655 | _head->release();
656 | log_i("PUB released");
657 | }
658 | _insert(msg);
659 | }
660 |
661 | void AsyncMqttClient::_onPubComp(uint16_t packetId) {
662 | _freeCurrentParsedPacket();
663 |
664 | // _head points to the PUBREL package
665 | if (_head && _head->packetId() == packetId) {
666 | _head->release();
667 | log_i("PUBREL released");
668 | }
669 |
670 | for (auto callback : _onPublishUserCallbacks) callback(packetId);
671 | }
672 |
673 | void AsyncMqttClient::_sendPing() {
674 | log_i("PING");
675 | _lastPingRequestTime = millis();
676 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PingReqOutPacket;
677 | _addBack(msg);
678 | }
679 |
680 | bool AsyncMqttClient::connected() const {
681 | return _state == CONNECTED;
682 | }
683 |
684 | void AsyncMqttClient::connect() {
685 | if (_state != DISCONNECTED) return;
686 | log_i("CONNECTING");
687 | _state = CONNECTING;
688 | _disconnectReason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED; // reset any previous
689 |
690 | _client.setRxTimeout(_keepAlive);
691 |
692 | #if ASYNC_TCP_SSL_ENABLED
693 | if (_useIp) {
694 | _client.connect(_ip, _port, _secure);
695 | } else {
696 | _client.connect(_host, _port, _secure);
697 | }
698 | #else
699 | if (_useIp) {
700 | _client.connect(_ip, _port);
701 | } else {
702 | _client.connect(_host, _port);
703 | }
704 | #endif
705 | }
706 |
707 | void AsyncMqttClient::disconnect(bool force) {
708 | if (_state == DISCONNECTED) return;
709 | log_i("DISCONNECT (f:%d)", force);
710 | if (force) {
711 | _state = DISCONNECTED;
712 | _client.close(true);
713 | } else if (_state != DISCONNECTING) {
714 | _state = DISCONNECTING;
715 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::DisconnOutPacket;
716 | _addBack(msg);
717 | }
718 | }
719 |
720 | uint16_t AsyncMqttClient::subscribe(const char* topic, uint8_t qos) {
721 | if (_state != CONNECTED) return 0;
722 | log_i("SUBSCRIBE");
723 |
724 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::SubscribeOutPacket(topic, qos);
725 | _addBack(msg);
726 | return msg->packetId();
727 | }
728 |
729 | uint16_t AsyncMqttClient::unsubscribe(const char* topic) {
730 | if (_state != CONNECTED) return 0;
731 | log_i("UNSUBSCRIBE");
732 |
733 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::UnsubscribeOutPacket(topic);
734 | _addBack(msg);
735 | return msg->packetId();
736 | }
737 |
738 | uint16_t AsyncMqttClient::publish(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length, bool dup, uint16_t message_id) {
739 | if (_state != CONNECTED || GET_FREE_MEMORY() < MQTT_MIN_FREE_MEMORY) return 0;
740 | log_i("PUBLISH");
741 |
742 | AsyncMqttClientInternals::OutPacket* msg = new AsyncMqttClientInternals::PublishOutPacket(topic, qos, retain, payload, length);
743 | _addBack(msg);
744 | return msg->packetId();
745 | }
746 |
747 | bool AsyncMqttClient::clearQueue() {
748 | if (_state != DISCONNECTED) return false;
749 | _clearQueue(false);
750 | return true;
751 | }
752 |
753 | const char* AsyncMqttClient::getClientId() const {
754 | return _clientId;
755 | }
756 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_ASYNCMQTTCLIENT_H_
2 | #define SRC_ASYNCMQTTCLIENT_H_
3 |
4 | #include "AsyncMqttClient.hpp"
5 |
6 | #endif // SRC_ASYNCMQTTCLIENT_H_
7 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "Arduino.h"
7 |
8 | #ifndef MQTT_MIN_FREE_MEMORY
9 | #define MQTT_MIN_FREE_MEMORY 4096
10 | #endif
11 |
12 | #ifdef ESP32
13 | #include
14 | #include
15 | #elif defined(ESP8266)
16 | #include
17 | #else
18 | #error Platform not supported
19 | #endif
20 |
21 | #if ASYNC_TCP_SSL_ENABLED
22 | #include
23 | #define SHA1_SIZE 20
24 | #endif
25 |
26 | #include "AsyncMqttClient/Flags.hpp"
27 | #include "AsyncMqttClient/ParsingInformation.hpp"
28 | #include "AsyncMqttClient/MessageProperties.hpp"
29 | #include "AsyncMqttClient/Helpers.hpp"
30 | #include "AsyncMqttClient/Callbacks.hpp"
31 | #include "AsyncMqttClient/DisconnectReasons.hpp"
32 | #include "AsyncMqttClient/Storage.hpp"
33 |
34 | #include "AsyncMqttClient/Packets/Packet.hpp"
35 | #include "AsyncMqttClient/Packets/ConnAckPacket.hpp"
36 | #include "AsyncMqttClient/Packets/PingRespPacket.hpp"
37 | #include "AsyncMqttClient/Packets/SubAckPacket.hpp"
38 | #include "AsyncMqttClient/Packets/UnsubAckPacket.hpp"
39 | #include "AsyncMqttClient/Packets/PublishPacket.hpp"
40 | #include "AsyncMqttClient/Packets/PubRelPacket.hpp"
41 | #include "AsyncMqttClient/Packets/PubAckPacket.hpp"
42 | #include "AsyncMqttClient/Packets/PubRecPacket.hpp"
43 | #include "AsyncMqttClient/Packets/PubCompPacket.hpp"
44 |
45 | #include "AsyncMqttClient/Packets/Out/Connect.hpp"
46 | #include "AsyncMqttClient/Packets/Out/PingReq.hpp"
47 | #include "AsyncMqttClient/Packets/Out/PubAck.hpp"
48 | #include "AsyncMqttClient/Packets/Out/Disconn.hpp"
49 | #include "AsyncMqttClient/Packets/Out/Subscribe.hpp"
50 | #include "AsyncMqttClient/Packets/Out/Unsubscribe.hpp"
51 | #include "AsyncMqttClient/Packets/Out/Publish.hpp"
52 |
53 | class AsyncMqttClient {
54 | public:
55 | AsyncMqttClient();
56 | ~AsyncMqttClient();
57 |
58 | AsyncMqttClient& setKeepAlive(uint16_t keepAlive);
59 | AsyncMqttClient& setClientId(const char* clientId);
60 | AsyncMqttClient& setCleanSession(bool cleanSession);
61 | AsyncMqttClient& setMaxTopicLength(uint16_t maxTopicLength);
62 | AsyncMqttClient& setCredentials(const char* username, const char* password = nullptr);
63 | AsyncMqttClient& setWill(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0);
64 | AsyncMqttClient& setServer(IPAddress ip, uint16_t port);
65 | AsyncMqttClient& setServer(const char* host, uint16_t port);
66 | #if ASYNC_TCP_SSL_ENABLED
67 | AsyncMqttClient& setSecure(bool secure);
68 | AsyncMqttClient& addServerFingerprint(const uint8_t* fingerprint);
69 | #endif
70 |
71 | AsyncMqttClient& onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback);
72 | AsyncMqttClient& onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback);
73 | AsyncMqttClient& onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback);
74 | AsyncMqttClient& onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback);
75 | AsyncMqttClient& onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback);
76 | AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback);
77 |
78 | bool connected() const;
79 | void connect();
80 | void disconnect(bool force = false);
81 | uint16_t subscribe(const char* topic, uint8_t qos);
82 | uint16_t unsubscribe(const char* topic);
83 | uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0);
84 | bool clearQueue(); // Not MQTT compliant!
85 |
86 | const char* getClientId() const;
87 |
88 | private:
89 | AsyncClient _client;
90 | AsyncMqttClientInternals::OutPacket* _head;
91 | AsyncMqttClientInternals::OutPacket* _tail;
92 | size_t _sent;
93 | enum {
94 | CONNECTING,
95 | CONNECTED,
96 | DISCONNECTING,
97 | DISCONNECTED
98 | } _state;
99 | AsyncMqttClientDisconnectReason _disconnectReason;
100 | uint32_t _lastClientActivity;
101 | uint32_t _lastServerActivity;
102 | uint32_t _lastPingRequestTime;
103 |
104 | char _generatedClientId[18 + 1]; // esp8266-abc123 and esp32-abcdef123456
105 | IPAddress _ip;
106 | const char* _host;
107 | bool _useIp;
108 | #if ASYNC_TCP_SSL_ENABLED
109 | bool _secure;
110 | #endif
111 | uint16_t _port;
112 | uint16_t _keepAlive;
113 | bool _cleanSession;
114 | const char* _clientId;
115 | const char* _username;
116 | const char* _password;
117 | const char* _willTopic;
118 | const char* _willPayload;
119 | uint16_t _willPayloadLength;
120 | uint8_t _willQos;
121 | bool _willRetain;
122 |
123 | #if ASYNC_TCP_SSL_ENABLED
124 | std::vector> _secureServerFingerprints;
125 | #endif
126 |
127 | std::vector _onConnectUserCallbacks;
128 | std::vector _onDisconnectUserCallbacks;
129 | std::vector _onSubscribeUserCallbacks;
130 | std::vector _onUnsubscribeUserCallbacks;
131 | std::vector _onMessageUserCallbacks;
132 | std::vector _onPublishUserCallbacks;
133 |
134 | AsyncMqttClientInternals::ParsingInformation _parsingInformation;
135 | AsyncMqttClientInternals::Packet* _currentParsedPacket;
136 | uint8_t _remainingLengthBufferPosition;
137 | char _remainingLengthBuffer[4];
138 |
139 | std::vector _pendingPubRels;
140 |
141 | #if defined(ESP32)
142 | SemaphoreHandle_t _xSemaphore = nullptr;
143 | #elif defined(ESP8266)
144 | bool _xSemaphore = false;
145 | #endif
146 |
147 | void _clear();
148 | void _freeCurrentParsedPacket();
149 |
150 | // TCP
151 | void _onConnect();
152 | void _onDisconnect();
153 | // void _onError(int8_t error);
154 | // void _onTimeout();
155 | void _onAck(size_t len);
156 | void _onData(char* data, size_t len);
157 | void _onPoll();
158 |
159 | // QUEUE
160 | void _insert(AsyncMqttClientInternals::OutPacket* packet); // for PUBREL
161 | void _addFront(AsyncMqttClientInternals::OutPacket* packet); // for CONNECT
162 | void _addBack(AsyncMqttClientInternals::OutPacket* packet); // all the rest
163 | void _handleQueue();
164 | void _clearQueue(bool keepSessionData);
165 |
166 | // MQTT
167 | void _onPingResp();
168 | void _onConnAck(bool sessionPresent, uint8_t connectReturnCode);
169 | void _onSubAck(uint16_t packetId, char status);
170 | void _onUnsubAck(uint16_t packetId);
171 | void _onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId);
172 | void _onPublish(uint16_t packetId, uint8_t qos);
173 | void _onPubRel(uint16_t packetId);
174 | void _onPubAck(uint16_t packetId);
175 | void _onPubRec(uint16_t packetId);
176 | void _onPubComp(uint16_t packetId);
177 |
178 | void _sendPing();
179 | };
180 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Callbacks.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "DisconnectReasons.hpp"
6 | #include "MessageProperties.hpp"
7 | #include "Errors.hpp"
8 |
9 | namespace AsyncMqttClientInternals {
10 | // user callbacks
11 | typedef std::function OnConnectUserCallback;
12 | typedef std::function OnDisconnectUserCallback;
13 | typedef std::function OnSubscribeUserCallback;
14 | typedef std::function OnUnsubscribeUserCallback;
15 | typedef std::function OnMessageUserCallback;
16 | typedef std::function OnPublishUserCallback;
17 | typedef std::function OnErrorUserCallback;
18 |
19 | // internal callbacks
20 | typedef std::function OnConnAckInternalCallback;
21 | typedef std::function OnPingRespInternalCallback;
22 | typedef std::function OnSubAckInternalCallback;
23 | typedef std::function OnUnsubAckInternalCallback;
24 | typedef std::function OnMessageInternalCallback;
25 | typedef std::function OnPublishInternalCallback;
26 | typedef std::function OnPubRelInternalCallback;
27 | typedef std::function OnPubAckInternalCallback;
28 | typedef std::function OnPubRecInternalCallback;
29 | typedef std::function OnPubCompInternalCallback;
30 | } // namespace AsyncMqttClientInternals
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/DisconnectReasons.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | enum class AsyncMqttClientDisconnectReason : uint8_t {
4 | TCP_DISCONNECTED = 0,
5 |
6 | MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
7 | MQTT_IDENTIFIER_REJECTED = 2,
8 | MQTT_SERVER_UNAVAILABLE = 3,
9 | MQTT_MALFORMED_CREDENTIALS = 4,
10 | MQTT_NOT_AUTHORIZED = 5,
11 |
12 | ESP8266_NOT_ENOUGH_SPACE = 6,
13 |
14 | TLS_BAD_FINGERPRINT = 7
15 | };
16 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Errors.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | enum class AsyncMqttClientError : uint8_t {
4 | MAX_RETRIES = 0,
5 | OUT_OF_MEMORY = 1
6 | };
7 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Flags.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AsyncMqttClientInternals {
4 | constexpr struct {
5 | const uint8_t RESERVED = 0;
6 | const uint8_t CONNECT = 1;
7 | const uint8_t CONNACK = 2;
8 | const uint8_t PUBLISH = 3;
9 | const uint8_t PUBACK = 4;
10 | const uint8_t PUBREC = 5;
11 | const uint8_t PUBREL = 6;
12 | const uint8_t PUBCOMP = 7;
13 | const uint8_t SUBSCRIBE = 8;
14 | const uint8_t SUBACK = 9;
15 | const uint8_t UNSUBSCRIBE = 10;
16 | const uint8_t UNSUBACK = 11;
17 | const uint8_t PINGREQ = 12;
18 | const uint8_t PINGRESP = 13;
19 | const uint8_t DISCONNECT = 14;
20 | const uint8_t RESERVED2 = 1;
21 | } PacketType;
22 |
23 | constexpr struct {
24 | const uint8_t CONNECT_RESERVED = 0x00;
25 | const uint8_t CONNACK_RESERVED = 0x00;
26 | const uint8_t PUBLISH_DUP = 0x08;
27 | const uint8_t PUBLISH_QOS0 = 0x00;
28 | const uint8_t PUBLISH_QOS1 = 0x02;
29 | const uint8_t PUBLISH_QOS2 = 0x04;
30 | const uint8_t PUBLISH_QOSRESERVED = 0x06;
31 | const uint8_t PUBLISH_RETAIN = 0x01;
32 | const uint8_t PUBACK_RESERVED = 0x00;
33 | const uint8_t PUBREC_RESERVED = 0x00;
34 | const uint8_t PUBREL_RESERVED = 0x02;
35 | const uint8_t PUBCOMP_RESERVED = 0x00;
36 | const uint8_t SUBSCRIBE_RESERVED = 0x02;
37 | const uint8_t SUBACK_RESERVED = 0x00;
38 | const uint8_t UNSUBSCRIBE_RESERVED = 0x02;
39 | const uint8_t UNSUBACK_RESERVED = 0x00;
40 | const uint8_t PINGREQ_RESERVED = 0x00;
41 | const uint8_t PINGRESP_RESERVED = 0x00;
42 | const uint8_t DISCONNECT_RESERVED = 0x00;
43 | const uint8_t RESERVED2_RESERVED = 0x00;
44 | } HeaderFlag;
45 |
46 | constexpr struct {
47 | const uint8_t USERNAME = 0x80;
48 | const uint8_t PASSWORD = 0x40;
49 | const uint8_t WILL_RETAIN = 0x20;
50 | const uint8_t WILL_QOS0 = 0x00;
51 | const uint8_t WILL_QOS1 = 0x08;
52 | const uint8_t WILL_QOS2 = 0x10;
53 | const uint8_t WILL = 0x04;
54 | const uint8_t CLEAN_SESSION = 0x02;
55 | const uint8_t RESERVED = 0x00;
56 | } ConnectFlag;
57 | } // namespace AsyncMqttClientInternals
58 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Helpers.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AsyncMqttClientInternals {
4 | class Helpers {
5 | public:
6 | static uint32_t decodeRemainingLength(char* bytes) {
7 | uint32_t multiplier = 1;
8 | uint32_t value = 0;
9 | uint8_t currentByte = 0;
10 | uint8_t encodedByte;
11 | do {
12 | encodedByte = bytes[currentByte++];
13 | value += (encodedByte & 127) * multiplier;
14 | multiplier *= 128;
15 | } while ((encodedByte & 128) != 0);
16 |
17 | return value;
18 | }
19 |
20 | static uint8_t encodeRemainingLength(uint32_t remainingLength, char* destination) {
21 | uint8_t currentByte = 0;
22 | uint8_t bytesNeeded = 0;
23 |
24 | do {
25 | uint8_t encodedByte = remainingLength % 128;
26 | remainingLength /= 128;
27 | if (remainingLength > 0) {
28 | encodedByte = encodedByte | 128;
29 | }
30 |
31 | destination[currentByte++] = encodedByte;
32 | bytesNeeded++;
33 | } while (remainingLength > 0);
34 |
35 | return bytesNeeded;
36 | }
37 | };
38 |
39 | #if defined(ARDUINO_ARCH_ESP32)
40 | #define SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY)
41 | #define SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
42 | #define GET_FREE_MEMORY() ESP.getMaxAllocHeap()
43 | #include
44 | #elif defined(ARDUINO_ARCH_ESP8266)
45 | #define SEMAPHORE_TAKE(X) while (_xSemaphore) { /*ESP.wdtFeed();*/ } _xSemaphore = true
46 | #define SEMAPHORE_GIVE() _xSemaphore = false
47 | #define GET_FREE_MEMORY() ESP.getMaxFreeBlockSize()
48 | #if defined(DEBUG_ESP_PORT) && defined(DEBUG_ASYNC_MQTT_CLIENT)
49 | #define log_i(...) DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.print("\n")
50 | #define log_e(...) DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.print("\n")
51 | #define log_w(...) DEBUG_ESP_PORT.printf(__VA_ARGS__); DEBUG_ESP_PORT.print("\n")
52 | #else
53 | #define log_i(...)
54 | #define log_e(...)
55 | #define log_w(...)
56 | #endif
57 | #else
58 | #pragma error "No valid architecture"
59 | #endif
60 |
61 | } // namespace AsyncMqttClientInternals
62 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/MessageProperties.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct AsyncMqttClientMessageProperties {
4 | uint8_t qos;
5 | bool dup;
6 | bool retain;
7 | };
8 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/ConnAckPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "ConnAckPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::ConnAckPacket;
4 |
5 | ConnAckPacket::ConnAckPacket(ParsingInformation* parsingInformation, OnConnAckInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _sessionPresent(false)
10 | , _connectReturnCode(0) {
11 | }
12 |
13 | ConnAckPacket::~ConnAckPacket() {
14 | }
15 |
16 | void ConnAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _sessionPresent = (currentByte << 7) >> 7;
20 | } else {
21 | _connectReturnCode = currentByte;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_sessionPresent, _connectReturnCode);
24 | }
25 | }
26 |
27 | void ConnAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/ConnAckPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class ConnAckPacket : public Packet {
10 | public:
11 | explicit ConnAckPacket(ParsingInformation* parsingInformation, OnConnAckInternalCallback callback);
12 | ~ConnAckPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnConnAckInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | bool _sessionPresent;
23 | uint8_t _connectReturnCode;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Connect.cpp:
--------------------------------------------------------------------------------
1 | #include "Connect.hpp"
2 |
3 | using AsyncMqttClientInternals::ConnectOutPacket;
4 |
5 | ConnectOutPacket::ConnectOutPacket(bool cleanSession,
6 | const char* username,
7 | const char* password,
8 | const char* willTopic,
9 | bool willRetain,
10 | uint8_t willQos,
11 | const char* willPayload,
12 | uint16_t willPayloadLength,
13 | uint16_t keepAlive,
14 | const char* clientId) {
15 | char fixedHeader[5];
16 | fixedHeader[0] = AsyncMqttClientInternals::PacketType.CONNECT;
17 | fixedHeader[0] = fixedHeader[0] << 4;
18 | fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.CONNECT_RESERVED;
19 |
20 | uint16_t protocolNameLength = 4;
21 | char protocolNameLengthBytes[2];
22 | protocolNameLengthBytes[0] = protocolNameLength >> 8;
23 | protocolNameLengthBytes[1] = protocolNameLength & 0xFF;
24 |
25 | char protocolLevel[1];
26 | protocolLevel[0] = 0x04;
27 |
28 | char connectFlags[1];
29 | connectFlags[0] = 0;
30 | if (cleanSession) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.CLEAN_SESSION;
31 | if (username != nullptr) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.USERNAME;
32 | if (password != nullptr) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.PASSWORD;
33 | if (willTopic != nullptr) {
34 | connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL;
35 | if (willRetain) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_RETAIN;
36 | switch (willQos) {
37 | case 0:
38 | connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS0;
39 | break;
40 | case 1:
41 | connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS1;
42 | break;
43 | case 2:
44 | connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS2;
45 | break;
46 | }
47 | }
48 |
49 | char keepAliveBytes[2];
50 | keepAliveBytes[0] = keepAlive >> 8;
51 | keepAliveBytes[1] = keepAlive & 0xFF;
52 |
53 | uint16_t clientIdLength = strlen(clientId);
54 | char clientIdLengthBytes[2];
55 | clientIdLengthBytes[0] = clientIdLength >> 8;
56 | clientIdLengthBytes[1] = clientIdLength & 0xFF;
57 |
58 | // Optional fields
59 | uint16_t willTopicLength = 0;
60 | char willTopicLengthBytes[2];
61 | char willPayloadLengthBytes[2];
62 | if (willTopic != nullptr) {
63 | willTopicLength = strlen(willTopic);
64 | willTopicLengthBytes[0] = willTopicLength >> 8;
65 | willTopicLengthBytes[1] = willTopicLength & 0xFF;
66 |
67 | if (willPayload != nullptr && willPayloadLength == 0) willPayloadLength = strlen(willPayload);
68 |
69 | willPayloadLengthBytes[0] = willPayloadLength >> 8;
70 | willPayloadLengthBytes[1] = willPayloadLength & 0xFF;
71 | }
72 |
73 | uint16_t usernameLength = 0;
74 | char usernameLengthBytes[2];
75 | if (username != nullptr) {
76 | usernameLength = strlen(username);
77 | usernameLengthBytes[0] = usernameLength >> 8;
78 | usernameLengthBytes[1] = usernameLength & 0xFF;
79 | }
80 |
81 | uint16_t passwordLength = 0;
82 | char passwordLengthBytes[2];
83 | if (password != nullptr) {
84 | passwordLength = strlen(password);
85 | passwordLengthBytes[0] = passwordLength >> 8;
86 | passwordLengthBytes[1] = passwordLength & 0xFF;
87 | }
88 |
89 | uint32_t remainingLength = 2 + protocolNameLength + 1 + 1 + 2 + 2 + clientIdLength; // always present
90 | if (willTopic != nullptr) remainingLength += 2 + willTopicLength + 2 + willPayloadLength;
91 | if (username != nullptr) remainingLength += 2 + usernameLength;
92 | if (password != nullptr) remainingLength += 2 + passwordLength;
93 | uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(remainingLength, fixedHeader + 1);
94 |
95 | uint32_t neededSpace = 1 + remainingLengthLength;
96 | neededSpace += 2;
97 | neededSpace += protocolNameLength;
98 | neededSpace += 1;
99 | neededSpace += 1;
100 | neededSpace += 2;
101 | neededSpace += 2;
102 | neededSpace += clientIdLength;
103 | if (willTopic != nullptr) {
104 | neededSpace += 2;
105 | neededSpace += willTopicLength;
106 |
107 | neededSpace += 2;
108 | if (willPayload != nullptr) neededSpace += willPayloadLength;
109 | }
110 | if (username != nullptr) {
111 | neededSpace += 2;
112 | neededSpace += usernameLength;
113 | }
114 | if (password != nullptr) {
115 | neededSpace += 2;
116 | neededSpace += passwordLength;
117 | }
118 |
119 | _data.reserve(neededSpace);
120 |
121 | _data.insert(_data.end(), fixedHeader, fixedHeader + 1 + remainingLengthLength);
122 |
123 | _data.push_back(protocolNameLengthBytes[0]);
124 | _data.push_back(protocolNameLengthBytes[1]);
125 |
126 | _data.push_back('M');
127 | _data.push_back('Q');
128 | _data.push_back('T');
129 | _data.push_back('T');
130 |
131 | _data.push_back(protocolLevel[0]);
132 | _data.push_back(connectFlags[0]);
133 | _data.push_back(keepAliveBytes[0]);
134 | _data.push_back(keepAliveBytes[1]);
135 | _data.push_back(clientIdLengthBytes[0]);
136 | _data.push_back(clientIdLengthBytes[1]);
137 |
138 | _data.insert(_data.end(), clientId, clientId + clientIdLength);
139 | if (willTopic != nullptr) {
140 | _data.insert(_data.end(), willTopicLengthBytes, willTopicLengthBytes + 2);
141 | _data.insert(_data.end(), willTopic, willTopic + willTopicLength);
142 |
143 | _data.insert(_data.end(), willPayloadLengthBytes, willPayloadLengthBytes + 2);
144 | if (willPayload != nullptr) _data.insert(_data.end(), willPayload, willPayload + willPayloadLength);
145 | }
146 | if (username != nullptr) {
147 | _data.insert(_data.end(), usernameLengthBytes, usernameLengthBytes + 2);
148 | _data.insert(_data.end(), username, username + usernameLength);
149 | }
150 | if (password != nullptr) {
151 | _data.insert(_data.end(), passwordLengthBytes, passwordLengthBytes + 2);
152 | _data.insert(_data.end(), password, password + passwordLength);
153 | }
154 | }
155 |
156 | const uint8_t* ConnectOutPacket::data(size_t index) const {
157 | return &_data.data()[index];
158 | }
159 |
160 | size_t ConnectOutPacket::size() const {
161 | return _data.size();
162 | }
163 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Connect.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include // strlen
5 |
6 | #include "OutPacket.hpp"
7 | #include "../../Flags.hpp"
8 | #include "../../Helpers.hpp"
9 |
10 | namespace AsyncMqttClientInternals {
11 | class ConnectOutPacket : public OutPacket {
12 | public:
13 | ConnectOutPacket(bool cleanSession,
14 | const char* username,
15 | const char* password,
16 | const char* willTopic,
17 | bool willRetain,
18 | uint8_t willQos,
19 | const char* willPayload,
20 | uint16_t willPayloadLength,
21 | uint16_t keepAlive,
22 | const char* clientId);
23 | const uint8_t* data(size_t index = 0) const;
24 | size_t size() const;
25 |
26 | private:
27 | std::vector _data;
28 | };
29 | } // namespace AsyncMqttClientInternals
30 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Disconn.cpp:
--------------------------------------------------------------------------------
1 | #include "Disconn.hpp"
2 |
3 | using AsyncMqttClientInternals::DisconnOutPacket;
4 |
5 | DisconnOutPacket::DisconnOutPacket() {
6 | _data[0] = AsyncMqttClientInternals::PacketType.DISCONNECT;
7 | _data[0] = _data[0] << 4;
8 | _data[0] = _data[0] | AsyncMqttClientInternals::HeaderFlag.DISCONNECT_RESERVED;
9 | _data[1] = 0;
10 | }
11 |
12 | const uint8_t* DisconnOutPacket::data(size_t index) const {
13 | return &_data[index];
14 | }
15 |
16 | size_t DisconnOutPacket::size() const {
17 | return 2;
18 | }
19 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Disconn.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "OutPacket.hpp"
4 | #include "../../Flags.hpp"
5 | #include "../../Helpers.hpp"
6 |
7 | namespace AsyncMqttClientInternals {
8 | class DisconnOutPacket : public OutPacket {
9 | public:
10 | DisconnOutPacket();
11 | const uint8_t* data(size_t index = 0) const;
12 | size_t size() const;
13 |
14 | private:
15 | uint8_t _data[2];
16 | };
17 | } // namespace AsyncMqttClientInternals
18 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/OutPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "OutPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::OutPacket;
4 |
5 | OutPacket::OutPacket()
6 | : next(nullptr)
7 | , timeout(0)
8 | , noTries(0)
9 | , _released(true)
10 | , _packetId(0) {}
11 |
12 | OutPacket::~OutPacket() {}
13 |
14 | bool OutPacket::released() const {
15 | return _released;
16 | }
17 |
18 | uint8_t OutPacket::packetType() const {
19 | return data(0)[0] >> 4;
20 | }
21 |
22 | uint16_t OutPacket::packetId() const {
23 | return _packetId;
24 | }
25 |
26 | uint8_t OutPacket::qos() const {
27 | if (packetType() == AsyncMqttClientInternals::PacketType.PUBLISH) {
28 | return (data()[1] & 0x06) >> 1;
29 | }
30 | return 0;
31 | }
32 |
33 | void OutPacket::release() {
34 | _released = true;
35 | }
36 |
37 | uint16_t OutPacket::_nextPacketId = 0;
38 |
39 | uint16_t OutPacket::_getNextPacketId() {
40 | if (++_nextPacketId == 0) {
41 | ++_nextPacketId;
42 | }
43 | return _nextPacketId;
44 | }
45 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/OutPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include // uint*_t
4 | #include // size_t
5 | #include // std::min
6 |
7 | #include "../../Flags.hpp"
8 |
9 | namespace AsyncMqttClientInternals {
10 | class OutPacket {
11 | public:
12 | OutPacket();
13 | virtual ~OutPacket();
14 | virtual const uint8_t* data(size_t index = 0) const = 0;
15 | virtual size_t size() const = 0;
16 | bool released() const;
17 | uint8_t packetType() const;
18 | uint16_t packetId() const;
19 | uint8_t qos() const;
20 | void release();
21 |
22 | public:
23 | OutPacket* next;
24 | uint32_t timeout;
25 | uint8_t noTries;
26 |
27 | protected:
28 | static uint16_t _getNextPacketId();
29 | bool _released;
30 | uint16_t _packetId;
31 |
32 | private:
33 | static uint16_t _nextPacketId;
34 | };
35 | } // namespace AsyncMqttClientInternals
36 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/PingReq.cpp:
--------------------------------------------------------------------------------
1 | #include "PingReq.hpp"
2 |
3 | using AsyncMqttClientInternals::PingReqOutPacket;
4 |
5 | PingReqOutPacket::PingReqOutPacket() {
6 | _data[0] = AsyncMqttClientInternals::PacketType.PINGREQ;
7 | _data[0] = _data[0] << 4;
8 | _data[0] = _data[0] | AsyncMqttClientInternals::HeaderFlag.PINGREQ_RESERVED;
9 | _data[1] = 0;
10 | }
11 |
12 | const uint8_t* PingReqOutPacket::data(size_t index) const {
13 | return &_data[index];;
14 | }
15 |
16 | size_t PingReqOutPacket::size() const {
17 | return 2;
18 | }
19 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/PingReq.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "OutPacket.hpp"
4 | #include "../../Flags.hpp"
5 | #include "../../Helpers.hpp"
6 |
7 | namespace AsyncMqttClientInternals {
8 | class PingReqOutPacket : public OutPacket {
9 | public:
10 | PingReqOutPacket();
11 | const uint8_t* data(size_t index = 0) const;
12 | size_t size() const;
13 |
14 | private:
15 | uint8_t _data[2];
16 | };
17 | } // namespace AsyncMqttClientInternals
18 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/PubAck.cpp:
--------------------------------------------------------------------------------
1 | #include "PubAck.hpp"
2 |
3 | using AsyncMqttClientInternals::PubAckOutPacket;
4 |
5 | PubAckOutPacket::PubAckOutPacket(PendingAck pendingAck) {
6 | _data[0] = pendingAck.packetType;
7 | _data[0] = _data[0] << 4;
8 | _data[0] = _data[0] | pendingAck.headerFlag;
9 | _data[1] = 2;
10 | _packetId = pendingAck.packetId;
11 | _data[2] = pendingAck.packetId >> 8;
12 | _data[3] = pendingAck.packetId & 0xFF;
13 | if (packetType() == AsyncMqttClientInternals::PacketType.PUBREL ||
14 | packetType() == AsyncMqttClientInternals::PacketType.PUBREC) {
15 | _released = false;
16 | }
17 | }
18 |
19 | const uint8_t* PubAckOutPacket::data(size_t index) const {
20 | return &_data[index];
21 | }
22 |
23 | size_t PubAckOutPacket::size() const {
24 | return 4;
25 | }
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/PubAck.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "OutPacket.hpp"
4 | #include "../../Flags.hpp"
5 | #include "../../Helpers.hpp"
6 | #include "../../Storage.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PubAckOutPacket : public OutPacket {
10 | public:
11 | explicit PubAckOutPacket(PendingAck pendingAck);
12 | const uint8_t* data(size_t index = 0) const;
13 | size_t size() const;
14 |
15 | private:
16 | uint8_t _data[4];
17 | };
18 | } // namespace AsyncMqttClientInternals
19 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Publish.cpp:
--------------------------------------------------------------------------------
1 | #include "Publish.hpp"
2 |
3 | using AsyncMqttClientInternals::PublishOutPacket;
4 |
5 | PublishOutPacket::PublishOutPacket(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length) {
6 | char fixedHeader[5];
7 | fixedHeader[0] = AsyncMqttClientInternals::PacketType.PUBLISH;
8 | fixedHeader[0] = fixedHeader[0] << 4;
9 | // if (dup) fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_DUP;
10 | if (retain) fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_RETAIN;
11 | switch (qos) {
12 | case 0:
13 | fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS0;
14 | break;
15 | case 1:
16 | fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS1;
17 | break;
18 | case 2:
19 | fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS2;
20 | break;
21 | }
22 |
23 | uint16_t topicLength = strlen(topic);
24 | char topicLengthBytes[2];
25 | topicLengthBytes[0] = topicLength >> 8;
26 | topicLengthBytes[1] = topicLength & 0xFF;
27 |
28 | uint32_t payloadLength = length;
29 | if (payload != nullptr && payloadLength == 0) payloadLength = strlen(payload);
30 |
31 | uint32_t remainingLength = 2 + topicLength + payloadLength;
32 | if (qos != 0) remainingLength += 2;
33 | uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(remainingLength, fixedHeader + 1);
34 |
35 | size_t neededSpace = 0;
36 | neededSpace += 1 + remainingLengthLength;
37 | neededSpace += 2;
38 | neededSpace += topicLength;
39 | if (qos != 0) neededSpace += 2;
40 | if (payload != nullptr) neededSpace += payloadLength;
41 |
42 | _data.reserve(neededSpace);
43 |
44 | _packetId = (qos !=0) ? _getNextPacketId() : 1;
45 | char packetIdBytes[2];
46 | packetIdBytes[0] = _packetId >> 8;
47 | packetIdBytes[1] = _packetId & 0xFF;
48 |
49 | _data.insert(_data.end(), fixedHeader, fixedHeader + 1 + remainingLengthLength);
50 | _data.insert(_data.end(), topicLengthBytes, topicLengthBytes + 2);
51 | _data.insert(_data.end(), topic, topic + topicLength);
52 | if (qos != 0) {
53 | _data.insert(_data.end(), packetIdBytes, packetIdBytes + 2);
54 | _released = false;
55 | }
56 | if (payload != nullptr) _data.insert(_data.end(), payload, payload + payloadLength);
57 | }
58 |
59 | const uint8_t* PublishOutPacket::data(size_t index) const {
60 | return &_data.data()[index];
61 | }
62 |
63 | size_t PublishOutPacket::size() const {
64 | return _data.size();
65 | }
66 |
67 | void PublishOutPacket::setDup() {
68 | _data[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_DUP;
69 | }
70 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Publish.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include // strlen
4 | #include
5 |
6 | #include "OutPacket.hpp"
7 | #include "../../Flags.hpp"
8 | #include "../../Helpers.hpp"
9 | #include "../../Storage.hpp"
10 |
11 | namespace AsyncMqttClientInternals {
12 | class PublishOutPacket : public OutPacket {
13 | public:
14 | PublishOutPacket(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length);
15 | const uint8_t* data(size_t index = 0) const;
16 | size_t size() const;
17 |
18 | void setDup(); // you cannot unset dup
19 |
20 | private:
21 | std::vector _data;
22 | };
23 | } // namespace AsyncMqttClientInternals
24 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Subscribe.cpp:
--------------------------------------------------------------------------------
1 | #include "Subscribe.hpp"
2 |
3 | using AsyncMqttClientInternals::SubscribeOutPacket;
4 |
5 | SubscribeOutPacket::SubscribeOutPacket(const char* topic, uint8_t qos) {
6 | char fixedHeader[5];
7 | fixedHeader[0] = AsyncMqttClientInternals::PacketType.SUBSCRIBE;
8 | fixedHeader[0] = fixedHeader[0] << 4;
9 | fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.SUBSCRIBE_RESERVED;
10 |
11 | uint16_t topicLength = strlen(topic);
12 | char topicLengthBytes[2];
13 | topicLengthBytes[0] = topicLength >> 8;
14 | topicLengthBytes[1] = topicLength & 0xFF;
15 |
16 | char qosByte[1];
17 | qosByte[0] = qos;
18 |
19 | uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(2 + 2 + topicLength + 1, fixedHeader + 1);
20 |
21 | size_t neededSpace = 0;
22 | neededSpace += 1 + remainingLengthLength;
23 | neededSpace += 2;
24 | neededSpace += 2;
25 | neededSpace += topicLength;
26 | neededSpace += 1;
27 |
28 | _data.reserve(neededSpace);
29 |
30 | _packetId = _getNextPacketId();
31 | char packetIdBytes[2];
32 | packetIdBytes[0] = _packetId >> 8;
33 | packetIdBytes[1] = _packetId & 0xFF;
34 |
35 | _data.insert(_data.end(), fixedHeader, fixedHeader + 1 + remainingLengthLength);
36 | _data.insert(_data.end(), packetIdBytes, packetIdBytes + 2);
37 | _data.insert(_data.end(), topicLengthBytes, topicLengthBytes + 2);
38 | _data.insert(_data.end(), topic, topic + topicLength);
39 | _data.push_back(qosByte[0]);
40 | _released = false;
41 | }
42 |
43 | const uint8_t* SubscribeOutPacket::data(size_t index) const {
44 | return &_data.data()[index];
45 | }
46 |
47 | size_t SubscribeOutPacket::size() const {
48 | return _data.size();
49 | }
50 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Subscribe.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include // strlen
4 | #include
5 |
6 | #include "OutPacket.hpp"
7 | #include "../../Flags.hpp"
8 | #include "../../Helpers.hpp"
9 | #include "../../Storage.hpp"
10 |
11 | namespace AsyncMqttClientInternals {
12 | class SubscribeOutPacket : public OutPacket {
13 | public:
14 | SubscribeOutPacket(const char* topic, uint8_t qos);
15 | const uint8_t* data(size_t index = 0) const;
16 | size_t size() const;
17 |
18 | private:
19 | std::vector _data;
20 | };
21 | } // namespace AsyncMqttClientInternals
22 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Unsubscribe.cpp:
--------------------------------------------------------------------------------
1 | #include "Unsubscribe.hpp"
2 |
3 | using AsyncMqttClientInternals::UnsubscribeOutPacket;
4 |
5 | UnsubscribeOutPacket::UnsubscribeOutPacket(const char* topic) {
6 | char fixedHeader[5];
7 | fixedHeader[0] = AsyncMqttClientInternals::PacketType.UNSUBSCRIBE;
8 | fixedHeader[0] = fixedHeader[0] << 4;
9 | fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.UNSUBSCRIBE_RESERVED;
10 |
11 | uint16_t topicLength = strlen(topic);
12 | char topicLengthBytes[2];
13 | topicLengthBytes[0] = topicLength >> 8;
14 | topicLengthBytes[1] = topicLength & 0xFF;
15 |
16 | uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(2 + 2 + topicLength, fixedHeader + 1);
17 |
18 | size_t neededSpace = 0;
19 | neededSpace += 1 + remainingLengthLength;
20 | neededSpace += 2;
21 | neededSpace += 2;
22 | neededSpace += topicLength;
23 |
24 | _packetId = _getNextPacketId();
25 | char packetIdBytes[2];
26 | packetIdBytes[0] = _packetId >> 8;
27 | packetIdBytes[1] = _packetId & 0xFF;
28 |
29 | _data.insert(_data.end(), fixedHeader, fixedHeader + 1 + remainingLengthLength);
30 | _data.insert(_data.end(), packetIdBytes, packetIdBytes + 2);
31 | _data.insert(_data.end(), topicLengthBytes, topicLengthBytes + 2);
32 | _data.insert(_data.end(), topic, topic + topicLength);
33 | _released = false;
34 | }
35 |
36 | const uint8_t* UnsubscribeOutPacket::data(size_t index) const {
37 | return &_data.data()[index];
38 | }
39 |
40 | size_t UnsubscribeOutPacket::size() const {
41 | return _data.size();
42 | }
43 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Out/Unsubscribe.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include // strlen
4 | #include
5 |
6 | #include "OutPacket.hpp"
7 | #include "../../Flags.hpp"
8 | #include "../../Helpers.hpp"
9 | #include "../../Storage.hpp"
10 |
11 | namespace AsyncMqttClientInternals {
12 | class UnsubscribeOutPacket : public OutPacket {
13 | public:
14 | explicit UnsubscribeOutPacket(const char* topic);
15 | const uint8_t* data(size_t index = 0) const;
16 | size_t size() const;
17 |
18 | private:
19 | std::vector _data;
20 | };
21 | } // namespace AsyncMqttClientInternals
22 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/Packet.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AsyncMqttClientInternals {
4 | class Packet {
5 | public:
6 | virtual ~Packet() {}
7 |
8 | virtual void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) = 0;
9 | virtual void parsePayload(char* data, size_t len, size_t* currentBytePosition) = 0;
10 | };
11 | } // namespace AsyncMqttClientInternals
12 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PingRespPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PingRespPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PingRespPacket;
4 |
5 | PingRespPacket::PingRespPacket(ParsingInformation* parsingInformation, OnPingRespInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback) {
8 | }
9 |
10 | PingRespPacket::~PingRespPacket() {
11 | }
12 |
13 | void PingRespPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
14 | (void)data;
15 | (void)currentBytePosition;
16 | }
17 |
18 | void PingRespPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
19 | (void)data;
20 | (void)currentBytePosition;
21 | }
22 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PingRespPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PingRespPacket : public Packet {
10 | public:
11 | explicit PingRespPacket(ParsingInformation* parsingInformation, OnPingRespInternalCallback callback);
12 | ~PingRespPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnPingRespInternalCallback _callback;
20 | };
21 | } // namespace AsyncMqttClientInternals
22 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubAckPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PubAckPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PubAckPacket;
4 |
5 | PubAckPacket::PubAckPacket(ParsingInformation* parsingInformation, OnPubAckInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | PubAckPacket::~PubAckPacket() {
14 | }
15 |
16 | void PubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_packetId);
24 | }
25 | }
26 |
27 | void PubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubAckPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PubAckPacket : public Packet {
10 | public:
11 | explicit PubAckPacket(ParsingInformation* parsingInformation, OnPubAckInternalCallback callback);
12 | ~PubAckPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnPubAckInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubCompPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PubCompPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PubCompPacket;
4 |
5 | PubCompPacket::PubCompPacket(ParsingInformation* parsingInformation, OnPubCompInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | PubCompPacket::~PubCompPacket() {
14 | }
15 |
16 | void PubCompPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_packetId);
24 | }
25 | }
26 |
27 | void PubCompPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubCompPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PubCompPacket : public Packet {
10 | public:
11 | explicit PubCompPacket(ParsingInformation* parsingInformation, OnPubCompInternalCallback callback);
12 | ~PubCompPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnPubCompInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubRecPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PubRecPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PubRecPacket;
4 |
5 | PubRecPacket::PubRecPacket(ParsingInformation* parsingInformation, OnPubRecInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | PubRecPacket::~PubRecPacket() {
14 | }
15 |
16 | void PubRecPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_packetId);
24 | }
25 | }
26 |
27 | void PubRecPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubRecPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PubRecPacket : public Packet {
10 | public:
11 | explicit PubRecPacket(ParsingInformation* parsingInformation, OnPubRecInternalCallback callback);
12 | ~PubRecPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnPubRecInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubRelPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PubRelPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PubRelPacket;
4 |
5 | PubRelPacket::PubRelPacket(ParsingInformation* parsingInformation, OnPubRelInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | PubRelPacket::~PubRelPacket() {
14 | }
15 |
16 | void PubRelPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_packetId);
24 | }
25 | }
26 |
27 | void PubRelPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PubRelPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class PubRelPacket : public Packet {
10 | public:
11 | explicit PubRelPacket(ParsingInformation* parsingInformation, OnPubRelInternalCallback callback);
12 | ~PubRelPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnPubRelInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PublishPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "PublishPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::PublishPacket;
4 |
5 | PublishPacket::PublishPacket(ParsingInformation* parsingInformation, OnMessageInternalCallback dataCallback, OnPublishInternalCallback completeCallback)
6 | : _parsingInformation(parsingInformation)
7 | , _dataCallback(dataCallback)
8 | , _completeCallback(completeCallback)
9 | , _dup(false)
10 | , _qos(0)
11 | , _retain(0)
12 | , _bytePosition(0)
13 | , _topicLengthMsb(0)
14 | , _topicLength(0)
15 | , _ignore(false)
16 | , _packetIdMsb(0)
17 | , _packetId(0)
18 | , _payloadLength(0)
19 | , _payloadBytesRead(0) {
20 | _dup = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_DUP;
21 | _retain = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_RETAIN;
22 | char qosMasked = _parsingInformation->packetFlags & 0x06;
23 | switch (qosMasked) {
24 | case HeaderFlag.PUBLISH_QOS0:
25 | _qos = 0;
26 | break;
27 | case HeaderFlag.PUBLISH_QOS1:
28 | _qos = 1;
29 | break;
30 | case HeaderFlag.PUBLISH_QOS2:
31 | _qos = 2;
32 | break;
33 | }
34 | }
35 |
36 | PublishPacket::~PublishPacket() {
37 | }
38 |
39 | void PublishPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
40 | char currentByte = data[(*currentBytePosition)++];
41 | if (_bytePosition == 0) {
42 | _topicLengthMsb = currentByte;
43 | } else if (_bytePosition == 1) {
44 | _topicLength = currentByte | _topicLengthMsb << 8;
45 | if (_topicLength > _parsingInformation->maxTopicLength) {
46 | _ignore = true;
47 | } else {
48 | _parsingInformation->topicBuffer[_topicLength] = '\0';
49 | }
50 | } else if (_bytePosition >= 2 && _bytePosition < 2 + _topicLength) {
51 | // Starting from here, _ignore might be true
52 | if (!_ignore) _parsingInformation->topicBuffer[_bytePosition - 2] = currentByte;
53 | if (_bytePosition == 2 + _topicLength - 1 && _qos == 0) {
54 | _preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
55 | return;
56 | }
57 | } else if (_bytePosition == 2 + _topicLength) {
58 | _packetIdMsb = currentByte;
59 | } else {
60 | _packetId = currentByte | _packetIdMsb << 8;
61 | _preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
62 | }
63 | _bytePosition++;
64 | }
65 |
66 | void PublishPacket::_preparePayloadHandling(uint32_t payloadLength) {
67 | _payloadLength = payloadLength;
68 | if (payloadLength == 0) {
69 | _parsingInformation->bufferState = BufferState::NONE;
70 | if (!_ignore) {
71 | _dataCallback(_parsingInformation->topicBuffer, nullptr, _qos, _dup, _retain, 0, 0, 0, _packetId);
72 | _completeCallback(_packetId, _qos);
73 | }
74 | } else {
75 | _parsingInformation->bufferState = BufferState::PAYLOAD;
76 | }
77 | }
78 |
79 | void PublishPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
80 | size_t remainToRead = len - (*currentBytePosition);
81 | if (_payloadBytesRead + remainToRead > _payloadLength) remainToRead = _payloadLength - _payloadBytesRead;
82 |
83 | if (!_ignore) _dataCallback(_parsingInformation->topicBuffer, data + (*currentBytePosition), _qos, _dup, _retain, remainToRead, _payloadBytesRead, _payloadLength, _packetId);
84 | _payloadBytesRead += remainToRead;
85 | (*currentBytePosition) += remainToRead;
86 |
87 | if (_payloadBytesRead == _payloadLength) {
88 | _parsingInformation->bufferState = BufferState::NONE;
89 | if (!_ignore) _completeCallback(_packetId, _qos);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/PublishPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../Flags.hpp"
6 | #include "../ParsingInformation.hpp"
7 | #include "../Callbacks.hpp"
8 |
9 | namespace AsyncMqttClientInternals {
10 | class PublishPacket : public Packet {
11 | public:
12 | explicit PublishPacket(ParsingInformation* parsingInformation, OnMessageInternalCallback dataCallback, OnPublishInternalCallback completeCallback);
13 | ~PublishPacket();
14 |
15 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
16 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
17 |
18 | private:
19 | ParsingInformation* _parsingInformation;
20 | OnMessageInternalCallback _dataCallback;
21 | OnPublishInternalCallback _completeCallback;
22 |
23 | void _preparePayloadHandling(uint32_t payloadLength);
24 |
25 | bool _dup;
26 | uint8_t _qos;
27 | bool _retain;
28 |
29 | uint8_t _bytePosition;
30 | char _topicLengthMsb;
31 | uint16_t _topicLength;
32 | bool _ignore;
33 | char _packetIdMsb;
34 | uint16_t _packetId;
35 | uint32_t _payloadLength;
36 | uint32_t _payloadBytesRead;
37 | };
38 | } // namespace AsyncMqttClientInternals
39 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/SubAckPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "SubAckPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::SubAckPacket;
4 |
5 | SubAckPacket::SubAckPacket(ParsingInformation* parsingInformation, OnSubAckInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | SubAckPacket::~SubAckPacket() {
14 | }
15 |
16 | void SubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::PAYLOAD;
23 | }
24 | }
25 |
26 | void SubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
27 | char status = data[(*currentBytePosition)++];
28 |
29 | /* switch (status) {
30 | case 0:
31 | Serial.println("Success QoS 0");
32 | break;
33 | case 1:
34 | Serial.println("Success QoS 1");
35 | break;
36 | case 2:
37 | Serial.println("Success QoS 2");
38 | break;
39 | case 0x80:
40 | Serial.println("Failure");
41 | break;
42 | } */
43 |
44 | _parsingInformation->bufferState = BufferState::NONE;
45 | _callback(_packetId, status);
46 | }
47 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/SubAckPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class SubAckPacket : public Packet {
10 | public:
11 | explicit SubAckPacket(ParsingInformation* parsingInformation, OnSubAckInternalCallback callback);
12 | ~SubAckPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnSubAckInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/UnsubAckPacket.cpp:
--------------------------------------------------------------------------------
1 | #include "UnsubAckPacket.hpp"
2 |
3 | using AsyncMqttClientInternals::UnsubAckPacket;
4 |
5 | UnsubAckPacket::UnsubAckPacket(ParsingInformation* parsingInformation, OnUnsubAckInternalCallback callback)
6 | : _parsingInformation(parsingInformation)
7 | , _callback(callback)
8 | , _bytePosition(0)
9 | , _packetIdMsb(0)
10 | , _packetId(0) {
11 | }
12 |
13 | UnsubAckPacket::~UnsubAckPacket() {
14 | }
15 |
16 | void UnsubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
17 | char currentByte = data[(*currentBytePosition)++];
18 | if (_bytePosition++ == 0) {
19 | _packetIdMsb = currentByte;
20 | } else {
21 | _packetId = currentByte | _packetIdMsb << 8;
22 | _parsingInformation->bufferState = BufferState::NONE;
23 | _callback(_packetId);
24 | }
25 | }
26 |
27 | void UnsubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
28 | (void)data;
29 | (void)currentBytePosition;
30 | }
31 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Packets/UnsubAckPacket.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Arduino.h"
4 | #include "Packet.hpp"
5 | #include "../ParsingInformation.hpp"
6 | #include "../Callbacks.hpp"
7 |
8 | namespace AsyncMqttClientInternals {
9 | class UnsubAckPacket : public Packet {
10 | public:
11 | explicit UnsubAckPacket(ParsingInformation* parsingInformation, OnUnsubAckInternalCallback callback);
12 | ~UnsubAckPacket();
13 |
14 | void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
15 | void parsePayload(char* data, size_t len, size_t* currentBytePosition);
16 |
17 | private:
18 | ParsingInformation* _parsingInformation;
19 | OnUnsubAckInternalCallback _callback;
20 |
21 | uint8_t _bytePosition;
22 | char _packetIdMsb;
23 | uint16_t _packetId;
24 | };
25 | } // namespace AsyncMqttClientInternals
26 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/ParsingInformation.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AsyncMqttClientInternals {
4 | enum class BufferState : uint8_t {
5 | NONE = 0,
6 | REMAINING_LENGTH = 2,
7 | VARIABLE_HEADER = 3,
8 | PAYLOAD = 4
9 | };
10 |
11 | struct ParsingInformation {
12 | BufferState bufferState;
13 |
14 | uint16_t maxTopicLength;
15 | char* topicBuffer;
16 |
17 | uint8_t packetType;
18 | uint16_t packetFlags;
19 | uint32_t remainingLength;
20 | };
21 | } // namespace AsyncMqttClientInternals
22 |
--------------------------------------------------------------------------------
/src/AsyncMqttClient/Storage.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace AsyncMqttClientInternals {
4 | struct PendingPubRel {
5 | uint16_t packetId;
6 | };
7 |
8 | struct PendingAck {
9 | uint8_t packetType;
10 | uint8_t headerFlag;
11 | uint16_t packetId;
12 | };
13 | } // namespace AsyncMqttClientInternals
14 |
--------------------------------------------------------------------------------