Simple Python MQTT Publish and Subscribe Example Script

This is a very simple example script to publish to a topic, and then receive the published message.

To do that we will need to first subscribe to the topic and then publish messages to the same topic.



We will publish and subscribe using the same client.

The Code


import time
import paho.mqtt.client as paho
broker="broker.hivemq.com"
broker="iot.eclipse.org"
#define callback
def on_message(client, userdata, message):
    time.sleep(1)
    print("received message =",str(message.payload.decode("utf-8")))

client= paho.Client("client-001") #create client object client1.on_publish = on_publish #assign function to callback client1.connect(broker,port) #establish connection client1.publish("house/bulb1","on")
######Bind function to callback
client.on_message=on_message
#####
print("connecting to broker ",broker)
client.connect(broker)#connect
client.loop_start() #start loop to process received messages
print("subscribing ")
client.subscribe("house/bulb1")#subscribe
time.sleep(2)
print("publishing ")
client.publish("house/bulb1","on")#publish
time.sleep(4)
client.disconnect() #disconnect
client.loop_stop() #stop loop

Note: You can copy the code direct from the page, paste it in a file and use.

When we run the script we get

pub-sub-code-output

Code Explanation and Notes

I use the time.sleep() function to insert delays so as to give the client time to connect etc.

We publish and subscribe using the same client.

The client.loop() is important otherwise the callbacks aren’t triggered.

The on message callback function catches the callback and the client.on_message=on_message binds the function to the callback.

There is no error checking i.e. I don’t check for a successful connection before publishing, and subscribing.

I have two free external brokers listed as sometimes the brokers are down and you will need to use the other one.

Just comment out the one you aren’t using.

I’ve used the topic house/bulb1 you can use any topic name you want.

Questions and Things to Try

  1. What would happen if I published before I subscribed?
  2. What would happen if I didn’t start the loop?
  3. What would happen if I didn’t bind the on_message function to the callback?

Answers below

Useful Tutorials and Resources:

Answers

  1. The message would be discarded by the broker.
  2. You wouldn’t see the message.
  3. You wouldn’t see the message.

Save

Save

Save

Save

Save

Save

Please rate? And use Comments to let me know more
[Total: 6    Average: 3.7/5]

