Basically, I want my userdata to act as a global flag that gets read and updated between successive executions of the on_message() callback. I wrote and ran the following script:
import paho.mqtt.client as mqtt
MQTT_SERVER = "broker.hivemq.com"
MQTT_TOPIC = "IvanHu"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(MQTT_TOPIC)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(userdata, end = '')
print(" message received.")
userdata += 1
user_data_set(client_userdata)
client_userdata = 1
client = mqtt.Client(userdata=client_userdata)
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_SERVER, 1883, 60)
client.loop_forever()
I then published to the topic IvanHu 4 times. I expect the output of my python paho script to be the following:
Connected with result code 0
1 message received.
2 message received.
3 message received.
4 message received.
However, this was what I got instead:
Connected with result code 0
1 message received.
1 message received.
1 message received.
1 message received.
Evidently, the variable client_userdata never got updated in successive on_message() calls. What is the correct way to update this variable?
You have to give a initial value for userdata by client.user_data_set before connection, then you can update the userdata in on_message callback.
import paho.mqtt.client as mqtt
MQTT_SERVER = "broker.hivemq.com"
MQTT_TOPIC = "IvanHu"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(MQTT_TOPIC)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(userdata, end = '')
print(" message received.")
user_data_set(userdata + 1) # <-- Update userdata
client_userdata = 1
client = mqtt.Client(userdata=client_userdata)
client.on_connect = on_connect
client.on_message = on_message
client.user_data_set(1) # <-- Initialize
client.connect(MQTT_SERVER, 1883, 60)
client.loop_forever()
I couldn't figure out a way to satisfy my requirement using the userdata approach, but I was able to do so with the global variables approach. Here's the working script:
import paho.mqtt.client as mqtt
MQTT_SERVER = "broker.hivemq.com"
MQTT_TOPIC = "IvanHu"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(MQTT_TOPIC)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
global client_userdata
print(client_userdata, end = '')
print(" message received.")
client_userdata += 1
client_userdata = 1
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_SERVER, 1883, 60)
client.loop_forever()
Output:
Connected with result code 0
1 message received.
2 message received.
3 message received.
4 message received.
You seem to have missed the bit in the docs that talks about updating the user_data object
It may be updated at a later point with the user_data_set() function.
Taken from the docs here
This means you need to pass the new value to set_userdata() so it should be as follows:
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(userdata, end = '')
print(" message received.")
userdata += 1
client.user_data_set(userdata)
Related
I am experimenting with mqtt with python paho mqtt library and a mqtt client mobile app with the test.mosquito.org server/broker.
This basic script works below connecting to the test.mosquitto server where I can publish a message from a mobile mqtt client app to this script and this script can also publish to the mobile app every 20 seconds a test message via the def publish(client): function.
import random
import time
from paho.mqtt import client as mqtt_client
broker = 'test.mosquitto.org'
port = 1883
# generate client ID with pub prefix randomly
client_id = "test_1"
topic_to_publish = f"laptop/publish"
topic_to_listen = f"mobile/publish"
topic_to_wildcard = f"testing/*"
username = ""
password = ""
def connect_mqtt():
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.subscribe(topic_to_listen)
print(f"Connected to MQTT Broker on topic: {topic_to_wildcard}")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(client_id)
client.username_pw_set(username, password)
client.on_connect = on_connect
client.connect(broker, port)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
return client
def publish(client):
msg_count = 0
while True:
time.sleep(20)
msg = f"hello from {client_id}: {msg_count}"
result = client.publish(topic_to_publish, msg)
# result: [0, 1]
status = result[0]
if status == 0:
print(f"Send {msg} to topic {topic_to_publish}")
else:
print(f"Failed to send message to topic {topic_to_publish}")
msg_count += 1
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
print("Message received-> " + msg.topic + " " + str(msg.payload))
def run():
client = connect_mqtt()
client.loop_start()
publish(client)
if __name__ == '__main__':
run()
Can someone give me a tip on how to modify the def publish(client): function not to be a while loop that will fire off messages every 20 seconds but to only publish if the message from the mobile app received equals a string "zone temps"?
Am I on track at all removing the publish(client) from main run function as well as the while loop from def publish(client):? Thanks any tips greatly appreciated. What I am running into is I am missing something when I run this modified version there is no message exchange between at all.
def on_message(client, userdata, msg):
print("Message received-> " + msg.topic + " " + str(msg.payload))
if str(msg.payload) == "zone temps":
publish(client,"avg=72.1;min=66.4;max=78.8")
def run():
client = connect_mqtt()
client.loop_start()
if __name__ == '__main__':
run()
I m also beginner; but ill create a variable to is it publish or listening, like:
phoneAppListener = 0
and also
if str(msg.payload) == "zone temps":
when i print my payload it looks like:
b'payload'
firstly you need to split your payload like:
tempMsgHolder = str(msg.payload).split("'")
when you do this. tempMsgHolder[1] is your pure payload.
if tempMsgHolder[1] == "zone temps": phoneAppListener = 1
phoneAppListener value make the decision 0 is listen, 1 is publish. on your publish loop you set this
phoneAppListener == 1: publish your message
import random
import time
import threading
from paho.mqtt import client as mqtt_client
class moduleDatas:
broker = ('test.mosquitto.org')
port = (1883)
# generate client ID with pub prefix randomly
client_id = "test_1"
topic_to_publish = f"laptop/publish"
topic_to_listen = f"mobile/publish"
topic_to_wildcard = f"testing/*"
username = ""
password = ""
# Create clients object:
# You can create mqtt client obj using same pattern. Client has different on_msg or ex.
mqttClient_1 = mqtt_client.Client(moduleDatas.client_id) # You can create what ever you want to create a new thread
def mqttClientConnect():
mqttClient_1.connect(moduleDatas.broker[0], moduleDatas.port[0])
mqttClient_1.loop_start() # It creates daemon thread while your main thread running, this will handle your mqtt connection.
#mqttClient_1.connect_callback()
def on_connect(client, userdata, flags, rc):
if rc == 0:
print(f"Connected to MQTT Broker on topic: {moduleDatas.topic_to_wildcard}")
else:
print("Failed to connect, return code %d\n", rc)
#mqttClient_1.publish_callback()
def on_publish(client, userdata, mid):
print(mid) # If publish is success its return 1 || If mid = 1 publish success. || You can check your publish msg if it return failed try to send again or check your connection.
#mqttClient_1.message_callback()
def on_message(client, userdata, message):
temp_str = str(message.payload).split("'")
if temp_str[1] == "zone temps":
msg = "hello world" # <-- Your message here. Some func return or simple texts
mqttClient_1.publish(topic= moduleDatas.topic_to_publish, payload= msg, qos= 0)
def mqttClientSubscribe():
mqttClient_1.subscribe(moduleDatas.topic_to_listen)
def threadMqttClient1():
mqttClientConnect()
mqttClientSubscribe()
def buildThreads():
threads= []
t = threading.Thread(target=threadMqttClient1(), daemon= True)
threads.append(t)
# You can create on same pattern and append threads list.
for t in threads:
t.start()
while True: # this will your main thread, you can create an operation, ill go with just idling.
pass
if __name__ == "__main__":
buildThreads()
I'm trying to Globalize the msg.topic with test and then use it in showdata function. But when I called the test variable inside my showdata function, it show me "name 'test' is not defined"Does anyone have any idea on this issue? Thanks in advance.
Views.py
from django.shortcuts import render
from paho.mqtt import client as mqtt_client
import random
broker = 'localhost'
port = 1883
topic = "Wind"
# generate client ID with pub prefix randomly
client_id = f'python-mqtt-{random.randint(0, 100)}'
# username = 'emqx'
# password = 'public'
def connect_mqtt() -> mqtt_client:
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(client_id)
client.on_connect = on_connect
client.connect(broker, port)
return client
def subscribe(client: mqtt_client):
def on_message(client, userdata, msg, request):
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
global test
test = msg.topic
client.subscribe(topic)
client.on_message = on_message
def showdata(request):
context = test
return render(request, 'index.html', {'data': context})
def run():
client = connect_mqtt()
subscribe(client)
client.loop_forever()
if __name__ == '__main__':
run()
You have to declare the global variable in your upper scope like this:
broker = 'localhost'
port = 1883
topic = "Wind"
# generate client ID with pub prefix randomly
client_id = f'python-mqtt-{random.randint(0, 100)}'
# username = 'emqx'
# password = 'public'
test = None
Then you can use it in your function as expected.
I am using python Paho client.
I am using this in to my function.
my code is showing
import paho.mqtt.client as mqtt
import time, logging
broker = "127.0.0.1"
port = 1883
QOS = 0
CLEAN_SESSION = True
# error logging
# use DEBUG,INFO,WARNING
def on_subscribe(client, userdata, mid, granted_qos): # create function for callback
# print("subscribed with qos",granted_qos, "\n")
time.sleep(1)
print("sub acknowledge message id=" + str(mid))
pass
def on_disconnect(client, userdata, rc=0):
print("DisConnected result code " + str(rc))
def on_connect(client, userdata, flags, rc):
print("Connected flags" + str(flags) + "result code " + str(rc))
def on_message(client, userdata, message):
msg = str(message.payload.decode("utf-8"))
print("message received in mqtt_subscriber " + msg)
def on_publish(client, userdata, mid):
print("message published " + str(mid))
topic1 = "test"
client = mqtt.Client("RDAresp", False) # create client object
client.on_subscribe = on_subscribe # assign function to callback
client.on_disconnect = on_disconnect # assign function to callback
client.on_connect = on_connect # assign function to callback
client.on_message = on_message
client.connect(broker, port) # establish connection
time.sleep(1)
client.loop_start()
client.subscribe("RemoteDoorAccess")
count = 1
while True: # runs forever break with CTRL+C
print("publishing on topic ", topic1)
msg = "message : RemoteDoorAccess_resp is published "
client.publish(topic1, msg)
count += 1
time.sleep(5)
and in views.py
def on_message(client, userdata, message):
msg = str(message.payload.decode("utf-8"))
print("message authority resp module " + msg)
def on_subscribe(client, userdata, mid, granted_qos): # create function for callback
print("subscribed with qos", granted_qos, "\n")
time.sleep(1)
pass
def on_disconnect(client, userdata, rc=0):
print("DisConnected result code " + str(rc))
def on_connect(client, userdata, flags, rc):
print("Connected flags" + str(flags) + "result code " + str(rc))
def on_publish(client, userdata, mid):
print("message published " + str(mid))
def mqttConnection():
topic = "RemoteDoorAccess"
client = mqtt.Client("RDA", False) # create client object
client.on_subscribe = on_subscribe # assign function to callback
client.on_disconnect = on_disconnect # assign function to callback
client.on_connect = on_connect # assign function to callback
client.on_message = on_message
client.connect(broker, port) # establish connection
time.sleep(1)
client.subscribe("test")
time.sleep(1)
print("publishing on topic ", topic)
msg = "RemoteDoor Access published"
client.publish(topic, msg)
time.sleep(10)
#api_view(['GET'])
#permission_classes([])
def remotedooraccess_mobile(request):
mqttConnection()
return Response({msg: validation["FDP34"]}, status=status.HTTP_200_OK)
Here the topic 'test' is published but not subscribing.
please check views output
in my views.py on_message function is not called by the topic test.
how can I solve this.
I am totally stuck here.. in view.py subscribe function is not calling.
I am very new to mqtt.
please help
You need to start the client loop in your views.py code otherwise there is nothing to actually run your on_message() callback.
You should also move all your calls to client.subscribe() to into the on_connect callback and remove most of the calls to time.sleep()
Hi I have some mqtt python code. I want to publish and subscribe messages but the following code does not output any publish or subscribe messages. How do I fix the code so that publish and subscribe messages are outputted? Any help will be appreciated.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
class MyMQTTClass(mqtt.Client):
def on_connect(self, mqttc, obj, flags, rc):
print("rc: "+str(rc))
def on_message(self, mqttc, userdata, message):
print("message received " ,str(message.payload.decode("utf-8")))
print("message topic=",message.topic)
print("message qos=",message.qos)
print("message retain flag=",message.retain)
def on_publish(self, mqttc, obj, mid):
print("mid: "+str(mid))
def on_subscribe(self, mqttc, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def on_log(self, mqttc, obj, level, string):
print(string)
def run(self):
self.connect("m2m.eclipse.org", 1883, 60)
rc = 0
while rc == 0:
rc = self.loop()
return rc
# If you want to use a specific client id, use
# mqttc = MyMQTTClass("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = MyMQTTClass()
rc = mqttc.run()
print("rc: "+str(rc))
broker_address="broker.hivemq.com"
#broker_address="iot.eclipse.org"
print("creating new instance")
client = mqtt.Client("P1") #create new instance
client.on_message=on_message #attach function to callback
print("connecting to broker")
client.connect(broker_address) #connect to broker
client.loop_start() #start the loop
print("Subscribing to topic","house/bulbs/bulb1")
client.subscribe("house/bulbs/bulb1")
print("Publishing message to topic","house/bulbs/bulb1")
client.publish("house/bulbs/bulb1","OFF")
time.sleep(4) # wait
client.loop_stop() #stop the loop
Output:
Sending CONNECT (u0, p0, wr0, wq0, wf0, c1, k60) client_id=b''
Received CONNACK (0, 0)
rc: 0
Try this code. Added wait after connect to allow time to get the connection established before publishing. Also, I think you can use any one of brokers either iot.eclipse.org or broker.hivemq.com
import paho.mqtt.client as mqtt
import time
class MyMQTTClass(mqtt.Client):
def on_connect(self, mqttc, obj, flags, rc):
print("rc: "+str(rc))
print("Subscribing to topic","house/bulbs/bulb1")
mqttc.subscribe("house/bulbs/bulb1")
def on_message(self, mqttc, userdata, message):
print("message received " ,str(message.payload.decode("utf-8")))
print("message topic=",message.topic)
print("message qos=",message.qos)
print("message retain flag=",message.retain)
def on_publish(self, mqttc, obj, mid):
print("mid: "+str(mid))
def on_subscribe(self, mqttc, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def run(self):
self.connect("broker.hivemq.com", 1883, 60)
print("creating new instance")
client = MyMQTTClass()
client.run()
client.loop_start() #start the loop
time.sleep(2)
print("Publishing message to topic","house/bulbs/bulb1")
client.publish("house/bulbs/bulb1","OFF")
time.sleep(2) # wait
client.loop_stop() #stop the loop
The first script works, meaning the callbacks are called and it ends printing puback: True
The second script where I use class A to do the work does not work. Callbacks are not called, and it ends with a.puback: False
I'm not sure if my problem is that callbacks don't work this way, in which case how can I get my class to work with these Paho MQTT callbacks? or if it's something more subtle.
WORKS:
def on_log_puback(client, userdata, level, buf):
global puback
if 'PUBACK' in buf:
puback = True
print "PUBACK!"
def on_connect(client, userdata, flags, rc):
print "Connect code: ", rc
def on_disconnect(client, userdata, flags, rc=0):
print "Disconnect code: ", rc
def on_message(client, userdata, msg):
print " Message: ", str(msg.payload.decode("utf-8", "ignore"))
def stop():
print ("stopping loop")
client.loop_stop()
print "disconnecting"
client.disconnect()
import paho.mqtt.client as mqtt
import time
client = mqtt.Client("Luke, I am your client")
mqtt.Client.connected_flag = False
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_log = on_log_puback
client.on_message = on_message
status = client.connect(host="test.mosquitto.org",
keepalive=60, port=1883)
print "connect status: ", status
time.sleep(2)
print "loop_start"
client.loop_start()
time.sleep(1)
sret = client.subscribe("test_topic")
print "subscribe returns sret: ", sret
time.sleep(2)
# initialize global
puback = False
# test publish with qos=1
status, msg_id = client.publish(topic="test_topic",
payload="hello!",
qos=1, retain=True)
print "publish status: ", status
time.sleep(2)
print "puback: ", puback
stop()
DOESN'T WORK:
import paho.mqtt.client as mqtt
import time
class A(object):
def __init__(self):
client = mqtt.Client("Luke, I am your client")
self.client = client
mqtt.Client.connected_flag = False
client.on_connect = self.on_connect
client.on_disconnect = self.on_disconnect
client.on_log = self.on_log_puback
client.on_message = self.on_message
status = client.connect(host="test.mosquitto.org",
keepalive=60, port=1883)
print "connect status: ", status
time.sleep(2)
print "loop_start"
client.loop_start()
time.sleep(1)
sret = client.subscribe("test_topic")
print "subscribe returns: ", sret
time.sleep(2)
# initialize global
puback = False
# test publish with qos=1
status, msg_id = client.publish(topic="test_topic",
payload="hello!",
qos=1, retain=True)
print "publish status: ", status
time.sleep(2)
self.puback = puback
def on_log_puback(client, userdata, level, buf):
global puback
if 'PUBACK' in buf:
puback = True
print "PUBACK!"
def on_connect(client, userdata, flags, rc):
print "Connect code: ", rc
def on_disconnect(client, userdata, flags, rc=0):
print "Disconnect code: ", rc
def on_message(client, userdata, msg):
print " Message: ", str(msg.payload.decode("utf-8", "ignore"))
def stop(self):
print ("stopping loop")
self.client.loop_stop()
print "disconnecting"
self.client.disconnect()
a = A()
time.sleep(2)
print 'a.puback: ', a.puback
a.stop()
Found it. When I moved the callbacks into the class, e.g. on_log_puback(self,...) I'd simply forgotten to add self to the beginning of the arguments. With that, it works nicely now.