This code that was suggested to handle publishing a message every 10 seconds. But how to handle reconnects if needed?
import paho.mqtt as mqtt
import time
mqttc=mqtt.Client("ioana")
mqttc.connect("127.0.0.1" 1883, 60, True)
#mqttc.subscribe("test/", 2) # <- pointless unless you include a subscribe callback
mqttc.loop_start()
while True:
mqttc.publish("test","Hello")
time.sleep(10)# sleep for 10 seconds before next call
The script is the absolute bare bones of what is needed send a MQTT message repeatedly but it will automatically reconnect if disconnected as it stands.
You can have it print a message when it is disconnected and reconnected to track this by modifying it as follows:
import paho.mqtt.client as mqtt
import time
def onDisconnect(client, userdata, rc):
print("disonnected")
def onConnect(client, userdata, rc):
print("connected")
mqttc=mqtt.Client("ioana")
mqttc.on_connect = onConnect
mqttc.on_disconnect = onDisconnect
mqttc.connect("127.0.0.1", port=1883, keepalive=60)
mqttc.loop_start()
while True:
mqttc.publish("test","Hello")
time.sleep(10)# sleep for 10 seconds before next call
EDIT:
To test. If you are using mosquitto as your broker then you will probably have the mosquitto_pub command installed, you can use this to force the python to disconnect by using the same client id.
mosquitto_pub -t test -i 'ioana' -m foo
Related
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).
I am attempting to write a python script (for Raspbian) that sends mqtt message on button pushes, and changes LED's on/off when mqtt messages are received.
I can send no worries, and my script structure on 4 RPi's is the same;
import
set variables
while true:
do stuff endlessly
I however can not get a basic script running for paho to receive in this structure.
I tried to follow the paho guide but can not adapt it to an endless while loop application.
Why can't I receive mqtt messages? I cross check with 2 terminals; mosquito sub & pub, they are definitely been sent/received elsewhere on network.
Edit;
New on_connect callback displays "Connected" every 2 seconds, but the script still doesn't receive/print mqtt messages. It still does send them when I push the button.
Attempted to fire up a different MQTT broker in Docker, made no difference.
Edit3; WORKS! :D Last I test I must not have been awake and had the subscribe line commented out in the on_connect callback.
I also removed the "P1" from the mqtt.Client thing which I copied from a tutorial.
Can happierly confirm, it sends and receives mqtt messages. Now I can integrate into my larger script. Thanks hardlib
New code is this;
import sys
sys.path.append('/usr/local/lib/python2.7/dist-packages/paho/mqtt')
import os
import time
import paho.mqtt.client as mqtt
from gpiozero import Button
from time import sleep
def on_message(client, 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_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe("button")
button = Button(25)
broker_address="192.168.1.10"
client = mqtt.Client()
client.on_message=on_message
client.on_connect=on_connect
client.connect(broker_address, 1883, 60)
#client.subscribe("button")
client.loop_start()
while 1:
if button.is_pressed:
print("button pressed")
client.publish("button","ON",1)
sleep(0.1)
sleep(0.1)
print("*")
The publish call in the main method dosen't send a message to the broker, but returns (0,2). on_publish won't be called.
I am not sure how I should look where the error is. I tried qos=2 and run the main method async but it didn't fixed it. The base connection work, because the on_message is called. The mqtt-server works fine with other projects too.
Has anyone some ideas?
import paho.mqtt.client as mqtt
import subprocess
import shutil
import os
import glob
import logging
from datetime import datetime
import RPi.GPIO as GPIO
import time
from multiprocessing import Pool
GPIO.setmode(GPIO.BCM)
direction = 19
pwm = 26
GPIO.setup(direction, GPIO.OUT)
GPIO.setup(pwm, GPIO.OUT)
pwm = GPIO.PWM(pwm,19000)
speed = 0
pwm.start(speed)
running = False
client = mqtt.Client()
#First: pip3 install paho-mqtt
print("Starte Listener")
def start():
GPIO.output(direction, GPIO.LOW)
for dc in range(10, 40, 1):
speed = dc
pwm.ChangeDutyCycle(speed)
time.sleep(0.25)
def end():
GPIO.output(direction, GPIO.LOW)
for dc in list(reversed(range(0,40,1))):
speed = dc
pwm.ChangeDutyCycle(speed)
time.sleep(0.5)
def main(mqttClient):
print("Started Spinning")
running = True
start()
print(mqttClient.publish("scanner","shoot"))
print("Waiting 124 seconds")
time.sleep(124)
print("ending spinning")
end()
print("finished spinning")
running = False
def on_connect2(client, userdata, flags, rc):
client.subscribe("scanner",2)
print("Connected "+str(rc))
# The callback for when a PUBLISH message is received from the server.
def on_message(mqttClient, userdata, msg):
print("Recived message: "+str(msg.payload,'UTF-8'))
if(str(msg.payload,'UTF-8') == "spin" and running == False):
main(mqttClient)
def disconnected():
print("Disconneted")
def on_publish(self, client, userdata, mid):
print("onPublish")
print(client,userdata,mid)
logger = logging.getLogger(__name__)
client.enable_logger(logger)
client.on_connect = on_connect2
client.on_message = on_message
client.on_publish = on_publish
client.on_disconnect = disconnected
client.username_pw_set("....","....")
client.connect("gx1", 1883,60)
client.loop_forever()
This is because you are blocking the MQTT client thread.
When you call client.loop_forever() it takes over the processes main thread and uses it to handle all the MQTT communication. When a new message is received it is picked up from the network stack by the MQTT client thread and turned into the message object that is then passed to the on_message() callback. This function runs on the client thread.
When you call client.publish() this will do one of 2 things
If the message is QOS 0 and smaller than the network MTU it will publish the message.
If the message is QOS 1 or 2 or larger than the network MTU then it will queue the message to be handled by the client thread.
The problem* in your code is that you are blocking the return from main() by 124 seconds which in turn is blocking the return of the on_message() function so the client thread can not get to publishing your message.
If you want to do things the take a long time or block in the on_message() (or any of the callback functions) you should start a separate thread to run them on.
*In theory your message looks like it should fall into case 1 listed above, but there may be other factors that are causing it to queue)
As a quickfix I created a new Client:
def main(mqttClient):
print("Started Spinning")
running = True
start()
mq = mqtt.Client("shot_idicator")
mq.username_pw_set("...","...")
mq.connect("gx1", 1883,60)
mq.publish("scanner","shoot")
mq.disconnect()
mq = None
print("Waiting 124 seconds")
time.sleep(124)
print("ending spinning")
end()
print("finished spinning")
running = False
Now it works but I think this is not how it should be done.
I have my python script that I connect to my mqtt broker and subscribe to a topic. When I receive the payload "hello" the script executes an action in my case prints "good". What I am trying to do is, when the payload "hello" has been received I am trying to execute an action for 20 seconds and in that time I do not want the script to detect any other payloads i.e. it should unsubscribe from the topic (I thought this was a better way but I am open to suggestions).
I do not want any payloads to be detected in my topic during those 20 seconds delay. In my case (script) what happens is that in those 20 seconds delay the script is still subscribed to the topic and after 20 seconds it prints "good" again. I do not want that to happen; instead after the 20 seconds things should resume normally i.e. another "hello" is detected .
Here is my code:
import paho.mqtt.client as mqttClient
import time
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to broker")
global Connected #Use global variable
Connected = True #Signal connection
else:
print("Connection failed")
def on_message(client, userdata, message):
##
if message.payload == "hello":
client.unsubscribe("home/OpenMQTTGateway/433toMQTT")
print("good")
time.sleep(20)
Connected = False #global variable for the state of the connection
broker_address= "192.168.0.6" #Broker address
port = 1883 #Broker port
user = "123456" #Connection username
password = "abcdef" #Connection password
client = mqttClient.Client("Python") #create new instance
client.username_pw_set(user, password=password) #set username and password
client.on_connect= on_connect #attach function to callback
client.on_message= on_message #attach function to callback
client.connect(broker_address,port,60) #connect
client.subscribe("home/OpenMQTTGateway/433toMQTT") #subscribe
client.loop_forever() #then keep listening forever
UPDATE:
#Mike Organek
What i now want is that after it prints good.. I need it to print bad until another hello is published in the topic and it prints good for another 20 seconds so the cycle continues. Hope that makes sense?
You really should not be calling sleep in on_message().
The on_message() callback runs on the MQTT client network thread, while it is running the client can not publish or receive any other messages, this includes keep alive packets.
As a rule you should do no long running tasks or things that might block in the callback. If you need to do long running tasks then you need to do these on a separate thread.
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.