39 comments

  1. Hi Steve!
    I have a question about the disconnect/loop_stop functions. I want to publish a message every 30 seconds (a reading from an IoT device). I’m planning to set client.connected_flag=True in the on_connect function and then do the following after connecting to the broker:

    client.loop_start()
    while client.connected_flag:
    current_device_reading = get_device_read()
    client.publish(my_topic, current_device_reading)
    time.sleep(30)

    My question is do I still need to call client.loop_stop() or client.disconnect() somewhere? If the broker goes offline for some reason, is this when disconnect/loop_stop need to be invoked?

    1. Sam
      By starting a loop the client will handle reconnects for you I would change the code to
      client.loop_start()
      while True: #loop forever
      while client.connected_flag:
      current_device_reading = get_device_read()
      client.publish(my_topic, current_device_reading)
      time.sleep(30)
      If you get disconnected it wont try tp publish until it reconnects
      client.disconnect()
      Loop_stop()
      is at very end of script
      Rgds
      Steve

      1. Thanks! But in that case doesn’t the “while True” loop prevent the “client.disconnect()” and “client.loop_stop()” from ever being called?

        1. yes it does.
          the alternative is
          while flag
          code

          inside the while loop set the flag False on some condition.
          rgds
          Steve

  2. Can a Python script be both a publisher and a subscriber e.g. subscribe to one topic, publish to another? I have multiple RPi devices, one as a web server and mqtt borker, others as sensors/controllers. As I issue commands to the sensors/controllers, I presently have 1 script for publish and 1 script for receive on both sides (device and server) to enable bi-directional com. It is making maintenance/upgrades difficult, and I’m just wondering if there is a better way.

    Thanks!
    Reagan

    1. Yes It can be. It can also connect to multiple brokers at the same time. If you want an example script use the ask-steve page to let me know.
      Rgds
      Steve

  3. Is it possible to publish objects and not just string messages? The object of course could have a bunch of properties (strings, integers, etc.). Will the broker accept objects?

  4. What if instead of 2 MQTT brokers that you are using in this example I’d like to use the mosquitto broker that runs locally on my Raspi ? I am newbie to Python although have decades of C/C++/C# under my belt (this is the 2nd time I see Python script in my life).

    1. Yes you can use a single broker, As far as I remember the script does. It may use two clients but you can also use a single client.

  5. How do I publish and subscribe through different clients?
    I do not have raspberry Pi and only using PAho-mqtt python package.
    Thanks

    1. You can use the example script and run it twice. One would publish on topic one and the other would subscribe on topic1.
      Does that make sense?
      rgds
      Steve

  6. Hey, I love this tutorial but I stumbled across this tutorial because I am in the learning process and want to succeed in making a Python client that’ll receive messages on a subscribed topic for 30 mins for various continuous messages and then package it all into one file maybe .txt, .json or something.
    I’ll love it if you provide me with a direction where to go next in this learning process.

  7. Hi

    I want to save my data from the subscribed topic on to a text file. Here’s what I added to my code:

    “client.on_message= on_message
    f = open(‘/home/pi/test.txt’,’w’)
    f.write(‘on_message’)
    f.close() ”

    However the text file test.txt doesnt save the desired rather it just saves the word “on_message”

    Can anybody please help?

    Thanks.

    1. the on_message is a function
      You need to declare and on message function like below:
      def on_message(client, userdata, message):
      msg=msg.decode(“utf-8”) #get the message
      f = open(‘/home/pi/test.txt’,’w’)
      f.write(msg)
      f.close()

  8. Hello Steve ,

    Your explanation is very helpful. Is that possible in python to store the real time publish data. I am getting the data as a message , which includes integer , character as well as real number. Is there any way to store or save that published data. I want to use this data for training my deep learning model. Thanking you .

    Hitesh

  9. Hi Steve,
    Wondering if you tried to log a MQTT message to a SQL database (MySQL for instance)? I’m working on a small python script that keeps status of a MQTT message before logging data into MySQL database. The very odd thing is that the MySQL piece of code block never get executed (within a try … except trap). Any experience in that direction?
    Thanks,
    Yanick

  10. Hi, I tried your script with a broker (an other pi) on my lan and I get the result expected. Therefore, for me is still missing a piece of the story (I admit, non only from you tutorial!): how can I publish from a client and read from an other client? How can I keep running two pi’s and transmit infos with mqtt from one pi to the other? Should I launch a script like this one for the publisher and a second script for the subscriber? The web if full of examples using third party apps for the cloud but I would leave all my stuff on the lan.
    Thanks

    1. To get a publish script and subscribe script then just copy the pub-sub script.
      You then need to change the client name as each client needs to use its own name.
      You can then remove the subscribe code from the publish script and the publish code from the subscribe script.

      To move info between two pi’s then you need to publish from pi 1 and subscribe using pi 2 and vice versa. This you can do using the original pub-sub script.you will need to assign topic names e.g
      pi 1 would publish on topic pi-1 and pi 2 would subscribe to topic pi-1 and vice versa.
      The broker could be located on one of the pi’s or in the cloud.
      Does it make sense?

      1. Hi Steve, thank you for the suggestion. I created two separate scripts, one for the publisher (also broker) and one on a second machine for the subscriber. Both scripts have a while cicle. I launch together and I get message appearing on subscriber at regular intervals.
        Thank you again

  11. Hi Steve,
    Thanks for nice explanation,I want to explore on MQTT from scratch .
    Could you please suggest any link/documentation with few hands on examples.
    Thanks in advance!!!

  12. I get this error please suggest solution for this

    Traceback (most recent call last):
    File “/home/pi/RPi/mqtt1.py”, line 29, in
    client.loop_forever()
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 1481, in loop_forever
    rc = self.loop(timeout, max_packets)
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 1003, in loop
    rc = self.loop_read(max_packets)
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 1284, in loop_read
    rc = self._packet_read()
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 1849, in _packet_read
    rc = self._packet_handle()
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 2311, in _packet_handle
    return self._handle_connack()
    File “/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py”, line 2372, in _handle_connack
    self.on_connect(self, self._userdata, flags_dict, result)
    File “/home/pi/RPi/mqtt1.py”, line 12, in on_connect
    client.subscribe(“/esp/pot”,0) # remember this topic to put inside ESP code later
    AttributeError: ‘NoneType’ object has no attribute ‘subscribe’

    1. my script file running in raspberry pi

      # Main template for our paho.mqtt.client code
      # lets obtain potentiometer reading from ESP8266
      # listen to topic “/esp/pot”
      import paho.mqtt.client as mqtt

      # The callback for when the client receives a CONNACK response from the server.
      def on_connect(self,client, userdata, rc):
      print(“Connected with result code “+str(rc))
      # Subscribing in on_connect() means that if we lose the connection and
      # reconnect then subscriptions will be renewed.
      client.subscribe(“/esp/pot”) # remember this topic to put inside ESP code later

      # The callback for when a PUBLISH message is received from the server.
      def on_message(client, userdata, msg):
      print(“on msg”)
      print(msg.topic+” “+str(msg.payload))

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

      client.connect(“localhost”, 1883) # localhost is the Raspberry Pi itself

      # Blocking call that processes network traffic, dispatches callbacks and
      # handles reconnecting.
      # Other loop*() functions are available that give a threaded interface and a
      # manual interface.
      client.loop_forever()

  13. Thank you for the explanation on how to use MQTT under Python. I have managed to successfully write a script to control my house heating using a Raspberry Pi with Mosquitto and Python3. It is running flawlessly as far as I can tell.

    I have tried to make it start at boot in case the Pi goes down and starts when power is restored but I can’t get it to work when called via a cron job. It seems it can’t find the client and publish libraries presumably because it is being run by root at this stage.

    Is it possible to give the full path to the import function and, what is more to the point, what would that path be?

    1. John
      I’ve haven’t done what you are trying but I would try running the cron job as your normal user I found these instructions.
      https://stackoverflow.com/questions/8475694/how-to-specify-in-crontab-by-what-user-to-run-script

      On my Linux box the mqtt client is under
      /home/steve/.local/lib/site-packages
      On some of my scripts I use my own classes which I don’t put in site packages the command at the top of the python script to set the path is this.(windows system)
      sys.path.append(“c:/python34/steve/network/”)
      Hope that helps.
      Rgds
      Steve

      1. Hi Steve,

        Many thanks for your reply. I really thought that you had cracked it when you suggested using cron as a local user but, unfortunately, not.

        I also tried adding the two lines:
        import sys
        sys.path.append(“/home/pi/.local/lib/python3.5/site-packages/paho/mqtt”)
        at the top of my script (having checked with FileZilla that that was where the packages where kept) but again, no luck.

        It’s interesting that Python did not balk at the’ import sys’ so it must be something to do with Python not being able to find the Paho library. It says, “No module named ‘paho'”

        Anyway, I shall have to shelve this for now; it has taken up far too much of my time (and yours). The program works just fine; I just have to start it manually in the event of a system reboot.

        Thanks again,

        John.

        1. John
          Try import the paho client using the full path in a normal script (not cron) or a python shell. That way you can be sure it is were you think it is.
          If it is finding SYS Ok then it is a path problem.

          1. Once again, so close but not quite there.

            I added the full path but Python balked at the forward slashes. Then it dawned on me that the path uses dots and not forward slashes. BUT the .local directory has a dot in the name and I haven’t figured out how to escape it in that situation.

            Despite the fact that I said I was going to shelve the problem, I did try putting ‘sleep 100’ in the shell file so that the system had time to settle before attempting to import any Paho libraries but again, no joy.

            Thanks for your help. If I ever sort it, I shall get back to you and let you know what the solution is.

            John.

          2. I finally got back to this problem and I have now found a solution.
            I created a script file called ‘ch_boot.sh’ in my python directory. It contains 2 lines:
            sleep 10
            python3 /home/pi/jb_python/Heating_RPi.py &
            In my cron file, opened using crontab -e (not sudo), I added the following:
            @reboot /home/pi/jb_python/ch_boot.sh
            And it works!
            I think that allowing the 10 seconds before starting the script allows Raspbian time to set up set up MQTT properly and load the references to the Paho library.
            Anyway I thought you would be interested in the solution and many thanks for you help.

Leave a Reply

Your email address will not be published. Required fields are marked *