How to run multile device under same paho-mqtt script - python

I am writing a script which will log more than one device with different credentials using paho-mqtt. All the client is running in the same address and with the same port. If I change the username and pass then I get the different feeds depending upon the credentials. It's working fine if I write for different devices different script. But I want to log all the devices in on_connet. I have written the script but it's working only for one device. Here is the script:
import paho.mqtt.client as mqtt
import time, json, threading, logging,ssl
clients=[
{"ursername":"username1","password": 'password1'},
{"ursername":"username2","password":'password2'},
{"ursername":"username3","password":'password3'}
]
nclients=len(clients)
run = True
def Create_connections():
for i in range(nclients):
t=int(time.time())
client_id = "client" + str(t)
client = mqtt.Client(client_id)
username = credentials[i]["ursername"]
password = credentials[i]["password"]
client.on_log=on_log
client.on_connect = on_connect
client.on_subscribe=on_subscribe
client.on_message = on_message
client.on_disconnect = on_disconnect
print("connecting to broker")
client.tls_set("CXXXXX.crt", tls_version=ssl.PROTOCOL_TLSv1_2)
client.tls_insecure_set(True)
client.username_pw_set(username, password)
client.loop_start()
client.connect("XXXXX", XXXX, XX)
print("Loop pass ")
def on_log(client, userdata, level, buf):
print("message:" + str(buf))
print("userdata:" + str(userdata))
def on_message(client, userdata, message):
msg="message received",str(message.payload.decode("utf-8"))
print(msg)
def on_connect(client, userdata, flags, rc):
print("Connected with result code:"+str(rc))
client.subscribe('v3/+/devices/+/up')
def on_disconnect(client, userdata, rc):
pass
def on_publish(client, userdata, mid):
print("mid: " + str(mid) + '\n')
def on_subscribe(mosq, obj, mid, granted_qos):
print("Subscribed: " + str(mid) + " " + str(granted_qos))
mqtt.Client.connected_flag=False
no_threads=threading.active_count()
print("current threads =",no_threads)
print("Creating Connections ",nclients," clients")
Create_connections()
Response
current threads = 1
Creating Connections 2 clients
____________________________________________________________________________
client01597648398
<paho.mqtt.client.Client object at 0x7f73c0a5b1d0>
username1
connecting to broker
message:Sending CONNECT (u1, p1, wr0, wq0, wf0, c1, k60) client_id=b'client01597648398'
userdata:None
message:Received CONNACK (0, 0)
userdata:None
Connected with result code:0
message:Sending SUBSCRIBE (d0, m1) [(b'XX/+/devices/+/XX', 0)]
userdata:None
message:Received SUBACK
userdata:None
Subscribed: 1 (0,)
message:Sending PINGREQ
userdata:None
message:Received PINGRESP
userdata:None
Any help will be highly appreciated. Thanks in advance

Your problem lies here:
while run:
client.loop_forever()
loop_forever() is a blocking call which will only return when the associated client is disconnected:
This is a blocking form of the network loop and will not return until the client calls disconnect(). It automatically handles reconnecting. paho-mqtt
So your other clients never get initialized/connected. You might want to use loop_start instead - this will use a separate thread to handle communication for every client:
def Create_connections():
for i in range(nclients):
# ...
client.loop_start()
client.connect("XXXXXXXXXX", XXXX, XX)
# ...
Create_connections()
while run:
pass

Related

MQTT Subscriber Failed to Subscribe to the Same Topic With MQTT Publisher

