adafruit / adafruit/Adafruit_CircuitPython_MiniMQTT

Wrong _sock_exact_recv implementation used when adafruit_esp32spi.adafruit_esp32spi_socket provided

Open
#165 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
81
Forks
52
PR merge metrics
No merged PRs in 30d

Description

As the title states this library uses the implementation meant for CPython/socket pools instead of the ESP32SPI implementation.

### Steps to reporduce
1. Upload the following script, and install `adafruit_minimqtt` and `adafruit_esp32spi`
```python
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import busio
from digitalio import DigitalInOut
import neopixel
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

### WiFi ###

# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

# If you have an externally connected ESP32:
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset, debug=False)
"""Use below for Most Boards"""
status_light = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.2
) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

### Adafruit IO Setup ###

# Setup a feed named `testfeed` for publishing.
default_topic = 'adafruit/circuitpython/minimqtt/esp32spi/test'

### Code ###

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connected(client, userdata, flags, rc):
# This function will be called when the client is connected
# successfully to the broker.
print("Connected to MQTT broker! Listening for topic changes on %s" % default_topic)
# Subscribe to all changes on the default_topic feed.
client.subscribe(default_topic)


def disconnected(client, userdata, rc):
# This method is called when the client is disconnected
print("Disconnected from MQTT Broker!")


def message(client, topic, message):
"""Method callled when a client's subscribed feed has a new
value.
:param str topic: The topic of the feed with a new value.
:param str message: The new value
"""
print("New message on topic {0}: {1}".format(topic, message))


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

# Initialize MQTT interface with the esp interface
MQTT.set_socket(socket, esp)

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
broker='test.mosquitto.org',
port=1883,
keep_alive=5,
)

# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message

# Connect the client to the MQTT broker.
print("Connecting to MQTT broker...")
mqtt_client.connect()

# Start a blocking message loop...
# NOTE: NO code below this loop will execute
# NOTE: Network reconnection is handled within this loop
while True:
try:
print('Checking for messages...')
mqtt_client.loop(timeout=1)
print('Done...')
except (ValueError, RuntimeError) as e:
print("Failed to get data, retrying\n", e)
wifi.reset()
mqtt_client.reconnect()
continue
time.sleep(1)
```
The above script is a modified version of [examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py](https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/blob/main/examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py). Modified to reduce dependence on secrets.py. Shortened `keep_alive` to exhibit bug sooner. Added non-zero timeout to `.loop()` invocation to prevent infinite blocking loop (separate bug already mentioned in issues, #142).
2. Add WiFi SSID and password to secrets.py.
3. Reset device.

### Actual Results
`MMQTTException: Unable to receive 1 bytes within 5 seconds.`

### Expected Results
`Checking for messages...` followed by `Done...` repeatedly.

### Root cause
It appears as though this library makes an assumption that is the socket has a `recv_into` implementation, that the socket is not an ESP32SPI socket. As of approximately 18 months ago, this is no longer the case. A `recv_into` implementation was added to `adafruit_esp32spi`. [PR 151](https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/pull/151)

### Library versions
* adafruit_minimqtt: 7.3.2
* adafruit_esp32spi: 5.0.5

### boot_out.txt
```
Adafruit CircuitPython 8.0.5 on 2023-03-31; Adafruit PyPortal with samd51j20
Board ID:pyportal
```

### Related
* [adafruit_esp32spi PR 151](https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/pull/151)
* #138
* #148

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py and the MQTT.set_socket call, then trace how the supplied adafruit_esp32spi_socket object receives data during mqtt_client.loop(). Reproduce the reported timeout with the listed library versions and compare the selected receive path with the ESP32SPI socket behavior. Done means the example repeatedly prints “Checking for messages...” followed by “Done...” without the receive timeout.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.