What is the most reliable way to use the Python Paho MQTT client? I want to be able to handle connection interruptions due to WiFi drops and keep trying to reconnect until it's successful.
What I have is the following, but are there any best practices I'm not adhering to?
import argparse
from time import sleep
import paho.mqtt.client as mqtt
SUB_TOPICS = ("topic/something", "topic/something_else")
RECONNECT_DELAY_SECS = 2
def on_connect(client, userdata, flags, rc):
print "Connected with result code %s" % rc
for topic in SUB_TOPICS:
client.subscribe(topic)
# EDIT: I've removed this function because the library handles
# reconnection on its own anyway.
# def on_disconnect(client, userdata, rc):
# print "Disconnected from MQTT server with code: %s" % rc
# while rc != 0:
# sleep(RECONNECT_DELAY_SECS)
# print "Reconnecting..."
# rc = client.reconnect()
def on_msg(client, userdata, msg):
print "%s %s" % (msg.topic, msg.payload)
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("user")
p.add_argument("password")
p.add_argument("host")
p.add_argument("--port", type=int, default=1883)
args = p.parse_args()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_msg
client.username_pw_set(args.user, args.password)
client.connect(args.host, args.port, 60)
client.loop_start()
try:
while True:
sleep(1)
except KeyboardInterrupt:
pass
finally:
client.loop_stop()
Set a client_id so it's the same across reconnects
Set the clean_session=false connection option
Subscribe at QOS greater than 0
These options will help ensure that any messages published while disconnected will be delivered once the connection is restored.
You can set the client_id and clean_session flag in the constructor
client = mqtt.Client(client_id="foo123", clean_session=False)
And set the QOS of the subscription after the topic
client.subscribe(topic, qos=1)
Related
I cant get mqtt messages from pythonscript on Node Red and cant recieve mqtt messages from Node Red.
this is my 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 = "a topic"
subtopic = "fromNodeRed"
def on_log(client, userdata, level, buf):
print(" client: ",client)
print(" userdata: ",userdata)
print(" level: ",level)
print(" buf: ",buf)
def on_connect(client, userdate, flags, rc):#, message):
if rc==0:
print("connected OK ")
print("Subscribing to topic ",subtopic)#topic)
client.subscribe(subtopic)
else:
print("Bad Connection Returned code=",rc)
def on_message(client,userdate,msg):
topic=msg.topic
m_decode=str(msg.payload.decode("utf-8","ignore"))
print("message recieved",m_decode)
print("Publishing message to topic ",topic)
client.publish(topic, "python mqqt message")
def on_disconnect(client, userdate, 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")
time.sleep(4)
client.loop_stop()
client.disconnect()
I changed some data for protection.
Here is the console output:
I checked the topics, the IP, the Port. Everything seems to be ok.
What do i miss?
I used the portnumber from Node Red instead of the mqtt broker.
After that, I recieved and published messages.
I am using the following code in attempt to connect to Azure IoT Hub. It uses SAS, therefore no security certificates are needed. I am successfully connecting to Azure IoT Hub using the same M2MQTT library in C# but this code fails with:
Failed to connect.Connection refused, not authorized. Error Code= 5
I tried any possible combos of security parameters but at no avail. The SAS token is generated by DeviceExplorer.
#! /usr/bin/python3.5
import serial
import time
import datetime
import os
import socket
import ssl
import logging
import paho.mqtt.client as mqtt
import sys
print(sys.executable)
def on_disconnect(client, userdata, rc):
if rc==0:
print("client disconnected OK")
client.connected_flag=False
def on_connect(client, userdata, flags, rc):
if rc==0:
print("Connected OK")
mqtt.Client.connected_flag=True
mqtt.Client.bad_connection_params=False
else:
mqtt.Client.bad_connection_params=True
if rc==1:
print("Failed to connect. Connection refused, unacceptable
protocol version. Error Code=", rc)
elif rc==2:
print("Failed to connect.Connection refused, identifier
rejected. Error Code=", rc)
elif rc==3:
print("Failed to connect.Connection refused, server unavailable. Error Code=", rc)
elif rc==4:
print("Failed to connect.Connection refused, bad user name or password. Error Code=", rc)
elif rc==5:
print("Failed to connect.Connection refused, not authorized. Error Code=", rc)
def on_publish(client, userdata, mid):
if rc==0:
print("Data published OK: ", userdata)
else:
print("Failed to publish data. MessageID=", mid)
pass
broker="myIoTHubName.azure-devices.net"
port=8883
DeviceID="MasterTag"
DeviceKey="myDeviceKey"
IoTHubName="myIoTHubName"
SasToken="SharedAccessSignature sr=myIoTHubName.azure-devices.net&sig=..."
# Create client object
# 4 stands for MQTTv311
rpiclient = mqtt.Client("PahoClient-on-RPi-Gateway2", clean_session=True, userdata=None, protocol=4, transport="tcp")
usernameFormat="{}{}{}"
username=usernameFormat.format(IoTHubName, ".azure-devices.net/", DeviceID)
password=SasToken
rpiclient.username_pw_set(username, password)
rpiclient.tls_set(tls_version=ssl.PROTOCOL_TLSv1_2)
rpiclient.tls_insecure_set(True)
# connection flag indicates that connection was made or not
mqtt.Client.connected_flag = False
# connection parameters are incorrect: ip address, port, authentication, etc
mqtt.Client.bad_connection_params=False
#assign function to callback
rpiclient.on_connect = on_connect
#assign function to callback
rpiclient.on_publish = on_publish
# bind the disconnect callback
rpiclient.on_disconnect = on_disconnect
rpiclient.loop_start()
rpiclient.will_set("dwm/position", "Client PahoClient-on-RPi2 had unexpectedly disconnected", 1, True)
try:
print("Connecting to MQTT broker ",broker)
# Connect to the MQTT Broker
rpiclient.connect(broker, port)
time.sleep(1)
# Wait in a loop until we are connected
print("mqtt.Client.connected_flag={},
mqtt.Client.bad_connection_params={}".format(mqtt.Client.connected_flag, mqtt.Client.bad_connection_params))
while mqtt.Client.connected_flag == False and mqtt.Client.bad_connection_params == False:
print("Waiting for connection...");
time.sleep(1)
if mqtt.Client.bad_connection_params == True:
rpiclient.loop_stop()
sys.exit()
except Exception as ex:
print("Connection to MQTT Broker failed: ", ex)
rpiclient.loop_stop()
# Disconnect MQTT Client
rpiclient.disconnect()
Any advice is appreciated.
the following is a working example of the simulated device1 connected to the Azure IoT Hub using a paho.Mqtt client library:
from paho.mqtt import client as mqtt
import time
import ssl
def on_subscribe(client, userdata, mid, granted_qos):
print('Subscribed for m' + str(mid))
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
def on_message(client, userdata, message):
print("Received message '" + str(message.payload) + "' on topic '" + message.topic + "' with QoS " + str(message.qos))
def on_log(client, userdata, level, buf):
print("log: ",buf)
device_id = "device1"
iot_hub_name = "myIoTHub"
sas_token = "SharedAccessSignature sr=myIoTHub.azure-devices.net%2Fdevices%2Fdevice1&sig=****&se=1586926815"
client = mqtt.Client(client_id=device_id, protocol=mqtt.MQTTv311, clean_session=False)
client.on_log = on_log
client.tls_set_context(context=None)
# Set up client credentials
username = "{}.azure-devices.net/{}/api-version=2018-06-30".format(iot_hub_name, device_id)
client.username_pw_set(username=username, password=sas_token)
# Connect to the Azure IoT Hub
client.on_connect = on_connect
client.connect(iot_hub_name+".azure-devices.net", port=8883)
# Publish
client.publish("devices/{device_id}/messages/events/".format(device_id=device_id), payload="{}", qos=0, retain=False)
# Subscribing on the topic ,
client.on_message = on_message
client.on_subscribe = on_subscribe
client.subscribe("devices/{device_id}/messages/devicebound/#".format(device_id=device_id))
client.subscribe("$iothub/twin/PATCH/properties/desired/#")
client.subscribe("$iothub/methods/POST/#")
client.loop_forever()
Update:
The sas_token for the specific device can be generated using the Device Explorer tool, see the following screen snippet:
and the output log should be looked like the following screen snippet:
When you use MQTT to connect the Azure IoT Hub directly, you need to connect over TLS/SSL. In order to establish a TLS connection, you may need to download and reference the DigiCert Baltimore Root Certificate, and then set the certificate for the TLS/SSL connection. Please see the document Communicate with your IoT hub using the MQTT protocol.
client.tls_set(ca_certs=path_to_root_cert, certfile=None, keyfile=None,
cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None)
client.tls_insecure_set(False)
I have somewhat unstable wifi network at home. I have pyhon code running on raspberry pi which tries to connect to "test.mosquitto.com" broker.
Below is the python code which I have written
import time
import paho.mqtt.client as mqtt
bConnected = False
def on_disconnect(client, userdata, msg):
print "Disonnected from broker"
global bConnected
bConnected = False
def on_connect(client, userdata, msg):
print "Connected to broker"
global bConnected
bConnected = True
def worker_thread():
"""Creates mqtt client which check connection between pi and mqtt broker"""
client = mqtt.Client()
client.on_disconnect = on_disconnect
client.on_connect = on_connect
global bConnected
while not bConnected:
try:
print "Trying to connect broker"
client.connect("test.mosquitto.org", 1883, 5)
client.loop_forever()
time.sleep(5)
except:
bConnected = False
It works fine for me.
Please let me know is there another efficient way to do this.(I guess there should be another way).
Thanks in advance.
I have a Python script wherein I'm connected to a MQTT server. I'm expecting to get a message through the topic where I'm subscribed to, but should I not receive the message, I want to terminate the script entirely.
The script I'm working with looks as follows:
#!/usr/bin/python
import sys
import json
import paho.mqtt.client as mqtt
def on_message(client, userdata, msg):
if msg.topic == "discovery":
data = json.loads(msg.payload)
serial = data['serial']
print "test successful!"
sys.exit(0)
def on_connect(client, userdata, flags, rc):
client.subscribe([("discovery", 2)])
client = mqtt.Client()
try:
client.connect('localhost', 4444)
except:
print "ERROR: Could not connect to MQTT
client.on_connect = on_connect
client.on_message = on_message
client.loop_forever()
I have tried using a while True statement to loop and figure the time passing in between starting the script and it getting the message, but it seemed to (obviously) not escape the loop even while it gets the message through.
Is there a way that I can say how long it be connected for, and when it exceeds that time, just terminate the script entirely?
Or perhaps, is there a way (as I tried before) to make a loop but also consider the message coming through while in the loop?
Thanks for your suggestions!
Try something like this
It should wait for about 5 seconds for an incoming message then quit. You can adjust the wait time by changing the value waitTime just before the while loop
I have used the version of the mqtt network loop function that only runs for a short time and put it in a while loop. The loop also checks elapsed time and disconnects the client cleanly before bailing out of the loop. I also added a clean client exit for when a message is received.
#!/usr/bin/python
import sys
import json
import paho.mqtt.client as mqtt
import time
def on_message(client, userdata, msg):
if msg.topic == "discovery":
data = json.loads(msg.payload)
serial = data['serial']
print "test successful!"
client.disconnect()
sys.exit(0)
def on_connect(client, userdata, flags, rc):
client.subscribe([("discovery", 2)])
client = mqtt.Client()
try:
client.connect('localhost', 4444)
except:
print "ERROR: Could not connect to MQTT"
client.on_connect = on_connect
client.on_message = on_message
startTime = time.time()
waitTime = 5
while True:
client.loop()
elapsedTime = time.time() - startTime
if elapsedTime > waitTime:
client.disconnect()
break
I'm trying to send a jpg image using MQTT mosquitto broker(pub and sub) in Raspberry Pi2.
This is my python code pub.py(modified)
import paho.mqtt.client as mqtt
def on_publish(mosq, userdata, mid):
mosq.disconnect()
client = mqtt.Client()
client.connect("test.mosquitto.org", 1883, 60)
client.on_publish = on_publish
f=open("b.jpg", "rb") #3.7kiB in same folder
fileContent = f.read()
byteArr = bytearray(fileContent)
client.publish("image",byteArr,0)
client.loop_forever()
and it's sub.py(modified)
import paho.mqtt.client as mqtt
def on_connect(client, userdata, rc):
print("Connect" + str(rc))
client.subscribe("image")
def on_message(client, userdata, msg):
print "Topic : ", msg.topic
f = open("/tmp/output.jpg", "w") #there is a output.jpg which is different
f.write(msg.payload)
f.close()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("test.mosquitto.org", 1883, 60)
client.loop_forever()
My python verson is 2.7.9.
After I solved some error, It seems to work but It doesn't.
When I implement the sub.py, It connects successfully so I implement the pub.py in other terminal.
However, There isn't any reaction without connect message which is "Connect with result code 0"
There is no error message so I don't know what my mistake is.
Tested Code:
Requirements:
Install Mosquitto Broker
Install paho.mqtt package.
pub.py
import paho.mqtt.publish as publish
MQTT_SERVER = "xxx.xxx.xxx.xxx" #Write Server IP Address
MQTT_PATH = "Image"
f=open("image_test.jpg", "rb") #3.7kiB in same folder
fileContent = f.read()
byteArr = bytearray(fileContent)
publish.single(MQTT_PATH, byteArr, hostname=MQTT_SERVER)
One small modification, file permission is write byte instead of write mode.
sub.py
import paho.mqtt.client as mqtt
MQTT_SERVER = "localhost"
MQTT_PATH = "Image"
# 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(MQTT_PATH)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
# more callbacks, etc
# Create a file with write byte permission
f = open('output.jpg', "wb")
f.write(msg.payload)
print("Image Received")
f.close()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_SERVER, 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()
Expected Output:
Able to transfer the Image from Raspberry Pi to server machine.
In sub.py you have 2 on_public functions which should be renamed on_connect and on_publish respectively.
In pub.py you need to actually set the on_publish method on the client so it will be called once the publish has been completed.
...
client.connect("test.mosquitto.org", 1883, 60)
client.on_publish = on_public
...
Also as #ralight pointed out in his answer to your previous question, you should change client.loop(5) to client.loop_forever() it will still exit once the message has been sent because of the mosq.disconnect()
In your sub.py you need a callback which handles incoming messages for your subscription. The standard callback for that is on_message.
Just rename in your sub.y on_publish(client, userdata, msg) to on_message(client, userdata, msg)and assign client.on_message = on_message.