How to receive messages from multiple clients over mqtt? - python

I am sending a number of messages simultaneously from multiple clients from one python script and trying to receive them on another script. The problem I am getting is that the message is received but only from the first client that gets connected and it keeps on looping over it.
What I need to have is that I get messages from each client in the manner they are published.
import paho.mqtt.client as mqtt
import time
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to broker")
global Connected
Connected = True
else:
print("Connection failed")
def on_message(client, userdata, message):
print ("Message received: " + str(message.payload) + " from " + str(client))
Connected = False
client = mqtt.Client()
client.on_connect= on_connect
client.on_message= on_message
client.connect(host)
client.loop_start()
while Connected != True:
time.sleep(0.1)
client.subscribe("test")
print("subscribed")
client.loop_stop()

You are misunderstanding what the client argument in the on_message callback is.
This value is a link to the local instance of the MQTT client that has subscribed to the topic.
MQTT messages do not carry any information about the client that published them, unless you explicitly encode it into the payload. Part of the point of a Pub/Sub protocol like MQTT is to totally decouple the information creator (publisher) from the information consumer (subscriber).
Also you should move the call to client.subscribe("test") to inside the on_connect() callback because as you have it you are trying to resubscribe to the same topic 10 times a second which will achieve absolutely nothing, except to generate unneeded load on the broker.

Related

Mqtt Subscriber, loop_forever() does not work

