Ahmed Al-haddad
Ahmed Al-haddad

Reputation: 833

How to write a function that awaits for a message from MQTT before continuing the program?

I am trying to send a message using publish.single and then receive it and act upon the data received. Hence, I can't proceed unless I receive something from the server, so is there a way to write a statement that will wait for a message from MQTT before proceeding?

Example code:

publish.single("$topic", data, ip_address)
#can't do anything here 
receive(data_from_broker) #or anythin that looks like it!
#continue with the program here 

Upvotes: 0

Views: 5677

Answers (2)

F.M.F.
F.M.F.

Reputation: 2292

As hardillb noted, this is not the right approach for working with MQTT. If you still want to do it, you can write a function that blocks the main thread until a message arrives.

For example as follows (copy & paste solution):

import ssl
import paho.mqtt.client as mqtt
import time

def mqtt_get_value_blocking(broker, port, username, password, topic):
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            client.subscribe(topic)
        else:
            print(f"Connection failed with error code {rc}")

    def on_message(client, userdata, msg):
        nonlocal val
        val = msg.payload.decode()

    val = None
    client = mqtt.Client()
    client.username_pw_set(username, password)
    client.on_connect = on_connect
    client.on_message = on_message
    client.tls_set(certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED)
    client.connect(broker, port=port)
    client.loop_start()

    while val is None:
        time.sleep(0.1)

    client.loop_stop()
    client.disconnect()

    return val

Call the function as follows:

# TODO: adjust the following broker details
BROKER = "<mqtt broker address>"
PORT = 8883 
USERNAME = "<mqtt username>"
PASSWORD = "<mqtt password>"

topic = "test/test"
val = mqtt_get_value_blocking(BROKER, PORT, USERNAME, PASSWORD, topic)
print(val)

Note: This function assumes that you are not using SSL. If you want to or must use SSL, adapt the line client.tls_set(certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED) accordingly.

Upvotes: 0

hardillb
hardillb

Reputation: 59608

The short answer is you don't.

At least not in the way you describe. MQTT is an asynchronous protocol, there is no sense of sending a message and waiting for a response, a publishing client has no way to know if the is 0, 1 or n subscribing clients listening on the topic the message is published.

You will need to build something called a state machine to keep track of where in the program flow you are when messages are received.

e.g.

  1. Application published message and sets flag in the state machine to say the message was sent
  2. Remote client receives message and publishes a response
  3. New message is received by the first client, it checks the state machine to determine that the message should be treated as a response to the original message.

To subscribe to a topic you will have to move on from using publish.single to the full MQTT client so you can setup the onMessage callback to handle the incoming messages.

Upvotes: 3

Related Questions