In short, I have a Paho/MQTT sample set that works fine, but I'm having trouble detecting errors. Specifically, I'm not getting an on_connect callback and when an invalid UserID/Pswd combination is given, it fails silently.
In fact, by every indication, it all worked fine!
What am I doing wrong?
(snip)
def on_connect(client, userdata, flags, rc):
print("Connected with flags [%s] rtn code [%d]"% (flags, rc) )
def on_disconnect(client, userdata, rc):
print("disconnected with rtn code [%d]"% (rc) )
def on_publish(client, userdata, msgID):
print("Published with MsgID [%d]"% (msgID) )
mqttc = mqtt.Client()
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.on_publish = on_publish
mqttc.username_pw_set(Q_USER, Q_PSWD)
rc=mqttc.connect(Q_BROKER, Q_PORT)
print "Return="+str(rc)
mqttc.loop_start()
rc=mqttc.publish(Q_TOPIC, "Hello, World!")
print "Return="+str(rc)
mqttc.disconnect()
mqttc.loop_stop()
Output when UserID or Pswd is deliberately wrong:
Return=0
Published with MsgID [1]
Return=(0, 1)
disconnected with rtn code [0]
Turns out that it was a few different issues.
First, Connect returns without truly being connected. Adding a wait loop with a Sleep until the Connect callback is received is crucial. Only the callback tells you the Connect succeeded or failed.
Second, once I saw the return code, I saw mine was failing on Protocol Version Mismatch. The Mosquitto version my Pi was pulling down was REALLY old. Found a post that pointed my system to a newer version. Solved the version mismatch problem.
Third, adding the "loop_start()" before Connect does not work. The intuitive place is not the right place.
Fourth, one needs to add Sleep() calls before Disconnect, or you will not see all the Callbacks.
Bottom line, while the docs show how to use the APIs, they don't show you how to write a robust program for production use.
I'm still still looking for ways to know when all in-flight publish calls have cleared, rather than using a blind Sleep call, but my original issue is resolved.
When you send mqtt CONNECT packet, you should receive CONNACK response. This response contains the following codes
0 - success, connection accepted
1 - connection refused, bad protocol
2 - refused, client-id error
3 - refused, service unavailable
4 - refused, bad username or password
5 - refused, not authorized
As you can see, your response should be 4. But it is zero. It might be that you broker doesn't check credentials so your connect message is accepted. Client looks fine.
Try to put the publish into a while loop like this
while True:
mqttc.loop()
rc=mqttc.publish(Q_TOPIC, "Hello, World!")
print("Return=" + str(rc))
if sth:
break
mqttc.disconnect()
In this way, the callback function may be executed before the program ends.
Related
I have this mqtt class
class MQTT():
def __init__(self):
# Observer.__init__(self) # DON'T FORGET THIS
self.mqttClient = paho.Client(client_id=constants.MQTT_CLIENT_ID)
self.mqttClient.username_pw_set(username=constants.MQTT_BROKER_USERNAME, password=constants.MQTT_BROKER_PASSWORD)
# assign mqtt event callbacks
self.mqttClient.on_message = self.on_message
self.mqttClient.on_connect = self.on_connect
self.mqttClient.on_disconnect = self.on_disconnect
self.mqttClient.on_socket_close = self.on_disconnect
self.mqttClient.on_log = self.on_log
def on_disconnect(self,client, userdata, rc):
log("MQTT DISCONNECT:",client, userdata, rc)
and then
mqtt = MQTT()
If i run my code it work perfectly but then i have to run some functions when internet connection is lost. So for that i am using on_disconnect and after running code if turn of network nothing happen. I want some call back to run on Internet connection lost. Do we have any ?
on_disconnect is the right callback for this - the question is when it gets called?
If the network connection is lost your client will only notice it at its next attempt of transmission. So if the client is not about to publish something (or acknowledge a subscription which was received just before the connection dropped) the next transmission will be the PINGREQ
By default keepalive is set to 60 - that means your client will send a PINGREQ every 60 seconds if no other control package was sent within this time interval.
So the on_disconnect callback will be called, it just does not happen as fast as you expected. Try a lower keepalive to improve on this
I'm writing a python program that runs on the raspberry pi and connects to the pic-camera. As I'm using MQTT, when the client does not connect by program freezes. is there any way to continue to run the program even if the client does not connect, i.e. I don't receive any data but the camera still runs.
As an example, how do I print x even if the client does not connect?
import time
import paho.mqtt.client as mqtt
import json
def on_connect(client, userdata, rc):
print ("Connected with rc: " + str(rc))
client.subscribe("demo/test1")
def on_message(client, userdata, msg):
data_json = msg.payload
data = json.loads(data_json)
print(data['ant_plus_power'])
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
x = client.connect("118.138.47.99", 1883, 60)
print(x)
client.loop_forever()
Edit: I ran your code on my end and I get a TimeoutError exception after about 30 seconds: "A connection attempt failed because the connected party did not properly respond after a period of time". You need to handle that exception in your code, so that the program continues running even when it fails to connect:
try:
client.connect("118.138.47.99", 1883, 60)
client.loop_forever()
except:
print("failed to connect, moving on")
print("rest of the code here")
This outputs:
failed to connect, moving on
rest of the code here
However, using connect() and loop_forever() isn't suitable for your needs since they are blocking functions (meaning, they block the execution of your code and prevent it from doing anything else). With the code above, if the client successfully connects, print("rest of the code here") will never be reached due to loop_forever().
Instead, try using connect_async() in conjunction with loop_start() to connect in a non-blocking manner (meaning, your program can continue doing other things while attempting to connect in the background):
client.connect_async("118.138.47.99", 1883, 60)
client.loop_start()
print("rest of the code here")
while True:
time.sleep(1)
This outputs rest of the code here and continues running indefinitely (in the infinite while loop) regardless of whether the connection was successful or not.
Note that your on_connect() definition is missing one argument. It should be:
on_connect(client, userdata, flags, rc)
Also it might be a good idea to check the return code of on_connect, and only subscribe if the connection is successful:
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.connected_flag = True # set flag
print("Connected OK")
client.subscribe("demo/test1")
else:
print("Bad connection, RC = ", rc)
mqtt.Client.bad_connection_flag = True
# create flags so you can check the connection status throughout the script
mqtt.Client.connected_flag = False
mqtt.Client.bad_connection_flag = False
See https://www.eclipse.org/paho/clients/python/docs/ and http://www.steves-internet-guide.com/client-connections-python-mqtt/.
For quickly testing a successful connection, you can connect to test.mosquitto.org (see https://test.mosquitto.org/).
I'm new to MQTT and raspberry pi! I'm running a client script and I simply publish a message using another script. I'm using my own Mosquitto broker.
Client:
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe("Example/test")
client.subscribe("Example/topic")
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
if msg.payload == "Hello":
print("Received message #1, do something")
if msg.payload == "World":
print("Received message #2, do something else")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.1.4", 1883)
client.loop_forever()
Publisher:
import paho.mqtt.publish as publish
publish.single("Example/test", "Hello", hostname="192.168.1.4")
publish.single("Example/topic", "World", hostname="192.168.1.4")
print("OK")
That's where things seem weird and those things happen.
1.When I run both the client and the publisher using the python3 command the if statements are skipped and only topic + payload are printed
2.When I run the client using python command and publisher using both python3 and python command everything works fine!
3.When I do all the above, while running the client on virtualenv again the if statements are ignored, in all occasions!
I would like to know if there is a compatibility reason for this to happen, or anything wrong in the code as I must run something more complicated like that on virtualenv!
UPDATE: I found a solution that works fine !
In the client code in the if statement I used
if msg.payload == b"Hello":
and
if msg.payload == b"World":
As far as I know it must have been due to a variable type thing and they didn't match.
I found a solution that works fine !
In the client code in the if statement I used
if msg.payload == b"Hello":
and
if msg.payload == b"World":
As far as I know it must have been due to a variable type thing and they didn't match.
stumbled upon this post while trying Paho mqtt client -
It may be good to note how characters & bytes are treated in Python, as explained in detail in this post.
So, above snippet could also be written as,
if (msg.payload.decode() == 'Hello'):
I have searched both the web and stockexchange for an explanation of this strange behavior I experience when connecting to MQTT. But I couldn't find any similar case and I would like to understand where the issue comes from.
So, I have set up a Mosquitto MQTT broker on my Raspberry Pi to listen on port 1883. I have also set up port forwarding on my router, so I can reach the Pi from outside my home network (though I can reproduce the behavior below with a public server like broker.hivemq.com as well). When I execute the following Node.js script, I can connect to the broker and subscribe and publish messages, it works perfectly:
const mqtt = require('mqtt')
const client = mqtt.connect('tcp://my.address.net:1883')
client.on('connect', () => {
console.log('Connected!')
client.subscribe("chat")
})
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on('line', function(line){
client.publish('chat',line)
})
client.on('message', (topic, message) => {
console.log('>> '+message)
})
However, when I leave out the tcp:// protocol and have
const client = mqtt.connect('my.address.net:1883')
as the host address I don't get a connection, but also no error message. The program just hangs until I terminate it. I don't understand that. Isn't MQTT using TCP by default?
Does that have to do with my client or with my broker? Could it have something to do with my system (OSX)?
Maybe this has nothing to do with it, but a similar behavior I get when I use the Paho MQTT package in Python, and this is actually the more important case for me, because here I don't get it to run at all. This is my code:
import paho.mqtt.client as paho
def on_connect(client, userdata, flags, rc):
print("connected")
client.disconnect()
def on_disconnect(client, userdata, rc):
print("disconnected")
client = paho.Client()
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.connect("my.address.net", 1883)
Using this version without protocol, I don't get any response. The program runs for a second and then terminates. If I use the protocol
client.connect("tcp://my.address.net", 1883)
I get the error
socket.gaierror: [Errno 8] nodename nor servname provided, or not known
I don't know what I am missing.
Can someone explain the difference of stating the tcp:// protocol in the address vs. leaving it out?
This is two question.
For the python part, the python client expects host and port separately as its connection arguments. It is not a URI. Hence no TCP//.
The python program does exactly what you ask - connect, then exit. You'll have to add more code beneath the connect call if you want it to do anything else. What that code is depends on what you want to do, but loop_forever() is a good start.
The other client expects a URI. I believe it uses TCP/SSL to distinguish between plain and encrypted connections.
The 2 libraries are requiring different things.
The NodeJS library is asking for a URI which includes
a schema ("tcp://")
a hostname/IP address ("local host")
a port number (":1883")
The host and port can easily be parsed from this.
Where as the Python library is explicitly asking for 2 separate things
the hostname
the port
These are taken as separate variables.
This just down to different authors choosing different approaches to gather information.
Client details: Paho MQTT Python client obtained from http://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.python.git, SHA id 300fcbdffd62d08f627f94f3074463cfa532ca87
Broker details: RabbitMQ 3.3.4 with the MQTT plugin
In the first scenario everything is being run locally with SSL enabled. I am using the client in a fashion where I have a separate process which publishes messages (> 10000 messages) and does not wait for an acknowledgment between publishes. The error is a result of the self._ssl.read(1) returning a a zero length value from the following code snippet from _packet_read in client.py:
...
if self._in_packet['command'] == 0:
try:
if self._ssl:
command = self._ssl.read(1)
else:
command = self._sock.recv(1)
except socket.error as err:
...
else:
if len(command) == 0:
return 1
command = struct.unpack("!B", command)
self._in_packet['command'] = command[0]
...
and occurs after receiving and parsing 25 acknowledgments from RabbitMQ. After this error I no longer receive anything back from the broker.
If I run with SSL disabled I do not encounter any errors and can successfully receive acknowledgements for all messages sent.
If I run the broker remotely (some location on the internet) I get the same results over SSL. However, when not using SSL I get the read error/disconnect at different intervals but the client is able to recover/reconnect and I receive broker acknowledgements for all messages sent.
Here is the client configuration that I'm using:
...
client = Client('foo')
client.max_inflight_messages_set(65536)
client.on_connect = self.on_connect_callback
client.on_disconnect = self.on_disconnect_callback
client.on_publish = self.on_publish_callback
client.on_message = self.on_message_callback
client.username_pw_set('foo', 'bar')
client.tls_set("ca-cert.pem",
"client-cert.pem",
"client-key.pem")
client.will_set(topic="last_will/foo",
payload=Message().body, qos=1)
client.connect('127.0.0.1', 8883, 30)
client.loop_start()
...
Any idea on what could be causing this and/or suggestions for troubleshooting?
UPDATE 20140828: I was stepping through the loop_read and noticed that I get an empty socket return value after successfully receiving the first full packet (the connection acknowledgment). The call to select that precedes the socket.recv call indicates that there is data ready to be read on the socket. Could this be a socket buffer issue? I'm not sure what the behavior of a Python socket receive buffer (btw I'm running this on OSX) is if it overflows.