I am having an issue with the code below.
The code works perfectly at the beginning. First it says Connected to MQTT Broker! and receives data from it. But after a long time (like 6 hours, or 10 hours etc.) it says again Connected to MQTT Broker! and after that id does not receive any other data.
I am trying to make this program work forever, but i don't know what i have done wrong.
Any ideas?
# python3.6
import random
import mysql.connector
from paho.mqtt import client as mqtt_client
import json
# Code for MQTT Connection
broker = 'YOUR_BROKER'
port = 1883
topic = "YOUR_TOPIC"
# generate client ID with pub prefix randomly
client_id = f'python-mqtt-{random.randint(0, 100)}'
username = "THE_USERNAME"
password = "THE_PASSWORD"
# Function to connect on mqtt
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.username_pw_set(username, password)
client.on_connect = on_connect
client.connect(broker, port)
return client
# function to subscribe from mqtt
def subscribeFunc(client: mqtt_client):
def on_messageFunc(client, userdata, msg):
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
client.subscribe(topic)
client.on_message = on_messageFunc
def run():
client = connect_mqtt()
subscribeFunc(client)
client.loop_forever()
if __name__ == '__main__':
run()
I tried to find the problem but it seems that nothing changed significantly.
I am expecting this program to receive data without stopping.
Network connections may not be 100% reliable (and servers etc are restarted from time to time) so expecting the connection to remain up forever is unrealistic. The issue with your code is that it connects then subscribes; if the connection is dropped it does not resubscribe. As the connection is clean_session=true and subscription qos=0 (the defaults) the broker will forget about any active subscriptions when the connection drops (so the client will reconnect but not receive any more messages).
The Simple solution is to use the approach shown in the docs and subscribe in the on_connect callback (that way the subscription will be renewed after reconnection):
def on_connect(client, userdata, flags, rc):
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("$SYS/#")
client = mqtt.Client()
client.on_connect = on_connect
client.connect("mqtt.eclipseprojects.io", 1883, 60)
You may also want to consider the advice in this answer (as per the comment from #mnikley) because that way the broker will queue up messages for you while the connection is down (otherwise these will be lost).

Use mqtt message data in program not only in on_message function (paho mqtt python)

I have a little python script which connects, subscribes, publish and disconnects to a mqtt broker. Everything is fine, but I only can use the received data in the on_message function.
first the actual code:
import time
import sys
sys.path.append('C:\\Users\\user\\Python\\pyproj\\project1\\Lib\\site-packages\\paho_mqtt-1.6.1-py3.10.egg')
import paho.mqtt.client as mqtt
userdata = "Leer"
client_name = "Pythonscript"
broker_address="000.000.000.000"
port = 1234
topic = "topic to publish"
subtopic = "topic to recieve"
mqtt1 = ''
def on_log(client, userdata, level, buf):
print(" client: ",client)
print(" userdata: ",userdata)
print(" level: ",level)
print(" buf: ",buf)
def on_connect(client, userdata, flags, rc):
if rc==0:
#print("connected OK ")
#print("Subscribing to topic ",subtopic)
client.subscribe(subtopic)
else:
print("Bad Connection Returned code=",rc)
def on_message(client,userdata,msg):
topic=msg.topic
m_decode=str(msg.payload.decode("utf-8","ignore"))
global mqtt1
mqtt1 = m_decode
print("message recieved:",m_decode)
print("message in mqtt1:",mqtt1)
return m_decode
def on_disconnect(client, userdata, flags, rc=0):
print("Disconnected result code "+str(rc))
print("creating new instance ",client_name)
client = mqtt.Client(client_name)
client.on_connect=on_connect
client.on_disconnect=on_disconnect
#client.on_log=on_log
client.on_message=on_message
print("connecting to broker ",broker_address+" :"+str(port))
client.connect(broker_address,port,60)
client.loop_start()
print("Publishing message to topic ",topic)
client.publish(topic, "python mqqt message")
result = mqtt1 + "changes"
print("recieved Message in Variable:",result)
time.sleep(4)
client.loop_stop()
client.disconnect()
i would like to get the whole message object and work with that data in my script.
I see my msg.payload in the console with print in on_message.
but i cant use it, for instance, in the variable result.
I tried with a global variable, with a return from the on_message function. but nothing works.
I think I don't understand which parameters I should give the on_message function to get my returned value (later the whole msg object)
perhaps somebody can help me to understand.
here is my console output:
=========== RESTART: C:\Users\user\Python\pyproj\project1\mqtt.py ==========
creating new instance Pythonscript
connecting to broker ip :1883
Publishing message to topic my topic
recieved Message in Variable: changes
message recieved: 73
message in mqtt1: 73
message recieved: 91
message in mqtt1: 91
message recieved: 57
message in mqtt1: 57
message recieved: 50
message in mqtt1: 50
message recieved: 50
message in mqtt1: 50
Disconnected result code 0
But I cant get the value in an variable out of the on_message function...
You need to remember that the on_message() function is not called by any of your code, it is called by the MQTT client's network loop as it processes incoming packets from the network.
The return at the end of on_message() will not do anything useful as there is nowhere to return anything to.
Your next problem is that you have to remember that MQTT is asynchronous, so trying to read the value of mqtt1 immediate after the call to client.publish() will just not work. Your code will try to read the value immediately, but you have no way of knowing how long it will take for a message to be delivered as a possible response from any client that is subscribed.
Remember that you should not think of MQTT as something like HTTP, it is not a request response synchronous model. While MQTTv5 added the concept of response topics in the headers, it did not change the fact that a response message is totally separate the request message and may arrive, arrive late, never arrive or arrive multiple times from multiple other clients.
You need to spend some time learning about asynchronous systems and look at how you maintain state under those circumstances (e.g. a state machine model)

How to view the last SMS from the device in the topic with broker Mosquitto?

For example, there I am listening to the settings / # topic. The device sends a line to the settings / topicN topic, where N is the device number.
The moment I receive something from the device, I want to see the previous SMS from it.
How to see the moment of receiving data from the device exactly according to
Is this thread just the latest post in python?
import paho.mqtt.client as mqtt
# 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))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("settings/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
imei = msg.topic.split('settings/')[1]
data = msg.payload.decode()
print(imei)
print(data)
publish(client, imei)
def publish(client,imei):
topic = 'test/'+ imei
client.publish(topic,'hello')
print('SEND')
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.on_connect = on_connect
client.on_message = on_message
client.enable_bridge_mode()
You don't.
If you are not subscribed to a topic then there is no way to see any old messages sent to that topic before you subscribed to it. (With the exception of if the message was published with the retained bit set to true, then the last message published will be delivered by the broker at the point of subscription before any new messages)
If you want to keep the history, then you need to create a client that is always subscribed to the topics and stores them in some sort of database.

How to use both subscribe and publish at the same time in mqtt python?

I am working with a device that publishes to the topic test/123, where 123 is the name of the device. I need to subscribe to that topic (and processes received messages); in addition I also need to send a word to the same topic (test/123). The device only looks at this topic.
How can I distinguish between incoming and outgoing by content? More precisely, how to send correctly. In the on_message method you need to do this or you need to create another method, but then how to receive incoming messages there. From the incoming messages I need to get the name of the device and then work with it.
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
# 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))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("/test/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
imei = msg.topic.split('test/')[1]
data = msg.payload.decode()
print(imei)
print(data)
publish(imei)
def publish(imei):
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.connect("localhost",1883)
topic = '/test/'+ imei
client.publish(topic,'hello')
print('SEND')
client.disconnect()
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
client.loop_forever()
MQTT does not differentiate between clients in any way, that means if a client subscribes to a given topic it will receive ALL messages on that topic, including the ones it publishes it's self. So with your current design you will always get the message you publish in response to the first message back and this will trigger re-sending that message.
MQTT messages do NOT contain any information about who published the message unless you choose to add it to the payload, so you have no way to identify the incoming message as being the one you just published and this will cause a message loop storm.
The CORRECT solution is to not use the same topic for the 2 messages.
MQTT v5 has a flag that can be passed as part of establishing the connection
which prevents messages being returned to the client that published them. At this time it does not appear that the Paho Python library has a way to set this flag.
If you are using MQTT v3.1.1 and the mosquitto or RSMB MQTT broker then there is an undocumented option (this is not part of the MQTT spec) that can be set which will also prevent messages being returned. The following code will ONLY work with the 2 brokers I have mentioned.
import paho.mqtt.client as mqtt
# 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))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("test/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
imei = msg.topic.split('test/')[1]
data = msg.payload.decode()
print(imei)
print(data)
publish(client, imei)
def publish(client,imei):
topic = 'test/'+ imei
client.publish(topic,'hello')
print('SEND')
client = mqtt.Client()
user = 'test'
passw = '1111'
client.username_pw_set(user,passw)
client.on_connect = on_connect
client.on_message = on_message
client.enable_bridge_mode()
client.connect("localhost", 1883, 60)
client.loop_forever()
p.s. do not start topics with a leading / while legal according to the spec, it will break things like shared subscriptions and adds an extra null to the start of the topic tree.

Send python input through mosquitto

Here is the code I'm running for my client. It works pretty well, but it doesn't allow python inputs to be made. I considered making another .py-file for typing and sending messages, but I'm not sure how to import the established connection.
Is it somehow possible to enable a python input chat using mqtt?
import paho.mqtt.client as mqtt
# 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))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("hello/world")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+"| "+ userdate + " said: "+str(msg.payload))
id = raw_input('username: ')
client = mqtt.Client(id)
client.on_connect = on_connect
client.on_message = on_message
client.connect_async("192.168.0.24", 1883, 60)
# 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()

Categories