Publish in on_message with Paho Mqtt Client not working - python

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.

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).

paho loop issue in python on raspbian

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("*")

mqtt client does not receive message in case of thread and rest-api

I have a python script, which based on flask and mqtt. The use case is to receive a request via rest-api then to create a new thread which publishes some messages on mosquitto mqtt and expects a response (see subscribe). My problem is that I don't receive any messages. I think it has something to do with the thread, because without the thread it's working very fine..
Do you know what can be the problem?
Thank you in anticipation!
here the code:
from flask import Flask, Response
import paho.mqtt.client as mqtt
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("/mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
print(msg.topic)
client = mqtt.Client(client_id=client_name, clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
client.username_pw_set(mqtt_user, mqtt_password)
client.loop_start()
client.connect(mqtt_host)
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("/mytopic", "")
while True:
check the response from mqtt => but i don't get the response anymore
....
break
finally:
lock.release()
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
app.run(debug=True)
There are a couple of problems with this.
Both the client.connect() and the client.subscribe() calls need iterations of the client network loop to run in order to complete properly.
The network loop needs to run at least once every keep alive period the time after the connection has been made in order to stop the broker disconnecting the client as dead. This means if there is a delay between starting the code and the first REST request then the client will be disconnected.
Better to use the client.start_loop() function to run MQTT client network loop continuously in the background on it's own.
You should also remove the call to client.subscribe() that is outside the on_connect() callback.
EDIT:
As hashed out in the comments/chat the following works. It looks like running the flask app in debug mode does some strange things and creates multiple MQTT clients over and over again with the same client id. This leads to the broker constantly kicking the old ones off so messages never get delivered.
from flask import Flask, Response
import paho.mqtt.client as mqtt
import time
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
sessionId=0
cont=True
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
global cont
print(msg.topic)
cont=False
client = mqtt.Client(client_id="foo", clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
#client.username_pw_set(mqtt_user, mqtt_password)
client.connect("localhost", port=1884)
client.loop_start()
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("mytopic", "foo")
while cont:
time.sleep(5)
print("loop")
finally:
lock.release()
result = "foo"
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
print("started")
app.run()

Python script running in IDLE but not in terminal

There are many instances of this problem here, but all of them list an error when attempting to run the script. When I try to run my script from the terminal, it thinks for about a second, then just goes back to a normal terminal prompt (without an error message).
I am new to Python, so forgive me if I'm simply doing something wrong, but for the life of me . . . this runs perfectly fine when opening/running in IDLE.
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import RPi.GPIO as GPIO
RELAY_PIN_1 = 23
RELAY_PIN_2 = 24
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(RELAY_PIN_1, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(RELAY_PIN_2, GPIO.OUT, initial=GPIO.HIGH)
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe("/amber/1/1")
client.subscribe("/amber/1/2")
def on_message(client, userdata, msg):
if msg.topic == "/amber/1/1":
if msg.payload == b'ON':
GPIO.output(RELAY_PIN_1, GPIO.LOW)
elif msg.payload == b'OFF':
GPIO.output(RELAY_PIN_1, GPIO.HIGH)
if msg.topic == "/amber/1/2":
if msg.payload == b'ON':
GPIO.output(RELAY_PIN_2, GPIO.LOW)
elif msg.payload == b'OFF':
GPIO.output(RELAY_PIN_2, GPIO.HIGH)
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("10.0.0.163", 1883, 60)
client.loop_start()
In case anyone is wondering, I have been trying to run this using the command python mqtt.py (with mqtt.py being the file name). Also, I am using Python2.7 in both IDLE and from the terminal.
When you call loop_start() you start the client on another thread, but right after that the script ends and the process terminates and so nothing happens.
If you want the client to keep running, either sleep() in your main thread after loop_start(), or instead, call loop_forever(), which will loop in the current thread and so your script will not terminate.
See the network loop section in the documentation.

Publish MQTT Message every 10 Seconds...and reconnect if needed

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

Categories