Problems with Mosquitto and last will (testament)

Goblinch picture Goblinch · Feb 19, 2015 · Viewed 9k times · Source

I'm using Mosquitto and the Python implementation of Paho to try to communicate a couple of programmes. I'm getting some troubles when I use the last will function. My code is this:

Suscriber:

import paho.mqtt.client as mqtt
def on_message(client, userdata, msg):
    print 'Received: ' + msg.payload

client = mqtt.Client()
client.on_message = on_message

client.connect('localhost', 1883)
client.subscribe('hello/#')

client.loop_forever()

Publisher:

import paho.mqtt.client as mqtt

client = mqtt.Client()

client.will_set('hello/will', 'Last will', 0, False)
client.connect('localhost', 1883)

client.publish('hello/world', 'Regular msg', 0, False)
client.disconnect()

The output:

Received: Last will

I should receive only the regular message because I use client.disconnect() to close the connection. If I comment the will_set line, I get the regular message. I also tried publishing both on the same topic and it doesn't work.

Answer

Jan Vlcinsky picture Jan Vlcinsky · Jan 10, 2016

How to set MQTT last will message properly

The Last will and testament feature requires to follow some rules.

Call client.will_set before you connect

Put call to will_set before client.connect.

client = mqtt.Client()
client.will_set("stack/clientstatus", "LOST_CONNECTION", 0, False)
client.connect(host, port, timeout)

Calling will_set after connect did not work for me.

Only last client.will_set counts

Trying to use will_set multiple times is possible, but only the last one will be effective, previous ones will be ignored.

Let client.disconnect to complete

The client.disconnect is actually request to send out the "DISCONNECT" message to the broker.

As the messages are sent asynchronously, it is possible, that your program ends sooner, then is the "DISCONNECT" message completed.

Use client.loop_*() after client.disconnect This shall allow the client to complete the process of sending the "DISCONNECT" message out.

Following combinations worked for me:

  • loop_start() -> loop_stop()
  • loop_start() -> loop_forever() (it completes after sending DISCONNECT message)

I did not experiment with other combinations.

From those option, the first one seems better to me (keeping loop_start paired with loop_stop). The second option might be simply wrong.

My working code:

import paho.mqtt.client as mqtt

host = "test.mosquitto.org"
port = 1883
timeout = 10

client = mqtt.Client()
client.will_set("stack/clientstatus", "LOST_CONNECTION", 0, False)
client.connect(host, port, timeout)

client.publish("stack/clientstatus", "ON-LINE")

client.loop_start()
for msg in ["msg1", "msg2", "msg3"]:
    client.publish("stack/message", msg)
    print msg

client.publish("stack/clientstatus", "OFF-LINE")
client.disconnect()
client.loop_stop()  # waits, until DISCONNECT message is sent out
print "Now I am done."

Not reliable methods (sleep, adding on_disconnect)

Adding time.sleep might help, but in other situation (not reliable connection) could fail.

Adding client.on_disconnect = on_disconnect did not help in my case as it seems, this callback is processed asynchronously and does not block the client.disconnect call.