Adafruit_MQTT_Library
Adafruit_MQTT_Library copied to clipboard
Support Retain for Publish
One useful pattern to track device up/down status is to have a will set to .../status with value '0' and retain, and when the device comes up to publish to the .../status with value '1' with retain. The retain allows the status to be interrogated at anytime by a client. The library has retain flag for will, but not for publish. Of course, it would be useful for lots of other things too, but thought I would point out one very valuable one...
+1 for retain support
if you are using this library with adafruit IO, your last published value should be retained by default on the server. we will try to get this added to the library as well.
Possibly related to https://forums.adafruit.com/viewtopic.php?f=56&t=88407 where I asked for functionality so that "the ESP8266 would not have to wait until a feed is updated; instead it would ask adafruit.io "give me the last value of feed x".
The answer given by @jwcooper was:
I believe this should be the default scenario. You should get an initial value as you subscribe to a feed. We store the last values sent (once you have sent an initial value, of course) so that you should immediately receive that value.
However, it is unclear how to do this and this ticket, especially the answer by @toddtreece makes me wonder whether it is implemented at all.
a lot has changed in the last few weeks - ill ask todd again, if he says it can be done, ill add it :)
@toddtreece answer makes me think there maybe misunderstanding of the usage of the retain bit. The retain bit is not telling the server to keep or not keep the data (it is free to keep as much as it likes), but is telling the server how to handle new subscribers. So, when you subscribe to a topic, if the publisher had set retain, the subscriber immediately gets the last published value (regardless of age). If the publisher did not set retain, a subscriber will only get a message when the publisher publishes a new value. This is a very important distinction, as it lets the publisher determine if the data is good when it is stale. For something like "device.up" (with a retained will value of say "0", and publish of "1" on boot) retain is crucial. For something motion detection, it would be problematic, you likely only want to know a bout motion as it happens, not if it happened some time in the past. Unfortunately MQTT won't give you the age of a message, so sometimes you'll want a retained value (say temperature), but also want to use a "up" topic to make sure the device is still publishing and it isn't days old or something.
In any case, retain is pretty crucial for sensor use in a general purpose broker.
@PaulKierstead there is no misunderstanding. we chose to force the retain flag on by default so that people have an easier time getting access to the last value. you bring up a valid point about the stale values, and there are currently ways to check staleness via MQTT.
Adafruit IO processes each message, saves a new record in Postgres, and then sends out new copies of the saved message on multiple MQTT topics with the retain bit flipped (including the full record in JSON format with timestamp at {username}/feeds/{id}/json).
Adafruit IO is independent of this library, and the discussion about IO's behavior with regard to retain can be continued in the io-issues repo. with my earlier comment I was saying is that retain support would have to be added to this library because IO's forced retained behavior is not standard behavior for general purpose MQTT brokers.
Even if Adafruit IO sets the retain flag by default, it doesn't make sense for the retain flag to be a per-connection setting lumped together with will() in this library. Typically this is implemented as a per-feed setting in the publish() call. Arguably, it could also be a subscribe() option, which makes sense given the default IO behavior. As implemented now, even though retain is on by default in IO, clients that connect through this library don't get the last value unless they call will(), which is not intuitively the same setting.
@igg2 retain is not set per connection in this library. i think what you might be referring to is the retain flag for the MQTT will feature, which is part of the connect packet and is unrelated to this issue: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/csprd02/mqtt-v3.1.1-csprd02.html#_Toc385349234
the retain feature for publish is not implemented in this library yet, and MQTT does not support setting a retain flag for subscriptions. there might be a separate bug that is causing the forced retained messages from IO to be missed by this library when connecting. we will check into that.
I see, so the retain message in will() refers only to the will message. That makes more sense. Since Adafruit IO has retain on by default for all messages, we should get the last known value for every feed we subscribe to when we connect - is that correct? That's definitely not happening with the latest release from GitHub. I will switch to the master branch and try with that. I know I can work around by getting the last known value from the REST API, but I'd rather keep it all in MQTT if possible.
ok just pushed to the library, if you subscribe to a topic, adafruit.io will send you the retained message the first time you call readSubscription however, right now it only works for a single sub. for more subs what we really have to do is use callbacks so the packet isnt 'eaten' anyways, it is commited - plz try!
So that totally works, except that now if you do have more than one subscription, it fails in mqtt.connect() with "Failed to subscribe". That seems kind of harsh, since now you're effectively limited to one subscription. Having one of them get a retained update at random when first connecting seems better. Better still would be a callback. But wouldn't it be simpler to just have a readRetained() method to return an array of subscription pointers, and leave everything else as it was for now? It only makes sense to get retained values once between connect() and readSubscription() anyway.
@igg2 do you have an example sketch where Retain can be seen in action? Do I have to modify the esp8266 example sketch for it to use Retain?
You don't have to modify the example sketch (mqtt_esp8266) for it to use retain, it'll just do it, assuming you're using the current master branch (with commit 79dd624). I don't think there's a way to not use retain at this point. Just keep hitting the reset button, and you'll see that it always gets the last value when it first calls readSubscription().
Thanks @ladyada works for me.
the real trick is to have it work for multiple subs. i can do it but requires a refactor to also do callback support (e.g. turn readpacket into readandprocesspacket). its on the list to do!
OK just fixed the multiple subscription bug in https://github.com/adafruit/Adafruit_MQTT_Library/commit/8f595cdb955adb561c5d1cafcdf737ec5e68d88c getting closer to callback support, i needed to have packet processing be able to handle out-of-order packets so if a subscription comes in, it'll do the callback right!
diff --git a/Adafruit_MQTT.cpp b/Adafruit_MQTT.cpp
index c4ff4e2..0f1d49c 100644
--- a/Adafruit_MQTT.cpp
+++ b/Adafruit_MQTT.cpp
@@ -583,8 +583,9 @@ uint8_t Adafruit_MQTT::publishPacket(uint8_t *packet, const char *topic,
uint8_t *data, uint8_t bLen, uint8_t qos) {
uint8_t *p = packet;
uint16_t len;
+ uint8_t retain = 0x1;
- p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1;
+ p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1 | retain << 0;
// fill in packet[1] last
p+=2;
Above patch will always publish with retain enabled. Could be extended to a parameter but I don't have time to make it neat.
Tested with mosquitto on debian.
Source: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718037
being able to turn on retain would be helpful for my homeassistant.io install. I have a handful of HUZZAH and Feather boards using this library to send data to AIO and mosquitto so this would be helpful. Any idea what the priority of getting this added is? Might just have to use DirtyJerz patch.
+1 for proper MQTT retain support. This is essential for various classes of use.
+1 for proper MQTT retain support
MQTT retain support is totally essential .... +1 Actualy I have seen some forks resolving the issue, does someeone knows why there aren't an official response?
Hope no one minds but I modified the code to support "retain" on publish as optional param. Adafruit_MQTT.h and Adafruit_MQTT.cpp have changes. Replace them with the files in this zip. Enjoy!
@lazarwulf Ey! Thanks! What a great news! I tried it but probably because I didn't use it properly it give me a lot of errors. What I tried to do was something.publish("value") change it to something.publish("value", true) but that as I said that didn't work, so can you please help me to know how should I use it? Thanks again!
@lazarwulf thanks for attaching the .zip. If you would like to have this included in the official version, it would be very kind of you if you could send a GitHub Pull Request. This will make it easier for adafruit to include your changes. Thanks.
@lazarwulf would you mind posting some examples how to use your modification?
I've been trying to get the value when I subscribe to a feed, and everything I have read has said the retain bit is on by default, but when I subscribe to a feed the debug output shows that the feed is empty and I don't receive the last value.
I'm updating the feed through IFTTT if that helps clear up anything
Same problem for me, it seems that the retain flag do'nt works as said !
I want to create a remote control using MQTT (Adafruit). The command is issued with a smartphone.
The remote harware is based on ESP8266 working on batteries. In order to save the power and make the batteries last long time, the ESP8266 is in Deepsleep mode most of the time. For example 5 seconds wake-up and 2 minutes in deepsleep.
I created a feed named "actionne les volets", In the code of ESP, I subscribe to the MQTT using :
Adafruit_MQTT_Subscribe monte_descend = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/actionne les volets");
and after the end of deepsleep (ie after reset) I check the incoming command with
while ((subscription = mqtt.readSubscription(5000))) { if (subscription == &monte_descend) { Serial.print(F("Got: ")); Serial.println((char *)monte_descend.lastread);}}
It works when the command is issued from my smartphone within the wake-up time (ie. 5 secondes) but I cannot retrieve the lastest command if the command is issued during the deepsleep time (before wake-up)
The question is : how to get the latest command issued by the smartphone ? (could be anytime during wake-up or not)
with the retain flag, I should get the latest command anytime . please give me some ideas, thanks
I know it has been a year since the last person posted there issue, but I am running into the same problem as Yodrack and others where I am only able to read the value if it is published while the readSubscription is running. I have tried turning off history while testing with no success. I am using the adafruit mqtt client library publishing and subscribing into the same 2 feeds, then having my esp32 go into deep sleep and wake on either preset timer or touch wake up. Any help or feedback will be Greatly appreciated.
This library is really good but I got a problem in my project, I need to get last value of some feed after my Wemos D1 rebooting.
the solution is the following : create the following feeds : (adapt with the "name" you want) 1/ Adafruit_MQTT_Subscribe FEED_name = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/name");
2/ Adafruit_MQTT_Publish FEED_name_get = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/name/get");
void setup(){ connect_wifi(); delay (100); mqtt.subscribe(&FEED_name); MQTT_connect(); FEED_name_get.publish(0); delay(50); .......
void loop(){
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(3000))) {
if (subscription == &FEED_name) {
if (strcmp((char *)FEED_name.lastread, "Fixe") == 0 ) {
.............
..............
this works and you will get the latest value of the page after a restart from deepsleep
good luck