I'm trying to make a simple "transit schedule changes" program using python MQTT where the publisher can input flight number (will be used as topic) and new flight time, while the transit location will be picked random from given list.
The subscriber will have to input flight number too which will be used as topic. But in my codes, it looks like the subscriber failed to get the message published to the same topic because it keeps on printing Connected Successfully (I'm using client.loop_forever()). Can someone please help me to figure out what's wrong with my code?
This is my first time asking question, so please ask me if something isn't clear from my explanation. Thank you so much :)
Publisher:
import paho.mqtt.client as mqtt
import time
from datetime import datetime, date
import random
def on_connect(client, userdata, flags, rc):
if (rc==0):
global connected
connected = True
#print("Successfully Connected.")
client.on_publish = on_publish
else:
print("Failed to connect.")
def on_publish(client, userdata, mid):
print("Published successfully. MID: "+str(mid))
listTransit = ["Singapura", "Qatar", "Korea Selatan", "Turki", "Republik Tiongkok",
"Amerika Serikat", "Jepang", "Uni Emirat Arab", "Oman", "Islandia"]
broker_address="broker.emqx.io"
client = mqtt.Client("Publisher")
client.on_connect = on_connect
client.connect(broker_address, port=1883)
client.loop_start()
topic = input("Masukkan nomor penerbangan: ")
negaraTujuan = input("negara tujuan: ")
print("Masukkan waktu penerbangan baru (Format: [jam::menit::detik])")
str_time = input()
Date = date.today()
Time = datetime.strptime(str_time, '%H::%M::%S').time()
Location = random.randrange(0,len(listTransit))
if (listTransit[Location] != negaraTujuan):
message = Date.strftime("%Y/%m/%d")+"\nTujuan: "+negaraTujuan+"\nLokasi Transit : "+listTransit[Location]+"\nJam terbang : "+Time.strftime("%H:%M:%S")
client.publish(topic, message)
print("Topic: ",topic)
print(message)
else:
while listTransit[Location] == negaraTujuan:
Location = random.randrange(0,len(listTransit))
message = Date.strftime("%Y/%m/%d")+"\nTujuan: "+negaraTujuan+"\nLokasi Transit : "+listTransit[Location]+"\nJam terbang : "+Time.strftime("%H:%M:%S")
client.publish(topic, message)
print(message)
client.loop_stop()
Subscriber:
import paho.mqtt.client as mqtt
import time
from datetime import datetime, datetime
import re
def on_connect(client, userdata, flags, rc):
if (rc == 0):
print("Connected successfully.")
#global topic
#topic = input("Masukkan nomor penerbangan anda: ")
#client.subscribe(topic)
else:
print("Connection failed.")
def on_message(client, userdata, msg):
print("on_message callback function activated.")
sched = str(msg.payload.decode("utf-8"))
print(sched)
def on_subscribe(client, userdata, mid, granted_qos):
print("Subscribed to "+topic+" successfully")
broker_address="broker.emqx.io"
topic = input("Masukkan nomor penerbangan anda: ")
negaraTujuan = input("negara tujuan: ")
client = mqtt.Client("Subscriber")
client.subscribe(topic)
client.on_connect = on_connect
client.on_message = on_message
client.on_subscribe = on_subscribe
client.connect(broker_address, port=1883)
client.loop_forever()
what I got from running both codes are
Masukkan nomor penerbangan: YT05TA
negara tujuan: Australia
Masukkan waktu penerbangan baru (Format: [jam::menit::detik])
12::50::00
Topic: YT05TA
2023/01/03
Tujuan: Australia
Lokasi Transit : Amerika Serikat
Jam terbang : 12:50:00
Published successfully. MID: 1
from Publisher
and
Masukkan nomor penerbangan anda: YT05TA
negara tujuan: Australia
Connected successfully.
Connected successfully.
Connected successfully.
Connected successfully.
from subscriber. It doesn't even print the print("on_message callback function activated.")
You are using a public broker, so that means it is likely to have LOTS of over clients.
Every client MUST have a UNIQUE Client ID, so using Publisher and Subscriber is very likely to clash with other clients.
The MQTT specification says the broker must disconnect the currently connected client when a new client connects with the same Client ID. As most client libraries will try and reconnect when they are disconnects this leads to a battle between the two clients to stay connected.
Change them both to random values

Python & paho.mqtt: disconnecting after 20 min

I've a problem with a simple python & paho.mqtt code, I report below the code that generates problems purged from connections to the db and other secondary functions
import mysql.connector
import datetime
import json
import paho.mqtt.client as mqtt
import aliveUtil
import logging
from logging.handlers import WatchedFileHandler
import dbconn
from signal import signal, SIGINT, SIGTERM
from sys import exit
import ast
import time
def on_connect(client, userdata, flags, rc):
if rc == 0:
mqttcR.subscribe([("/xxx/+/retained",1),("/xxx/status/send/#",1)])
else:
logging.error("Mqtt client retained connection error")
printOutput("Mqtt client retained connect error")
def on_disconnect(client, userdata, rc):
logging.debug("retained returnCode:"+str(rc))
printOutput("Disconnect retained returnCode:"+str(rc))
mqttcR.reconnect()
def on_message(client, obj, msg):
switchMessage(msg)
def on_publish(client, obj, mid):
logging.debug("Payload:" + str(obj.payload) + " Mid:" + str(mid))
def on_subscribe(client, obj, mid, granted_qos):
logging.debug("Mid:" + str(mid) + " GrantedQos:" + str(granted_qos))
def on_log(client, obj, level, string):
logging.debug(string)
def printOutput(stringa):
logging.info(stringa)
def switchMessage(msg):
payload=msg.payload.replace("'", " ")
try:
jsonReceived = json.loads(payload)
except Exception as exception:
logging.error('json.load payload')
logging.error(str(exception))
topic=str(msg.topic)
try:
if topic.find('retained') != -1:
saveRetained(jsonReceived)
elif topic.find('status') != -1:
saveStatus(jsonReceived)
except Exception as exception:
logging.error('Try topic exception')
logging.error(str(exception))
logging.error(str(topic))
#print str(jsonReceived)
def saveRetained(jMessage):
print "do something in saveRetained"
def saveStatus(jMessage):
print "do something in saveRetained"
logging = createWatchedLog('/var/log/xxx/xxx.log')
logging.info('==== START SCRIPT ====')
logging.info('Setup SIGINT signal handler')
signal(SIGINT, get_shutdown_handler('SIGINT received'))
logging.info('Setup SIGTERM signal handler')
signal(SIGTERM, get_shutdown_handler('SIGTERM received'))
logging.info('Setup mqtt client (pyPahoClient)')
unicoID="pythonXXXClient"+str(aliveUtil.randomDigits())
mqttcR = mqtt.Client(client_id=unicoID, clean_session=False)
mqttcR.on_message = on_message
mqttcR.on_connect = on_connect
mqttcR.on_disconnect = on_disconnect
mqttcR.on_publish = on_publish
mqttcR.on_subscribe = on_subscribe
MQTT_Broker = "localhost"
MQTT_Port = 1883
Keep_Alive_Interval = 600
logging.info('Setup mqtt client username e password')
mqttcR.username_pw_set("XXXuser", "xxx")
logging.info('Connect to mqtt broker')
mqttcR.connect(MQTT_Broker, int(MQTT_Port), int(Keep_Alive_Interval))
logging.info('Mqtt loop forever')
mqttcR.loop_forever()
Checking the log file I realized that every 20 minutes the client disconnects and reconnects with the RC 1 message.
2022-01-18 17:14:03,780: INFO: am_alive_mqtt.printOutput():69: Mqtt client connect
2022-01-18 17:14:03,818: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:7 GrantedQos:(1, 1)
2022-01-18 17:34:04,099: DEBUG: am_alive_mqtt.on_disconnect():46: returnCode:1
2022-01-18 17:34:04,101: DEBUG: am_alive_mqtt.on_connect():27: returnCode:0
2022-01-18 17:34:04,101: INFO: am_alive_mqtt.on_connect():30: Mqtt client connect
2022-01-18 17:34:04,145: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:8 GrantedQos:(1, 1)
2022-01-18 17:54:04,412: DEBUG: am_alive_mqtt.on_disconnect():46: returnCode:1
2022-01-18 17:54:04,414: DEBUG: am_alive_mqtt.on_connect():27: returnCode:0
2022-01-18 17:54:04,414: INFO: am_alive_mqtt.on_connect():30: Mqtt client connect
2022-01-18 17:54:04,448: DEBUG: am_alive_mqtt.on_subscribe():62: Mid:9 GrantedQos:(1, 1)
I checked several pages on this site but couldn't find an explanation.
Any suggestions?
Many thanks in advance
UPDATE
After several tests I discovered that the problem occurs when inside the saveRetained function there is a simple query:
query="SELECT * FROM table_mqtt WHERE serialnumber='"+str(serialnumber)+"'"
number_of_rows = cursor.execute(query);
row=cursor.fetchone()
if I comment this part everything works without restarting every 20 min

Rotate cube about fixed axis

So I have a code that takes in real time angle information from an IMU sensor in Raspberry Pi, which is transmitted to my computer using mqtt broker. The angle is based on the orientation of the IMU during upright position (i.e. fixed coordinate system). Now I want to animate the IMU by taking the difference between two corresponding sensor readings which would indicate the amount of rotation required in that particular axis. However, the animation I get is not displaying the orientation correctly. My question is, in the
obj.rotate(angle=a, axis=vec(x,y,z),origin=vector(x0,y0,z0))
function of vpython, how do I say such that the rotation is about the fixed coordinate system, instead of rotating around the IMU's axis? My code is below:
from vpython import *
import math
import paho.mqtt.client as mqtt
import time
scene.title = "VPython: Draw a rotating cube"
scene.range = 2
scene.autocenter = True
def on_connect(client, userdata, flags, rc):
global callback_on_connect
print("Connected with result code "+str(rc))
# Subscribing in on_connect() - if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("CoreElectronics/test")#these two are topics; thus the client here listens to these two topics
client.subscribe("CoreElectronics/topic")
callback_on_connect=1
# The callback for when a PUBLISH message is received from the server.
# ONLY WORKS IF YOU HAVE A MESSAGE FROM RASPBERRY PI SINCE IT IS A CALLBACK!
def on_message(client, userdata, msg):
global yaw
global pitch
global roll
global callback_on_message
callback_on_message=1
#print(msg.topic+" "+str(msg.payload))
#print(float(msg.payload))
#print(3)
f=msg.payload
angles = [float(i) for i in f.split()]
#print(3)
#type(angles)
yaw=angles[0]
pitch=angles[1]
roll=angles[2]
print(angles)
#x = [float(i) for i in f.split()]
#print(x[0])
#print(x[1])
# Do something else
print("Drag with right mousebutton to rotate view.")
print("Drag up+down with middle mousebutton to zoom.")
cube = box(pos=vec(0,0,0),length=1,height=0.1,width=1) # using defaults, see http://www.vpython.org/contents/docs/defaults.html
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
print("Connecting to server...")
client.connect("mqtt.eclipse.org", 1883, 60)
print("Reached loop function...")
client.loop_start()
yaw=''
pitch=''
roll=''
yaw2=0
pitch2=0
roll2=0
#callback_on_connect=0
callback_on_message=0;
while callback_on_message==0:
print("waiting for callback as a result of successful message receive", callback_on_message)
#now we are connected, can start taking in data
while True: # Animation-loop
try:
#print("animating")
#arrow(pos=vec(0,0,0),axis=vec(1,0,0))
#arrow(pos=vec(0,0,0),axis=vec(0,1,0))
#arrow(pos=vec(0,0,0),axis=vec(0,0,1))
cube.rotate(angle=radians(yaw-yaw2), axis=vec(1,0,0),origin=vector(0,0,0))
cube.rotate(angle=radians(pitch-pitch2), axis=vec(0,1,0),origin=vector(0,0,0))
cube.rotate(angle=radians(roll-roll2), axis=vec(0,0,1),origin=vector(0,0,0))
yaw2=yaw
pitch2=pitch
roll2=roll
except KeyboardInterrupt:
client.loop_stop()
#cube.rotate( angle=yaw, axis=vec(1,0,0) )
#cube.rotate( angle=pitch, axis=vec(0,1,0) )
#cube.rotate( angle=roll, axis=vec(0,0,1) )

Why does the paho python client publish short messages but not long ones

This works :
while True:
print('')
command_input = input()
if command_input == 'q':
break
mcp = Mqtt_command_publisher
mcp.publish_command(device_ids, command_input)
But this does not:
class Mqtt_command_bl:
def update_minutes_to_run_at(json):
if not json['device_ids']:
return 'Request must contain device ids'
device_ids = json['device_ids']
minutes_to_run_at = json['minutes_to_run_at']
minutes_to_run_at_command_section = ''
for i in minutes_to_run_at:
m = '"{}",'.format(i)
if i == minutes_to_run_at[len(minutes_to_run_at) - 1]:
m = '"{}"'.format(i)
minutes_to_run_at_command_section += m
#command_input = 'jq \'.+{{minutes_to_run_at:[{}]}}\' /home/pi/hallmonitor_lite/config.json > /home/pi/hallmonitor_lite/tmp.json && mv /home/pi/hallmonitor_lite/tmp.json /home/pi/hallmonitor_lite/new_config.json'.format(minutes_to_run_at_command_section)
command_input = 'mkdir /home/pi/hallmonitor_lite/hello_world'
mcp = Mqtt_command_publisher
mcp.publish_command(device_ids, command_input)
return 'Success'
The class they both call:
class Mqtt_command_publisher:
def publish_command(device_ids, command_input):
mqtt_msg = json.dumps({'device_ids':device_ids,'command':command_input})
print('\n{}'.format(mqtt_msg))
client = mqtt.Client()
client.connect('********', ****, 30)
client.publish('topic/commands', mqtt_msg)
client.disconnect()
Looking at the print statements output from the Mqtt_command_publisher, the output can be the exact same, however, only one of them will execute, and I don't see why one works and the other does not.
I tried this command for testing: mkdir /home/pi/hallmonitor_lite/hello_world
This is the receiving part:
device_id = 0
with open('/home/pi/hallmonitor_lite/config.json') as json_data_file:
data = json.load(json_data_file)
device_id = data['device_id']
def on_connect(client, userdata, flags, rc):
print("Connected with result code: " + str(rc))
client.subscribe("topic/commands")
def on_message(client, userdata, msg):
mqtt_message = msg.payload.decode()
print(mqtt_message)
ids_and_command = json.loads(mqtt_message)
if str(device_id) in ids_and_command['device_ids'] or not ids_and_command['device_ids']:
print(('Executing: {}').format(ids_and_command['command']))
os.system(ids_and_command['command'])
client = mqtt.Client()
client.connect("********", ****, 30)
client.on_connect = on_connect
client.on_message = on_message
client.loop_forever()
Any ideas?
The problem is most likely because the second set of code is creating a message bigger than will fit in a single TCP packet.
This is a problem because you are not running the client network loop so the client.publish command can only send a single packet, the rest of the message would have been sent by the network loop, but even if it was running you are calling disconnect immediately after the publish call.
The client is not meant to be spun up for a single message like that, it is meant to be started and then left running with you just calling the publish method when you want to send a message. If you don't want to do that or can't for some reason there is a specific helper class in the paho python package that will do all the heavy lifting of starting the client, sending the message and then tearing everything down nicely. The docs for the single publish are here.
import paho.mqtt.publish as publish
publish.single("paho/test/single", "payload", hostname="mqtt.eclipse.org")

Twisted Websocket - Broadcasting doesn't work when called from another file

I was setting up an Websocket Server which is loggin into another server and pushing data trough the socket to the webpage (trough a subscribe function). As long as i keep calling the broadcast function from the file where the websocket runs, everything is fine. But calling the broadcast method from another python-file where my push-function is printing to command line, no client is recieving a message.
I assume, that calling the broadcast from another file creates another instance and with that the self.clients is empty.
So to sum up, clients connected get the broadcast from loginGESI() but not in my second file from scrptCallbackHandlerExample(subType).
Would be happy about any help!
here is my Websocket file:
class BroadcastServerProtocol(WebSocketServerProtocol):
def onOpen(self):
self.factory.register(self)
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
self.factory.unregister(self)
class BroadcastServerFactory(WebSocketServerFactory):
clients = []
def __init__(self, url):
WebSocketServerFactory.__init__(self, url)
def register(self, client):
if client not in self.clients:
print("registered client {}".format(client.peer))
self.clients.append(client)
def unregister(self, client):
if client in self.clients:
print("unregistered client {}".format(client.peer))
self.clients.remove(client)
#classmethod
def broadcast(self, msg):
print("broadcasting message '{}' ..".format(msg))
print(self.clients)
for c in self.clients:
c.sendMessage(msg.encode('utf8'))
print("message sent to {}".format(c.peer))
def login():
codesys = Test_Client("FTS_test")
result = codesys.login()
# FTS = codesys.searchForPackage("F000012")
FTS = ["15900"];
scrptContextId = [None] * len(FTS)
itemContextIds_array = [None] * len(FTS)
for i in range(0,len(FTS)):
result, scrptContextId[i] = codesys.createSubscription(c_ScrptCallbackHandlerExample, 100, int(FTS[i]))
print("SubscriptionRoomId: "+str(scrptContextId[i]))
result, itemContextIds_array[i], diagInfo = codesys.attachToSubscription(1, [FTS[i]+'.speed'], [100])
print("Subscription done for: "+str(itemContextIds_array[i]))
print("Subscription for: Speed")
BroadcastServerFactory.broadcast(str(FTS[0]))
if __name__ == '__main__':
# Logger Websocket
log.startLogging(sys.stdout)
# factory initialisieren
ServerFactory = BroadcastServerFactory
factory = ServerFactory("ws://127.0.0.1:9000")
factory.protocol = BroadcastServerProtocol
listenWS(factory)
# reactor initialisieren
webdir = File(".")
web = Site(webdir)
reactor.listenTCP(8080, web)
reactor.callLater(5, login)
reactor.run()
and here my subscription file:
# Launch of the CallbackHandler named in the createSubscription function
# CallbackHandler describes what happens to a variable which changes its value
def scrptCallbackHandlerExample(subType):
BroadcastServerFactory.broadcast('test')
# Saves the value of the variables(s) in an array
dataValue = []
for i in range(0,subType.size):
dataValue.append(subType.dataItems[i].node.dataValue)
# Print variabel informations on the screen
print "*****Callback - Data Change in a Variable*****"
print( 'Subscription ID: %d' % subType.subscrId )
for idx in range(0,subType.size):
print( '** Item %d **' % idx )
print( 'Item Id: %d' % subType.dataItems[idx].dataItemId )
print( 'Item Node ID: %s' % subType.dataItems[idx].node.nodeId )
print( 'Item data value: %s' % subType.dataItems[idx].node.dataValue )
print( 'Item data type: %s' % subType.dataItems[idx].node.dataType )
print( '******************************' )
# Define the type of the function as an eSubscriptionType
CB_FUNC_TYPE = CFUNCTYPE( None, eSubscriptionType)
c_ScrptCallbackHandlerExample = CB_FUNC_TYPE( scrptCallbackHandlerExample )
Regards
I found, in my oppinion, a pretty neat workaround.
Whenever my subscribe function is called, I connect with a local client to my websocket server and send him a message with the new values, the websocket server then pushes this to the other clients. Since I am still working on it I can't post any code but the functionality is given. So if someone is interested in a solution let me know and I can post mine.

Categories