Callbacks are functions that are called in response to an event.
The events and callbacks for the Paho MQTT client are as follows:
- Event Connection acknowledged Triggers the on_connect callback
- Event Disconnection acknowledged Triggers the on_disconnect callback
- Event Subscription acknowledged Triggers the on_subscribe callback
- Event Un-subscription acknowledged Triggers the on_unsubscribe callback
- Event Publish acknowledged Triggers the on_publish callback
- Event Message Received Triggers the on_message callback
- Event Log information available Triggers the on_log callback
The Paho client is designed to only use the callback functions if they exist, and doesn’t provide any default callback functions.
However; it does provide a mechanism to set them.
To use a callback you need to do two things
- Create the callback function
- Assign the function to the callback.
Callback Function Example
As an example we will use the on_connect callback.
First I create my function. I’ve called it on_connect but I could have called it anything I wanted.
Here is the function.
def on_connect(client, userdata, flags, rc): logging.info("Connected flags"+str(flags)+"result code "\ +str(rc)+"client1_id ") client.connected_flag=True
The function simply logs a message, and sets a flag.
Now to associate my function with the On_connect callback I use the following:
Note: If my function had been called myfunction then I would use the following:
Callbacks and the Client Loop
Callbacks are dependent on the client loop as without the loop the callbacks aren’t triggered.
Below is the code of a simple python script that creates a connection and then waits in a loop for the on_connect callback to be triggered.
The on_connect callback then sets the flag that terminates the loop and the script ends.
This means that if the callback isn’t processed then the while loop never ends as we shall see later.
Here is the output generated by the script.
Notice how the loop executes waiting for the callback and stops once the callback gets processed.
So let’s run the above script again, but this time I won’t start the loop. To do that I simply comment out the client.start_loop() line.
Here is what happens when I run the script.
You can see that I had to manually terminate the script as it never processed the callback, and the callback is what terminates the while loop.
How Callbacks Work
If you dig into the Client code you will find the code for the Connect acknowledge message.
When the client receives a CONNACK message the callback is triggered if it exists.
Here is the relevant section of the code. (around line 276)
Notice the if on_connect statement. This is what checks for the callback, and if it exists it calls the callback- self.on_connect().
The self.on_connect function is the function we assigned earlier using.
You should see from the code that the function call passes several arguments, and so the function we create needs to be able to handle those arguments.
If you look back at the function I created you can see that it handles 4 arguments. The documentation has a description of those arguments
Asynchronous or Synchronous?
If you use the loop_start() or loop_forever functions then the loop runs in a separate thread, and it is the loop that processes the incoming and outgoing messages.
In this case the callbacks can occur any time in your script and are asynchronous.
However if you call the loop() function manually in the script then the callbacks are synchronous with your script as they can only occur when you call the loop() function.
Video- The Loop and Callbacks.
Returning Values from Callbacks
Due to the way they operate It is not possible to return values from a callback using the standard return command.
Therefore if you need to get status information from a callback you need to use some form of global variable in the callback.
If you refer back to the on_connect call back example I used the connected_flag which is a property of the client object.
Because the client object is available throughout the script it can be accessed anywhere in the script.
In my scripts my general approach is extend the main mqtt client class with flags and other variable I need.
When I create client object it has these flags already assigned.
This is what it looks like:
import paho.mqtt.client as mqtt #import mqqt client ### extend main class to include flags etc mqtt.Client.bad_connection_flag=False mqtt.Client.connected_flag=False mqtt.Client.disconnect_flag=False mqtt.Client.disconnect_time=0.0 mqtt.Client.pub_msg_count=0 ## client=mqtt.Client("myclient") #create client object ### client.client.connected_flag=True #set flag to true somewhere in script #####
Common Questions and Answers
Q- My callback is not being triggered?
A- Have you assigned the function to the Callback? Are you running a loop for that client?
Q-My callback returns a value but I don’t see it?
A- Because of the asynchronous nature of callbacks it doesn’t make sense to return a value from them.
Related Articles and Resources:
- Working with Client Connections- Python MQTT
- Using the Paho MQTT Python Client for Beginners
- MQTT Subscribe-Python MQTT Client Examples
- MQTT Publish-Python MQTT Client Examples
- Client Objects-Working with The Python MQTT Client
- Understanding The loop and Loop Functions