diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 2a270b1..0856b05 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v1 + - uses: actions/setup-python@v4 with: python-version: '3.x' - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: repository: adafruit/ci-arduino path: ci diff --git a/Adafruit_MQTT.cpp b/Adafruit_MQTT.cpp index 06907d5..08fc6ba 100644 --- a/Adafruit_MQTT.cpp +++ b/Adafruit_MQTT.cpp @@ -96,6 +96,22 @@ static uint8_t *stringprint(uint8_t *p, const char *s, uint16_t maxlen = 0) { return p + len; } +// packetAdditionalLen is a helper function used to figure out +// how bigger the payload needs to be in order to account for +// its variable length field. As per +// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.4_Size +// See also readFullPacket +static uint16_t packetAdditionalLen(uint32_t currLen) { + /* Increase length field based on current length */ + if (currLen < 128) // 7-bits + return 0; + if (currLen < 16384) // 14-bits + return 1; + if (currLen < 2097152) // 21-bits + return 2; + return 3; +} + // Adafruit_MQTT Definition //////////////////////////////////////////////////// Adafruit_MQTT::Adafruit_MQTT(const char *server, uint16_t port, const char *cid, @@ -117,6 +133,7 @@ Adafruit_MQTT::Adafruit_MQTT(const char *server, uint16_t port, const char *cid, will_retain = 0; packet_id_counter = 1; // MQTT spec forbids packet id of 0 if QOS=1 + keepAliveInterval = MQTT_CONN_KEEPALIVE; } Adafruit_MQTT::Adafruit_MQTT(const char *server, uint16_t port, @@ -138,6 +155,8 @@ Adafruit_MQTT::Adafruit_MQTT(const char *server, uint16_t port, will_retain = 0; packet_id_counter = 1; // MQTT spec forbids packet id of 0 if QOS=1 + keepAliveInterval = MQTT_CONN_KEEPALIVE; + } int8_t Adafruit_MQTT::connect() { @@ -202,6 +221,32 @@ int8_t Adafruit_MQTT::connect(const char *user, const char *pass) { return connect(); } +void Adafruit_MQTT::processSubscriptionPacket(Adafruit_MQTT_Subscribe *sub) { + if (sub->callback_uint32t != NULL) { + // execute callback in integer mode + uint32_t data = 0; + data = atoi((char *)sub->lastread); + sub->callback_uint32t(data); + } else if (sub->callback_double != NULL) { + // execute callback in doublefloat mode + double data = 0; + data = atof((char *)sub->lastread); + sub->callback_double(data); + } else if (sub->callback_buffer != NULL) { + // execute callback in buffer mode + sub->callback_buffer((char *)sub->lastread, sub->datalen); + } else if (sub->callback_io != NULL) { + // execute callback in io mode + ((sub->io_mqtt)->*(sub->callback_io))((char *)sub->lastread, sub->datalen); + } else { + DEBUG_PRINTLN( + "ERROR: Subscription packet did not have an associated callback"); + return; + } + // mark subscription message as "read"" + sub->new_message = false; +} + uint16_t Adafruit_MQTT::processPacketsUntil(uint8_t *buffer, uint8_t waitforpackettype, uint16_t timeout) { @@ -219,7 +264,9 @@ uint16_t Adafruit_MQTT::processPacketsUntil(uint8_t *buffer, return len; } else { if (packetType == MQTT_CTRL_PUBLISH) { - handleSubscriptionPacket(len); + Adafruit_MQTT_Subscribe *sub = handleSubscriptionPacket(len); + if (sub) + processSubscriptionPacket(sub); } else { ERROR_PRINTLN(F("Dropped a packet")); } @@ -233,7 +280,7 @@ uint16_t Adafruit_MQTT::readFullPacket(uint8_t *buffer, uint16_t maxsize, // will read a packet and Do The Right Thing with length uint8_t *pbuff = buffer; - uint8_t rlen; + uint16_t rlen; // read the packet type: rlen = readPacket(pbuff, 1, timeout); @@ -267,7 +314,8 @@ uint16_t Adafruit_MQTT::readFullPacket(uint8_t *buffer, uint16_t maxsize, DEBUG_PRINT(F("Packet Length:\t")); DEBUG_PRINTLN(value); - if (value > (maxsize - (pbuff - buffer) - 1)) { + // maxsize is limited to 65536 by 16-bit unsigned + if (value > uint32_t(maxsize - (pbuff - buffer) - 1)) { DEBUG_PRINTLN(F("Packet too big for buffer")); rlen = readPacket(pbuff, (maxsize - (pbuff - buffer) - 1), timeout); } else { @@ -316,26 +364,29 @@ bool Adafruit_MQTT::disconnect() { return disconnectServer(); } -bool Adafruit_MQTT::publish(const char *topic, const char *data, uint8_t qos) { - return publish(topic, (uint8_t *)(data), strlen(data), qos); +bool Adafruit_MQTT::publish(const char *topic, const char *data, uint8_t qos, + bool retain) { + return publish(topic, (uint8_t *)(data), strlen(data), qos, retain); } bool Adafruit_MQTT::publish(const char *topic, uint8_t *data, uint16_t bLen, - uint8_t qos) { + uint8_t qos, bool retain) { // Construct and send publish packet. - uint16_t len = publishPacket(buffer, topic, data, bLen, qos); + uint16_t len = publishPacket(buffer, topic, data, bLen, qos, + (uint16_t)sizeof(buffer), retain); + if (!sendPacket(buffer, len)) return false; // If QOS level is high enough verify the response packet. if (qos > 0) { - len = readFullPacket(buffer, MAXBUFFERSIZE, PUBLISH_TIMEOUT_MS); + len = processPacketsUntil(buffer, MQTT_CTRL_PUBACK, PUBLISH_TIMEOUT_MS); + DEBUG_PRINT(F("Publish QOS1+ reply:\t")); DEBUG_PRINTBUFFER(buffer, len); if (len != 4) return false; - if ((buffer[0] >> 4) != MQTT_CTRL_PUBACK) - return false; + uint16_t packnum = buffer[2]; packnum <<= 8; packnum |= buffer[3]; @@ -366,6 +417,25 @@ bool Adafruit_MQTT::will(const char *topic, const char *payload, uint8_t qos, return true; } +/***************************************************************************/ +/*! + @brief Sets the connect packet's KeepAlive Interval, in seconds. This + function MUST be called prior to connect(). + @param keepAlive + Maximum amount of time without communication between the + client and the MQTT broker, in seconds. + @returns True if called prior to connect(), False otherwise. +*/ +/***************************************************************************/ +bool Adafruit_MQTT::setKeepAliveInterval(uint16_t keepAlive) { + if (connected()) { + DEBUG_PRINT(F("keepAlive defined after connection established.")); + return false; + } + keepAliveInterval = keepAlive; + return true; +} + bool Adafruit_MQTT::subscribe(Adafruit_MQTT_Subscribe *sub) { uint8_t i; // see if we are already subscribed @@ -437,27 +507,8 @@ void Adafruit_MQTT::processPackets(int16_t timeout) { while (elapsed < (uint32_t)timeout) { Adafruit_MQTT_Subscribe *sub = readSubscription(timeout - elapsed); - if (sub) { - if (sub->callback_uint32t != NULL) { - // huh lets do the callback in integer mode - uint32_t data = 0; - data = atoi((char *)sub->lastread); - sub->callback_uint32t(data); - } else if (sub->callback_double != NULL) { - // huh lets do the callback in doublefloat mode - double data = 0; - data = atof((char *)sub->lastread); - sub->callback_double(data); - } else if (sub->callback_buffer != NULL) { - // huh lets do the callback in buffer mode - sub->callback_buffer((char *)sub->lastread, sub->datalen); - } else if (sub->callback_io != NULL) { - // huh lets do the callback in io mode - ((sub->io_mqtt)->*(sub->callback_io))((char *)sub->lastread, - sub->datalen); - } - } - + if (sub) + processSubscriptionPacket(sub); // keep track over elapsed time endtime = millis(); if (endtime < starttime) { @@ -467,10 +518,32 @@ void Adafruit_MQTT::processPackets(int16_t timeout) { } } Adafruit_MQTT_Subscribe *Adafruit_MQTT::readSubscription(int16_t timeout) { - // Check if data is available to read. - uint16_t len = - readFullPacket(buffer, MAXBUFFERSIZE, timeout); // return one full packet - return handleSubscriptionPacket(len); + + // Sync or Async subscriber with message + Adafruit_MQTT_Subscribe *s = 0; + + // Check if are unread messages + for (uint8_t i = 0; i < MAXSUBSCRIPTIONS; i++) { + if (subscriptions[i] && subscriptions[i]->new_message) { + s = subscriptions[i]; + break; + } + } + + // not unread message + if (!s) { + // Check if data is available to read. + uint16_t len = readFullPacket(buffer, MAXBUFFERSIZE, + timeout); // return one full packet + s = handleSubscriptionPacket(len); + } + + // it there is a message, mark it as not pending + if (s) { + s->new_message = false; + } + + return s; } Adafruit_MQTT_Subscribe *Adafruit_MQTT::handleSubscriptionPacket(uint16_t len) { @@ -491,7 +564,9 @@ Adafruit_MQTT_Subscribe *Adafruit_MQTT::handleSubscriptionPacket(uint16_t len) { } // Parse out length of packet. - topiclen = buffer[3]; + uint16_t const topicoffset = packetAdditionalLen(len); + uint16_t const topicstart = topicoffset + 4; + topiclen = buffer[3 + topicoffset]; DEBUG_PRINT(F("Looking for subscription len ")); DEBUG_PRINTLN(topiclen); @@ -504,10 +579,16 @@ Adafruit_MQTT_Subscribe *Adafruit_MQTT::handleSubscriptionPacket(uint16_t len) { continue; // Stop if the subscription topic matches the received topic. Be careful // to make comparison case insensitive. - if (strncasecmp((char *)buffer + 4, subscriptions[i]->topic, topiclen) == - 0) { + if (strncasecmp((char *)buffer + topicstart, subscriptions[i]->topic, + topiclen) == 0) { DEBUG_PRINT(F("Found sub #")); DEBUG_PRINTLN(i); + if (subscriptions[i]->new_message) { + DEBUG_PRINTLN(F("Lost previous message")); + } else { + subscriptions[i]->new_message = true; + } + break; } } @@ -520,21 +601,21 @@ Adafruit_MQTT_Subscribe *Adafruit_MQTT::handleSubscriptionPacket(uint16_t len) { // Check if it is QoS 1, TODO: we dont support QoS 2 if ((buffer[0] & 0x6) == 0x2) { packet_id_len = 2; - packetid = buffer[topiclen + 4]; + packetid = buffer[topiclen + topicstart]; packetid <<= 8; - packetid |= buffer[topiclen + 5]; + packetid |= buffer[topiclen + topicstart + 1]; } // zero out the old data memset(subscriptions[i]->lastread, 0, SUBSCRIPTIONDATALEN); - datalen = len - topiclen - packet_id_len - 4; + datalen = len - topiclen - packet_id_len - topicstart; if (datalen > SUBSCRIPTIONDATALEN) { datalen = SUBSCRIPTIONDATALEN - 1; // cut it off } // extract out just the data, into the subscription object itself - memmove(subscriptions[i]->lastread, buffer + 4 + topiclen + packet_id_len, - datalen); + memmove(subscriptions[i]->lastread, + buffer + topicstart + topiclen + packet_id_len, datalen); subscriptions[i]->datalen = datalen; DEBUG_PRINT(F("Data len: ")); DEBUG_PRINTLN(datalen); @@ -629,9 +710,9 @@ uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) { p[0] |= MQTT_CONN_PASSWORDFLAG; p++; - p[0] = MQTT_CONN_KEEPALIVE >> 8; + p[0] = keepAliveInterval >> 8; p++; - p[0] = MQTT_CONN_KEEPALIVE & 0xFF; + p[0] = keepAliveInterval & 0xFF; p++; if (MQTT_PROTOCOL_LEVEL == 3) { @@ -671,8 +752,8 @@ uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) { // as per // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718040 uint16_t Adafruit_MQTT::publishPacket(uint8_t *packet, const char *topic, - uint8_t *data, uint16_t bLen, - uint8_t qos) { + uint8_t *data, uint16_t bLen, uint8_t qos, + uint16_t maxPacketLen, bool retain) { uint8_t *p = packet; uint16_t len = 0; @@ -682,10 +763,27 @@ uint16_t Adafruit_MQTT::publishPacket(uint8_t *packet, const char *topic, if (qos > 0) { len += 2; // qos packet id } - len += bLen; // payload length + // Calculate additional bytes for length field (if any) + uint16_t additionalLen = packetAdditionalLen(len + bLen); + + // Payload remaining length. When maxPacketLen provided is 0, let's + // assume buffer is big enough. Fingers crossed. + // 2 + additionalLen: header byte + remaining length field (from 1 to 4 bytes) + // len = topic size field + value (string) + // bLen = buffer size + if (!(maxPacketLen == 0 || + (len + bLen + 2 + additionalLen <= maxPacketLen))) { + // If we make it here, we got a pickle: the payload is not going + // to fit in the packet buffer. Instead of corrupting memory, let's + // do something less damaging by reducing the bLen to what we are + // able to accomodate. Alternatively, consider using a bigger + // maxPacketLen. + bLen = maxPacketLen - (len + 2 + packetAdditionalLen(maxPacketLen)); + } + len += bLen; // remaining len excludes header byte & length field // Now you can start generating the packet! - p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1; + p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1 | (retain ? 1 : 0); p++; // fill in packet[1] last @@ -810,33 +908,34 @@ Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, topic = feed; qos = q; } -bool Adafruit_MQTT_Publish::publish(int32_t i) { + +bool Adafruit_MQTT_Publish::publish(int32_t i, bool retain) { char payload[12]; ltoa(i, payload, 10); - return mqtt->publish(topic, payload, qos); + return mqtt->publish(topic, payload, qos, retain); } -bool Adafruit_MQTT_Publish::publish(uint32_t i) { +bool Adafruit_MQTT_Publish::publish(uint32_t i, bool retain) { char payload[11]; ultoa(i, payload, 10); - return mqtt->publish(topic, payload, qos); + return mqtt->publish(topic, payload, qos, retain); } -bool Adafruit_MQTT_Publish::publish(double f, uint8_t precision) { +bool Adafruit_MQTT_Publish::publish(double f, uint8_t precision, bool retain) { char payload[41]; // Need to technically hold float max, 39 digits and minus // sign. dtostrf(f, 0, precision, payload); - return mqtt->publish(topic, payload, qos); + return mqtt->publish(topic, payload, qos, retain); } -bool Adafruit_MQTT_Publish::publish(const char *payload) { - return mqtt->publish(topic, payload, qos); +bool Adafruit_MQTT_Publish::publish(const char *payload, bool retain) { + return mqtt->publish(topic, payload, qos, retain); } // publish buffer of arbitrary length -bool Adafruit_MQTT_Publish::publish(uint8_t *payload, uint16_t bLen) { - - return mqtt->publish(topic, payload, bLen, qos); +bool Adafruit_MQTT_Publish::publish(uint8_t *payload, uint16_t bLen, + bool retain) { + return mqtt->publish(topic, payload, bLen, qos, retain); } // Adafruit_MQTT_Subscribe Definition ////////////////////////////////////////// @@ -852,6 +951,7 @@ Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, callback_double = 0; callback_io = 0; io_mqtt = 0; + new_message = false; } void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackUInt32Type cb) { diff --git a/Adafruit_MQTT.h b/Adafruit_MQTT.h index 089d0d4..01b373b 100644 --- a/Adafruit_MQTT.h +++ b/Adafruit_MQTT.h @@ -34,7 +34,7 @@ #define ADAFRUIT_MQTT_VERSION_PATCH 0 // Uncomment/comment to turn on/off debug output messages. -//#define MQTT_DEBUG +// #define MQTT_DEBUG // Uncomment/comment to turn on/off error output messages. #define MQTT_ERROR @@ -184,11 +184,15 @@ public: bool will(const char *topic, const char *payload, uint8_t qos = 0, uint8_t retain = 0); + // Sets the KeepAlive Interval, in seconds. + bool setKeepAliveInterval(uint16_t keepAlive); + // Publish a message to a topic using the specified QoS level. Returns true // if the message was published, false otherwise. - bool publish(const char *topic, const char *payload, uint8_t qos = 0); + bool publish(const char *topic, const char *payload, uint8_t qos = 0, + bool retain = false); bool publish(const char *topic, uint8_t *payload, uint16_t bLen, - uint8_t qos = 0); + uint8_t qos = 0, bool retain = false); // Add a subscription to receive messages for a topic. Returns true if the // subscription could be added or was already present, false otherwise. @@ -206,10 +210,12 @@ public: // messages! Adafruit_MQTT_Subscribe *readSubscription(int16_t timeout = 0); - // Handle any data coming in for subscriptions and fires them off to the - // appropriate callback + // Handle any data coming in for subscriptions Adafruit_MQTT_Subscribe *handleSubscriptionPacket(uint16_t len); + // Execute a subscription packet's associated callback and mark as "read" + void processSubscriptionPacket(Adafruit_MQTT_Subscribe *sub); + void processPackets(int16_t timeout); // Ping the server to ensure the connection is still alive. @@ -250,6 +256,7 @@ protected: const char *will_payload; uint8_t will_qos; uint8_t will_retain; + uint16_t keepAliveInterval; // MQTT KeepAlive time interval, in seconds uint8_t buffer[MAXBUFFERSIZE]; // one buffer, used for all incoming/outgoing uint16_t packet_id_counter; @@ -262,7 +269,8 @@ private: uint8_t connectPacket(uint8_t *packet); uint8_t disconnectPacket(uint8_t *packet); uint16_t publishPacket(uint8_t *packet, const char *topic, uint8_t *payload, - uint16_t bLen, uint8_t qos); + uint16_t bLen, uint8_t qos, uint16_t maxPacketLen = 0, + bool retain = false); uint8_t subscribePacket(uint8_t *packet, const char *topic, uint8_t qos); uint8_t unsubscribePacket(uint8_t *packet, const char *topic); uint8_t pingPacket(uint8_t *packet); @@ -274,15 +282,16 @@ public: Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, const char *feed, uint8_t qos = 0); - bool publish(const char *s); + bool publish(const char *s, bool retain = false); bool publish( double f, uint8_t precision = - 2); // Precision controls the minimum number of digits after decimal. - // This might be ignored and a higher precision value sent. - bool publish(int32_t i); - bool publish(uint32_t i); - bool publish(uint8_t *b, uint16_t bLen); + 2, // Precision controls the minimum number of digits after decimal. + // This might be ignored and a higher precision value sent. + bool retain = false); + bool publish(int32_t i, bool retain = false); + bool publish(uint32_t i, bool retain = false); + bool publish(uint8_t *b, uint16_t bLen, bool retain = false); private: Adafruit_MQTT *mqtt; @@ -316,8 +325,10 @@ public: AdafruitIO_MQTT *io_mqtt; + bool new_message; + private: Adafruit_MQTT *mqtt; }; -#endif +#endif \ No newline at end of file diff --git a/Adafruit_MQTT_Client.cpp b/Adafruit_MQTT_Client.cpp index 560ef46..9aa136c 100644 --- a/Adafruit_MQTT_Client.cpp +++ b/Adafruit_MQTT_Client.cpp @@ -83,17 +83,18 @@ uint16_t Adafruit_MQTT_Client::readPacket(uint8_t *buffer, uint16_t maxlen, bool Adafruit_MQTT_Client::sendPacket(uint8_t *buffer, uint16_t len) { uint16_t ret = 0; - + uint16_t offset = 0; while (len > 0) { if (client->connected()) { // send 250 bytes at most at a time, can adjust this later based on Client uint16_t sendlen = len > 250 ? 250 : len; // Serial.print("Sending: "); Serial.println(sendlen); - ret = client->write(buffer, sendlen); + ret = client->write(buffer + offset, sendlen); DEBUG_PRINT(F("Client sendPacket returned: ")); DEBUG_PRINTLN(ret); len -= ret; + offset += ret; if (ret != sendlen) { DEBUG_PRINTLN("Failed to send packet."); diff --git a/examples/adafruitio_secure_esp32/.esp8266.test.skip b/examples/adafruitio_secure_esp32/.esp8266.test.skip new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/adafruitio_secure_esp32/.esp8266.test.skip @@ -0,0 +1 @@ + diff --git a/examples/adafruitio_secure_esp32/.zero.test.skip b/examples/adafruitio_secure_esp32/.zero.test.skip new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/adafruitio_secure_esp32/.zero.test.skip @@ -0,0 +1 @@ + diff --git a/examples/adafruitio_secure_esp32/adafruitio_secure_esp32.ino b/examples/adafruitio_secure_esp32/adafruitio_secure_esp32.ino new file mode 100644 index 0000000..f573ede --- /dev/null +++ b/examples/adafruitio_secure_esp32/adafruitio_secure_esp32.ino @@ -0,0 +1,160 @@ +/*********************************************************************** + Adafruit MQTT Library ESP32 Adafruit IO SSL/TLS example + + Use the latest version of the ESP32 Arduino Core: + https://github.com/espressif/arduino-esp32 + + Works great with Adafruit Huzzah32 Feather and Breakout Board: + https://www.adafruit.com/product/3405 + https://www.adafruit.com/products/4172 + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Tony DiCola for Adafruit Industries. + Modified by Brent Rubell for Adafruit Industries + MIT license, all text above must be included in any redistribution + **********************************************************************/ +#include +#include "WiFiClientSecure.h" +#include "Adafruit_MQTT.h" +#include "Adafruit_MQTT_Client.h" + +/************************* WiFi Access Point *********************************/ + +#define WLAN_SSID "WLAN_SSID" +#define WLAN_PASS "WIFI_PASSWORD" + +/************************* Adafruit.io Setup *********************************/ + +#define AIO_SERVER "io.adafruit.com" + +// Using port 8883 for MQTTS +#define AIO_SERVERPORT 8883 + +// Adafruit IO Account Configuration +// (to obtain these values, visit https://io.adafruit.com and click on Active Key) +#define AIO_USERNAME "YOUR_ADAFRUIT_IO_USERNAME" +#define AIO_KEY "YOUR_ADAFRUIT_IO_KEY" + +/************ Global State (you don't need to change this!) ******************/ + +// WiFiFlientSecure for SSL/TLS support +WiFiClientSecure client; + +// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. +Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); + +// io.adafruit.com root CA +const char* adafruitio_root_ca = \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \ + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \ + "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \ + "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \ + "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \ + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \ + "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \ + "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \ + "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \ + "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \ + "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \ + "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \ + "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \ + "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \ + "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \ + "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \ + "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \ + "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \ + "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \ + "-----END CERTIFICATE-----\n"; + +/****************************** Feeds ***************************************/ + +// Setup a feed called 'test' for publishing. +// Notice MQTT paths for AIO follow the form: /feeds/ +Adafruit_MQTT_Publish test = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/test"); + +/*************************** Sketch Code ************************************/ + +void setup() { + Serial.begin(115200); + delay(10); + + Serial.println(F("Adafruit IO MQTTS (SSL/TLS) Example")); + + // Connect to WiFi access point. + Serial.println(); Serial.println(); + Serial.print("Connecting to "); + Serial.println(WLAN_SSID); + + delay(1000); + + WiFi.begin(WLAN_SSID, WLAN_PASS); + delay(2000); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + + Serial.println("WiFi connected"); + Serial.println("IP address: "); Serial.println(WiFi.localIP()); + + // Set Adafruit IO's root CA + client.setCACert(adafruitio_root_ca); +} + +uint32_t x=0; + +void loop() { + // Ensure the connection to the MQTT server is alive (this will make the first + // connection and automatically reconnect when disconnected). See the MQTT_connect + // function definition further below. + MQTT_connect(); + + // Now we can publish stuff! + Serial.print(F("\nSending val ")); + Serial.print(x); + Serial.print(F(" to test feed...")); + if (! test.publish(x++)) { + Serial.println(F("Failed")); + } else { + Serial.println(F("OK!")); + } + + // wait a couple seconds to avoid rate limit + delay(2000); + +} + +// Function to connect and reconnect as necessary to the MQTT server. +// Should be called in the loop function and it will take care if connecting. +void MQTT_connect() { + int8_t ret; + + // Stop if already connected. + if (mqtt.connected()) { + return; + } + + Serial.print("Connecting to MQTT... "); + + uint8_t retries = 3; + while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected + Serial.println(mqtt.connectErrorString(ret)); + Serial.println("Retrying MQTT connection in 5 seconds..."); + mqtt.disconnect(); + delay(5000); // wait 5 seconds + retries--; + if (retries == 0) { + // basically die and wait for WDT to reset me + while (1); + } + } + + Serial.println("MQTT Connected!"); +} \ No newline at end of file diff --git a/examples/adafruitio_secure_esp8266/adafruitio_secure_esp8266.ino b/examples/adafruitio_secure_esp8266/adafruitio_secure_esp8266.ino index bd069fe..87ad8ea 100644 --- a/examples/adafruitio_secure_esp8266/adafruitio_secure_esp8266.ino +++ b/examples/adafruitio_secure_esp8266/adafruitio_secure_esp8266.ino @@ -44,7 +44,12 @@ WiFiClientSecure client; Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); // io.adafruit.com SHA1 fingerprint -static const char *fingerprint PROGMEM = "59 3C 48 0A B1 8B 39 4E 0D 58 50 47 9A 13 55 60 CC A0 1D AF"; +/* WARNING - This value was last updated on 08/15/22 and may not be up-to-date! +* If security is a concern for your project, we strongly recommend users impacted by this moving +* to ESP32 which has certificate verification by storing root certs and having a +* chain-of-trust rather than doing individual certificate fingerprints. +*/ +static const char *fingerprint PROGMEM = "18 C0 C2 3D BE DD 81 37 73 40 E7 E4 36 61 CB 0A DF 96 AD 25"; /****************************** Feeds ***************************************/ diff --git a/examples/mqtt_retain/.due.test.skip b/examples/mqtt_retain/.due.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/examples/mqtt_retain/.leonardo.test.skip b/examples/mqtt_retain/.leonardo.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/examples/mqtt_retain/.uno.test.skip b/examples/mqtt_retain/.uno.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/examples/mqtt_retain/.zero.test.skip b/examples/mqtt_retain/.zero.test.skip new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/mqtt_retain/.zero.test.skip @@ -0,0 +1 @@ + diff --git a/examples/mqtt_retain/mqtt_retain.ino b/examples/mqtt_retain/mqtt_retain.ino new file mode 100644 index 0000000..41197cc --- /dev/null +++ b/examples/mqtt_retain/mqtt_retain.ino @@ -0,0 +1,140 @@ +/*************************************************** + Adafruit MQTT Library Retain Flag Example + + This example demonstrates use of the retain flag when publishing messages. + If retain is set, the MQTT broker will store the message. When a new + client subscribes to the topic, the retained message will be republished + to that client. This is useful for configuration messages and 'last known + good' values. + + Written by Ben Willmore. + MIT license, all text above must be included in any redistribution + ****************************************************/ + +#include // use for ESP32 +#include "Adafruit_MQTT.h" +#include "Adafruit_MQTT_Client.h" + +/************************* WiFi Access Point *********************************/ + +#define WLAN_SSID "...your SSID..." +#define WLAN_PASS "...your password..." + +/************************* Adafruit.io Setup *********************************/ + +#define MQTT_SERVER "...your MQTT server..." +#define MQTT_SERVERPORT 1883 // use 8883 for SSL +#define MQTT_USERNAME "MQTT username" +#define MQTT_KEY "MQTT key" +#define DEVICE_ID "mqtt-retain-example" + +/************ Global State (you don't need to change this!) ******************/ + +// Create a WiFiClient class to connect to the MQTT server. +WiFiClient client; +// or... use WiFiClientSecure for SSL +//WiFiClientSecure client; + +// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. +Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY); + +/****************************** Feeds ***************************************/ + +// Set up for publishing and subscribing to the same feed, for demonstration only +Adafruit_MQTT_Publish publish_feed = Adafruit_MQTT_Publish(&mqtt, DEVICE_ID "/temp"); +Adafruit_MQTT_Subscribe subscribe_feed = Adafruit_MQTT_Subscribe(&mqtt, DEVICE_ID "/temp"); + +/*************************** Sketch Code ************************************/ + +void setup() { + Serial.begin(115200); + delay(10); + + Serial.println(F("MQTT retain flag demo")); + + // Connect to WiFi access point. + Serial.println(); Serial.println(); + Serial.print("Connecting to "); + Serial.println(WLAN_SSID); + + WiFi.begin(WLAN_SSID, WLAN_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + // Connect to MQTT broker, then publish a retained message and a + // non-retained message. + Serial.print("\nConnecting to MQTT broker..."); + MQTT_connect(); + Serial.println("connected"); + + Serial.println("Publishing messages while not subscribed"); + publish_feed.publish("This message should be retained", true); + publish_feed.publish("This message should not be retained"); + + Serial.println("Disconnecting from MQTT broker\n"); + mqtt.disconnect(); + + subscribe_feed.setCallback(subscribe_callback); + mqtt.subscribe(&subscribe_feed); +} + +void subscribe_callback(char *data, uint16_t len) { + Serial.print("--> Message received: \""); + Serial.print(data); + Serial.println("\"\n"); +} + +void loop() { + + // Connect to MQTT broker. We should receive the retained message only. + Serial.println("Connecting to broker. Expect to receive retained message:"); + MQTT_connect(); + + mqtt.processPackets(1000); + + Serial.println("Publishing non-retained message. Expect to receive it immediately:"); + publish_feed.publish("This message should be received immediately but not retained"); + + mqtt.processPackets(1000); + + Serial.println("Publishing retained message. Expect to receive it immediately and on re-subscribing:"); + publish_feed.publish("This message should be received immediately AND retained", true); + + mqtt.processPackets(10000); + + Serial.println("Disconnecting from broker\n"); + mqtt.disconnect(); + + delay(15000); +} + +// Function to connect and reconnect as necessary to the MQTT server. +// Should be called in the loop function and it will take care if connecting. +void MQTT_connect() { + int8_t ret; + + // Stop if already connected. + if (mqtt.connected()) { + return; + } + + uint8_t retries = 3; + while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected + Serial.println(mqtt.connectErrorString(ret)); + Serial.println("Retrying MQTT connection in 5 seconds..."); + mqtt.disconnect(); + delay(5000); // wait 5 seconds + retries--; + if (retries == 0) { + // basically die and wait for WDT to reset me + while (1); + } + } +} diff --git a/library.properties b/library.properties index 957bb7e..024fb08 100644 --- a/library.properties +++ b/library.properties @@ -1,8 +1,8 @@ name=Adafruit MQTT Library -version=2.1.0 +version=2.5.4 author=Adafruit maintainer=Adafruit -sentence=MQTT library that supports the FONA, ESP8266, Yun, and generic Arduino Client hardware. +sentence=MQTT library that supports the FONA, ESP8266, ESP32, Yun, and generic Arduino Client hardware. paragraph=Simple MQTT library that supports the bare minimum to publish and subscribe to topics. category=Communication url=https://github.com/adafruit/Adafruit_MQTT_Library