pythonloopswhile-loopmqtttimelapse

MQTT client subscriber : How to break a loop by receiving a new message from a publisher?


Can someone help me on that ? I try to break a timelapse (written as looping a function) on my RPI started with a message "StartScanning", by receiving a "Quit" message. I am sorry for my knowledge on programming, but I am really stuck at this last step! Here the code block of the subscriber ( which is the RPI and its PiCamera )

import time
import paho.mqtt.client as mqtt

from time import sleep
from datetime import datetime, timedelta
from picamera import PiCamera
import subprocess
import pathlib


import socket

camera = PiCamera()

Broker = "192.168.1.100"
Port = 1883

# topics
pub_topic = "DIC/scanning"
sub_topic = "DIC/keyboard"



# on connect
def on_connect(mqttc, obj, flags, reason_code):
    print(f"rc :" + str(reason_code))
    mqttc.subscribe(sub_topic)
    
# on message
def on_message(mqttc, obj, msg):
    print(str(msg.topic) + " " +  str(msg.qos) + " " + str(msg.payload))
        
    if msg.payload.decode() == "StartScanning":
        while True:
             timelapse()
    elif msg.payload.decode() == "Quit" :
        timelapse.hasbeencalled=False
            

#fonction scanning avec timelapse
def timelapse():
    hostName = socket.gethostname()
    pathlib.Path('/home/pi/Desktop/DIC_' + hostName).mkdir(parents=True,exist_ok=True)
    fileName= datetime.now().strftime("%Y%m%d_%H-%M-%S-%f")+".jpg"
    camera.resolution = (2592, 1944) #picture resolution
    camera.capture('/home/pi/Desktop/DIC_' + hostName + '/' + fileName)
    #camera.capture('/home/pi/Desktop/DIC_RPI1/RPI1_' + fileName)
    sleep(1)


#on subscribe
def on_subscribe(mqttc, obj, mid, reason_code_list):
    print("Subscribed: " + str(mid) + " " + str(reason_code_list))
    
    
#on log
def on_log(mqttc, obj, level, string):
    print(string)
    
# on publish
def on_publish(mqttc, obj, mid):
    print(f"risposta inviata con message  id:" + str(mid))
    
# connect MQTT client
mqttc = mqtt.Client() #(mqtt.CallbackAPIVersion.VERSION1)

mqttc.on_subscribe = on_subscribe
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_publish = on_publish

mqttc.connect("192.168.1.100", 1883, 60)
#mqttc.subscribe(sub_topic)

mqttc.loop_forever()

I tried to insert inside the function timelapse() a ' while msg.payload=="StartScanning": ' but it didn't work. I also tried to publish the 2 messages "StartScanning" and "Quit" on two topics and subscribe o them, but since the loop function runs, I can't receive other message from a publisher.


Solution

  • while True (or any long-running code) blocks your code and it can't get messages.

    You have to run timelapse (with while-loop) in separate thread, and it needs queue to get messages which can control while-loop

    def timelapse(queue):
        while True:
            if not queue.empty():
                message = queue.get()
                if message == 'quit':
                    break  # exit `while`-loop and finish thread
    
            # ... your code ...
            sleep(1)
    
    if msg.payload.decode() == "StartScanning":
        global queue # inform function that it has to keep it as global variable
        queue = queue.Queue()
        thread.Thread(targe=timelapse, args=(queue,) ).start()  # it has to be comma in `(queue,)`
       
    if msg.payload.decode() == "Quit":
        queue.put('quit')