Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MQTT loop() blocks script. Works fine with adafruit_io #138

Open
mrmay-dev opened this issue Dec 30, 2022 · 3 comments
Open

MQTT loop() blocks script. Works fine with adafruit_io #138

mrmay-dev opened this issue Dec 30, 2022 · 3 comments
Assignees
Labels
bug Something isn't working

Comments

@mrmay-dev
Copy link

mrmay-dev commented Dec 30, 2022

This happens with CircuitPython 7.3.3 and 8 beta-6 using the mpy-20221230 libraries for both 7 and 8 releases.

The loop() works flawlessly when run used with the AIO client. However, it stalls with plain MQTT.

A simple workaround is to add a timeout (in seconds) to the loop: mqtt_client.loop(timeout = 1)

I expect that while loop() is checking for updates the code below would send messages to the topic. What happens instead, is that the loop() blocks the script until it receives a message and then it allows the rest of the code to run.

To reproduce:

  1. run this on a MatrixPortal M4
  2. notice that the script sends no messages
  3. from another client send a message to test/test-topic
  4. notice that the sent message is processed and the rest of the while True loop runs.

To run the same loop with adafruit_io change the use_MQTT = True variable to False.

''' Bug Reproduction Demo - MatrixPortal M4 '''

import time
import board
import busio
from microcontroller import cpu
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT


start = time.monotonic()

# -----------------------------------------------------------------------------------------
# WIFI Connection
# -----------------------------------------------------------------------------------------

try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)

wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)

print("Connecting to WiFi...")
wifi.connect()
print("Connected!")

# -----------------------------------------------------------------------------------------
# MQTT Connection
# -----------------------------------------------------------------------------------------

use_MQTT = True # set to False to use Adafruit IO
  
if use_MQTT:
    mqtt_topic = "test/test-topic"
    the_broker = "Mosquitto"
    
    # Initialize MQTT interface with the esp interface
    MQTT.set_socket(socket, esp)
    
    # Initialize a new MQTT Client object
    io = MQTT.MQTT(
        broker=secrets["mqtt_broker"],
        port=1883,
        username=secrets["mqtt_user"],
        password=secrets["mqtt_pass"],
        socket_pool=socket,
        is_ssl = False
    )

if not use_MQTT:
    mqtt_topic = "test-topic"
    the_broker = "Adafruit IO"
    
    # Initialize MQTT interface with the esp interface
    MQTT.set_socket(socket, esp)
    
    # Initialize a new MQTT Client object
    mqtt_client = MQTT.MQTT(
        broker="io.adafruit.com",
        username=secrets["aio_username"],
        password=secrets["aio_key"],
    )
    
    # Initialize an Adafruit IO MQTT Client
    io = IO_MQTT(mqtt_client)
    

def new_message(client, topic, message):
    # Method called whenever user/feeds/led has a new value
    print(f'New message on {topic}: {message}')
    
# Connect the callback methods defined above to Adafruit IO
io.on_message = new_message

# Connect to Adafruit IO
print(f"Connecting to {the_broker}...")
io.connect()
print(f'Time to connect: {(time.monotonic() - start):0.1f} seconds')


# -----------------------------------------------------------------------------------------
# MQTT Activity
# -----------------------------------------------------------------------------------------

print(f'\nSubscribing: {mqtt_topic}')
io.subscribe(mqtt_topic)

prv_refresh_time = 0.0
end_all = time.monotonic() + 30 # seconds
print('Listening for messages...')
while True:
    # Poll for incoming messages
    try:
        io.loop()
        # io.loop(timeout=1)  # this timeout allows the loop to continue
    except (ValueError, RuntimeError) as e:
        print("Failed to get data, retrying\n", e)
        wifi.reset()
        wifi.connect()
        io.reconnect()
        continue
    if (time.monotonic() - prv_refresh_time) > 5:
        cpu_temp = cpu.temperature
        cpu_temp = f'{cpu_temp:>0.2f}'
        print(f'Sending: {cpu_temp}')
        io.publish(mqtt_topic, cpu_temp)
        prv_refresh_time = time.monotonic()
    
    if time.monotonic() >= end_all:
        break

print(f'Unsubscribing: {mqtt_topic}')
io.unsubscribe(mqtt_topic)
print(f'Disconnecting from {the_broker}')
io.disconnect()
print(f'\nTotal Time: {(time.monotonic() - start):0.1f} seconds \nGood bye!')
@vladak
Copy link
Contributor

vladak commented Jan 12, 2023

For the record, #142 proposes to use non-zero timeout for loop() by default.

@brentru brentru self-assigned this Apr 11, 2023
@brentru brentru added the bug Something isn't working label Apr 11, 2023
@brentru
Copy link
Member

brentru commented Apr 11, 2023

Going with #142 - leaving this open until #142 is resolved

@evilworm
Copy link

evilworm commented Aug 28, 2024

This might be a bit old, but I got this issue on CP 9.1.2. Went through the code and it just didn't make any sense to use timeout > 0 for non-blocking loop ... in fact I tried but it would still block.
The solution for me was to patch adafruit_minimqtt.py and place self._sock.setblocking(False) just after a new socket is created self._sock = self._connection_manager.get_socket(...) (around line 494)
now the loop uses default timeout=0 (and socket_timeout=-1 in mqtt init) and it does not block anymore, hope this helps someone...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants