Git Product home page Git Product logo

adafruit_circuitpython_minimqtt's Introduction

Introduction

Documentation Status Discord Build Status Code Style: Black

MQTT Client library for CircuitPython.

Dependencies

This driver depends on:

Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading the Adafruit library and driver bundle.

Installing from PyPI

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally from PyPI. To install for current user:

pip3 install adafruit-circuitpython-minimqtt

To install system-wide (this may be required in some cases):

sudo pip3 install adafruit-circuitpython-minimqtt

To install in a virtual environment in your current project:

mkdir project-name && cd project-name
python3 -m venv .venv
source .venv/bin/activate
pip3 install adafruit-circuitpython-minimqtt

Usage Example

Please check the examples folder for usage examples for this library.

Documentation

API documentation for this library can be found on Read the Docs.

For information on building library documentation, please check out this guide.

Contributing

Contributions are welcome! Please read our Code of Conduct before contributing to help this project stay welcoming.

adafruit_circuitpython_minimqtt's People

Contributors

2bndy5 avatar ajlennon avatar alvarowolfx avatar askpatrickw avatar blitzcitydiy avatar brentru avatar calcut avatar dgriswo avatar dhalbert avatar dlizotte-uwo avatar eason010212 avatar evaherrada avatar fabaff avatar flavio-fernandes avatar foamyguy avatar jerryneedell avatar jinglemansweep avatar justmobilize avatar kattni avatar kevin-tritz avatar ladyada avatar makermelissa avatar phearzero avatar rjauquet avatar siddacious avatar sommersoft avatar tannewt avatar tekktrik avatar vladak avatar zbauman3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

adafruit_circuitpython_minimqtt's Issues

Implement exponential backoff/retry for reconnections

To avoid placing load on MQTT servers like Adafruit IO, Google Cloud IoT Core, or Amazon AWS IOT, reconnections should add an optional flag for implementing an exponential backoff after failed reconnection attempts. This will also help avoid throttling errors from the server.

reconnect() method: https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/blob/master/adafruit_minimqtt.py#L585

See:
AWS Python Client implementation: https://github.com/aws/aws-iot-device-sdk-python#id4
AWS IoT Retries: https://docs.aws.amazon.com/general/latest/gr/api-retries.html
Google Cloud IoT Backoff: https://cloud.google.com/iot/docs/how-tos/exponential-backoff

Error after updating to 5.0.4 - Feathers2

After updating to 5.0.4 on my Feathers2 I am getting the following error, the code was working on 5.0.0.

Traceback (most recent call last):
File "", line 1, in
File "codeold.py", line 86, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 437, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 235, in _get_connect_socket
AttributeError: 'NoneType' object has no attribute 'info'

No way to use key pair on esp32 native e.g. ESP32-S2 feather (not airlift)

Is there a way to connect to a MQTT broker that requires key pairs (like AWS MQTT)

Examples and code seem to only support ESP32 co processors. How can this be done on a ESP32-S3 feather?

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

ssl_context = ssl.create_default_context() 

# how to set these in the SSL context or MQTT for socket wrap?  e.g. AWS thing cert and .pem
# {"key": DEVICE_KEY, "cert": DEVICE_CERT}

client = MQTT.MQTT(broker=aws_endpoint socket_pool=pool, ssl_context=ssl_context)

# in micropython it can be done this way with umqtt.simple
mqtt_client = MQTTClient(aws_client_id, aws_endpoint, ssl=True, ssl_params={'key': DEVICE_KEY, 'cert': DEVICE_CERT})

Incorrect remaining length is calculated when topic contains utf-8-3 or utf-8-4 characters in SUBSCRIBE/PUBLISH packets

Current version of adafruit_minimqtt.py uses len() method to calculate a packet's remaining length, for example:

packet_length += sum(len(topic) for topic, qos in topics)
remaining_length = 2 + len(msg) + len(topic)

As shown above, strings are measured instead of UTF-8 bytes. When a topic only contains English characters, numbers and punctuation marks, the calculation result is correct; However, when it contains some utf-8-3 or utf-8-4 characters (such as Chinese characters) , an incorrect calculation result of remaining length will be obtained. For example, the actual encoding length of "中文" should be 6, while the length calculated by len() is 2. As a result, such packets will be rejected by any MQTT server.

In MQTT protocol, there is no restriction on utf-8-3 or utf-8-4 characters, so I think this issue is probably a bug that needs to be fixed.

At present, in the application I developed, I rewrite the len() method to temporarily solve this problem, but I still hope that the developers of this library can make official bug fixes.

Thank you very much.

ModuleNotFoundError: No module named 'adafruit_minimqtt'

I am trying to use minimqtt on linux (running in a docker container), however I am having trouble installing and importing the module.

I have installed the following packages:

pip3 install adafruit-blinka
pip3 install adafruit-circuitpython-lis3dh
pip3 install adafruit-circuitpython-minimqtt

I can confirm that these have been installed with pip3 list:

Package                         Version
------------------------------- -------
Adafruit-Blinka                 5.8.0
adafruit-circuitpython-lis3dh   5.1.5
adafruit-circuitpython-minimqtt 4.0.1
Adafruit-PlatformDetect         2.22.1
Adafruit-PureIO                 1.1.7
pip                             20.2.4
pyftdi                          0.52.0
pyserial                        3.4
pyusb                           1.1.0
setuptools                      50.3.2
wheel                           0.35.1

However if I try import adafruit_minimqtt.adafruit_minimqtt as MQTT then I get the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'adafruit_minimqtt'

My output of help("modules") is as follows, which shows the other adafruit modules are avilable, however minimqtt is not.

Please wait a moment while I gather a list of all available modules...

Adafruit_PureIO     _tkinter            getopt              resource
__future__          _tracemalloc        getpass             rlcompleter
_abc                _uuid               gettext             runpy
_aix_support        _warnings           glob                sched
_ast                _weakref            graphlib            secrets
_asyncio            _weakrefset         grp                 select
_bisect             _xxsubinterpreters  gzip                selectors
_blake2             _xxtestfuzz         hashlib             serial
_bootlocale         _zoneinfo           heapq               setuptools
_bootsubprocess     abc                 hello               shelve
_bz2                adafruit_blinka     hmac                shlex
_codecs             adafruit_lis3dh     html                shutil
_codecs_cn          adafruit_platformdetect http                signal
_codecs_hk          aifc                idlelib             site
_codecs_iso2022     analogio            imaplib             smtpd
_codecs_jp          antigravity         imghdr              smtplib
_codecs_kr          argparse            imp                 sndhdr
_codecs_tw          array               importlib           socket
_collections        ast                 inspect             socketserver
_collections_abc    asynchat            io                  spwd
_compat_pickle      asyncio             ipaddress           sqlite3
_compression        asyncore            itertools           sre_compile
_contextvars        atexit              json                sre_constants
_crypt              audioop             keyword             sre_parse
_csv                base64              lib2to3             ssl
_ctypes             bdb                 linecache           stat
_ctypes_test        binascii            locale              statistics
_curses             binhex              logging             string
_curses_panel       bisect              lzma                stringprep
_datetime           bitbangio           mailbox             struct
_decimal            board               mailcap             subprocess
_distutils_hack     builtins            marshal             sunau
_elementtree        busio               math                symbol
_functools          bz2                 microcontroller     symtable
_gdbm               cProfile            micropython         sys
_hashlib            calendar            mimetypes           sysconfig
_heapq              cgi                 mmap                syslog
_imp                cgitb               modulefinder        tabnanny
_io                 chunk               multiprocessing     tarfile
_json               cmath               neopixel_write      telnetlib
_locale             cmd                 netrc               tempfile
_lsprof             code                nis                 termios
_lzma               codecs              nntplib             textwrap
_markupbase         codeop              ntpath              this
_md5                collections         nturl2path          threading
_multibytecodec     colorsys            numbers             time
_multiprocessing    compileall          opcode              timeit
_opcode             concurrent          operator            tkinter
_operator           configparser        optparse            token
_osx_support        contextlib          os                  tokenize
_peg_parser         contextvars         ossaudiodev         trace
_pickle             copy                parser              traceback
_posixshmem         copyreg             pathlib             tracemalloc
_posixsubprocess    crypt               pdb                 tty
_py_abc             csv                 pickle              turtle
_pydecimal          ctypes              pickletools         turtledemo
_pyio               curses              pip                 types
_queue              dataclasses         pipes               typing
_random             datetime            pkg_resources       unicodedata
_sha1               dbm                 pkgutil             unittest
_sha256             decimal             platform            urllib
_sha3               difflib             plistlib            usb
_sha512             digitalio           poplib              uu
_signal             dis                 posix               uuid
_sitebuiltins       distutils           posixpath           venv
_socket             doctest             pprint              warnings
_sqlite3            easy_install        profile             wave
_sre                email               pstats              weakref
_ssl                encodings           pty                 webbrowser
_stat               ensurepip           pulseio             wheel
_statistics         enum                pwd                 wsgiref
_string             errno               py_compile          xdrlib
_strptime           faulthandler        pyclbr              xml
_struct             fcntl               pydoc               xmlrpc
_symtable           filecmp             pydoc_data          xxlimited
_sysconfigdata__linux_x86_64-linux-gnu fileinput           pyexpat             xxsubtype
_testbuffer         fnmatch             pyftdi              zipapp
_testcapi           formatter           queue               zipfile
_testimportmultiple fractions           quopri              zipimport
_testinternalcapi   ftplib              random              zlib
_testmultiphase     functools           re                  zoneinfo
_thread             gc                  readline
_threading_local    genericpath         reprlib

I am new to python, so apologies if I am missing something silly - however any assistance would be gratefully received.

Thank you.

Client Hangs during subscribe or unsubscribe

I've been seeing intermittent failures with MQTT, where function calls can hang and never return.
I've been working around it with a watchdog timer to detect the condition and hard reset the device, which is pretty ugly.

I think various things can cause it, but seems to be reproducable with subscribe or unsubscribe
If I run the code below, it typically loops for 10-30s before getting stuck. Sometimes longer.

Potentially another symptom of an underlying problem, see other issues e.g.
#107
#101

I have also tried this with a completely different MQTT broker / service, and had similar results. Quicker to fail if anything.
So I don't think this is down to the Adafruit IO server.

I wondered about throttling, but the time to failure seems too inconsistent

Adafruit CircuitPython 7.3.0 on 2022-05-23; Adafruit Feather ESP32S2 with ESP32S2

Minimal example

import time
import wifi, ssl, socketpool
import adafruit_minimqtt.adafruit_minimqtt as MQTT

from secrets import secrets


def connected_callback(client, userdata, flags, rc):
    print("Connected to AIO")

def subscribe_callback(client, userdata, topic, granted_qos):
    print(f"Subscribed to {topic}")

def unsubscribe_callback(client, userdata, topic, granted_qos):
    print(f"Unsubscribed from {topic}")

def message_callback(client, topic, payload):
    print(f"{topic} = {payload}")

ssid = secrets["ssid"]
password = secrets["password"]
wifi.radio.connect(ssid, password)

pool = socketpool.SocketPool(wifi.radio)

client = MQTT.MQTT(
    broker="io.adafruit.com",
    username=secrets["aio_username"],
    password=secrets["aio_key"],
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

client.on_subscribe = subscribe_callback
client.on_unsubscribe = unsubscribe_callback
client.on_connect = connected_callback
client.on_message = message_callback
client.connect()

start = time.monotonic()
while True:
    print(f'time = {round(time.monotonic() - start, 1)}s')
    client.subscribe('time/hours')
    time.sleep(0.1)
    client.unsubscribe('time/hours')
    time.sleep(0.1)

circuitphyton 5.x and version 3.1.0

I am getting an issue when i update my minimqtt library that i haven't had in the past. the .mpy file gives this error
AttributeError: 'module' object has no attribute 'set_socket'

import adafruit_minimqtt as MQTT
MQTT.set_socket(socket, esp) <-----

if i grab the .py file from GitHub, it works as it should. Do I need to do anything using .mpy file?

Incorrect type in docstring for second argument of MQTT.add_topic_callback(..)

The method signature of MQTT.add_topic_callback(..) on line 367 of adafruit_minimqtt.py is:

    def add_topic_callback(self, mqtt_topic, callback_method):
        """Registers a callback_method for a specific MQTT topic.
        :param str mqtt_topic: MQTT topic identifier.
        :param str callback_method: Name of callback method.
        """

As you can see, the docstring indicates that both method arguments are strings. I believe this is an error though. Shouldn't the second argument callback_method be a method instead of a string?

Publishing or receiving large-ish messages reproducibly fails in 2 ways

This is related to #101 and #81 (I think) but I have a single simple script (attached, zipped for github reasons) that reproduces both problems by adjusting the size of the message. Publishing one particular message gives MemoryError: memory allocation failed, while larger messages than that stop the client from interacting with the broker, resulting in disconnects.

Platform: UnexpectedMaker FeatherS2; No Peripherals
USB: power and serial; LiPo connected

boot_out.txt : Adafruit CircuitPython 7.2.5 on 2022-04-06; FeatherS2 with ESP32S2
Board ID:unexpectedmaker_feathers2

[Just tested on Adafruit MagTag and the behavior is the same as the FeatherS2.]

Working control platform: Same data (up to FRAME size 768) works with same broker CP and lib versions
and client running on Feather M4 Express with AirLift Wing

MQTT Broker: mosquitto version 1.4.15

By varying the size of the message (json encoded) you get different results:
payload = {'FRAME': [25.8683] * SIZE, 'C': 24.3511, 'RH': 16.9207}
SIZE <=313
NO ERROR, payload is published and read back on subscribed topic.

SIZE = 314
Payload is published, but MemoryError is thrown when mqtt_client.loop() is called
Traceback (most recent call last):
File "main.py", line 70, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 805, in loop
File "adafruit_minimqtt/adafruit_minimqtt.py", line 854, in _wait_for_msg
File "adafruit_minimqtt/adafruit_minimqtt.py", line 902, in _sock_exact_recv
MemoryError: memory allocation failed, allocating 4294952961 bytes

SIZE >=315
Broker never receives PUBLISH from client;
client stops sending PINGREQ; Broker disconnects @ timeout.
Client traceback:
Traceback (most recent call last):
File "main.py", line 63, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 802, in loop
File "adafruit_minimqtt/adafruit_minimqtt.py", line 565, in ping
MMQTTException: PINGRESP not returned from broker.

SIZE >=315 [alternate while loop timing]
If one adjusts the while loop to force a second publish before the timeout,
the 2nd time is publish is called, a message of the correct size
gets to broker; broker sends message back, reports socket error and disconnects.
Client traceback:
Traceback (most recent call last):
File "main.py", line 70, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 802, in loop
File "adafruit_minimqtt/adafruit_minimqtt.py", line 556, in ping
OSError: [Errno 104] ECONNRESET
Broker Log (2nd publish):
1649712800: Received PUBLISH from coop_v0 (d0, q0, r0, m0, 'test_zz', ... (2875 bytes))
1649712800: Sending PUBLISH to coop_v0 (d0, q0, r0, m0, 'test_zz', ... (2875 bytes))
1649712800: Socket error on client coop_v0, disconnecting.
main.zip

"keepalive" pings do not keep the connection alive with esp32spi

If I connect to a broker, subscribe to a topic, and no messages arrive from the broker within keep_alive seconds, an exception is thrown and the object disconnects. Pings are not sent to the broker to keep the connection alive.

This happens because loop() is actually blocking in the esp32spi implementation, and so doesn't get a chance to send/receive a ping to reset the object's timestamp.

loop() is blocking because _wait_for_msg() is blocking, which is because _sock_exact_recv() is blocking as currently implemented, I think just in the ESP32SPI implementation without socketpool. (Also note _sock_exact_recv() is defined twice in the file, once around line 312 and again at 894.) It's a bit weird because the timeout sent to loop() is applied to self._sock but then _wait_for_msg overwrites that (if it succeeds) with its own timeout. Either way the code in _sock_exact_recv for ESP32SPI waits for keep_alive seconds and then throws an exception, which defeats loop's ability to ping and reset the timestamp. I don't know what the "right behaviour" should be in this whole setup.

I am using a MatrixPortal with esp32spi.

MemoryError because of too big bytearray

The first 12 lines are normal. Then, for some reason, it stops getting the ISO8601 feed, and the time in-between runs decreases. The time has to be at least .1 seconds, but the loop timeout is .25 seconds, so it should be higher than that if it's still looking for the time value.
The numbers under 1 are the time in-between every loop run, and the very large number is the amount of memory left.

My guess at what's happening is that for whatever reason bufsize becomes so big that it would take up all the memory. I'm also running gc.collect() at the end of every loop run, so it seems the buffer gets made, getting bigger and bigger (I'm assuming that starts happening at line 13 (right after where I wrote unexpected behavior).

EXPECTED BEHAVIOR
2021-06-09 15:55:57.619000-04:00
0.386719
1915456
0.386719
1915456
2021-06-09 15:55:58.619000-04:00
0.496094
1915440
0.382813
1915440
0.386719
1915440
UNEXPECTED BEHAVIOR
0.136719
1915440
0.136719
1915440
0.132813
1915440
0.140625
1915440
0.136719
1915440
0.140625
1915440
0.136719
1915440
0.136719
1915440
0.132813
1915440
0.132813
1915440
0.132813
1915440
0.136719
1915440
Traceback (most recent call last):
  File "code.py", line 278, in <module>
  File "code.py", line 245, in <module>
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 786, in loop
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 835, in _wait_for_msg
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 881, in _sock_exact_recv
MemoryError: memory allocation failed, allocating 4294955011 bytes

CPython example not working with secure broker

This library currently works with CPython sockets connecting to a insecure MQTT broker port (1883) but falls down when attempting to connect to a secure MQTT broker port (8883):

  File "/Users/brentrubell/Desktop/github_brentru/Adafruit_CircuitPython_MiniMQTT/test_cpython.py", line 63, in <module>
    mqtt_client.connect()
  File "/Users/brentrubell/Desktop/github_brentru/Adafruit_CircuitPython_MiniMQTT/adafruit_minimqtt/adafruit_minimqtt.py", line 467, in connect
    self._sock = self._get_socket(self.broker, self.port)
  File "/Users/brentrubell/Desktop/github_brentru/Adafruit_CircuitPython_MiniMQTT/adafruit_minimqtt/adafruit_minimqtt.py", line 284, in _get_socket
    sock = self._ssl_context.wrap_socket(sock, server_hostname=host)
  File "/Users/brentrubell/Desktop/github_brentru/Adafruit_CircuitPython_MiniMQTT/adafruit_minimqtt/adafruit_minimqtt.py", line 114, in wrap_socket
    return _FakeSSLSocket(socket, self._iface.TLS_MODE)
AttributeError: 'NoneType' object has no attribute 'TLS_MODE'```

Invalid broker address

I've been playing with the Adafruit Matrix Portal to display some data from an AWS MQTT broker, and I've been running into an intermittent issue. After some period of time, it can be over 30 minutes or more, MiniMQTT throws an exception in the reconnect loop and dies, and I can't figure out why. Resetting the board makes it work again. Using libraries from adafruit-circuitpython-bundle-6.x-mpy-20201029.

code.txt

Failed to get data, retrying
Failed to send 2 bytes (sent 0)
Traceback (most recent call last):
File "code.py", line 159, in
File "code.py", line 158, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 675, in reconnect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 292, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 292, in connect
MMQTTException: ('Invalid broker address defined.', RuntimeError('Expected 01 but got 00',))

Seemingly no support for TLS

Greetings great programmers!

Kowledge level: Copy-pasting and happy when things dont crash :)

It would be of interest for me to be able to use TLS when connecting to the MQTT broker.
I have tried, but not been able to find any way to do this.
It might just be some obvious way to do this, which I just have not found.

There is an example under the ESPSPI-folder, with credentials, but it did not make me much smarter.

Thanks for all the good programming you guys do!

Getting trapped in publish?

When using a MatrixPortal with CircuitPython, “publish” will intermittently hang indefinitely without returning any error messages rendering the M4 unresponsive.

This is documented in Adafruit Forums.

is_connected raises MMQTTException when not connected, rather that returning False.

I would expect is_connected to return True or False, rather than raise an exception in the False case:

>>> mqtt_client.is_connected()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 978, in is_connected
MMQTTException: MiniMQTT is not connected.

To recreate:

import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from secrets import secrets
pool = socketpool.SocketPool(wifi.radio)
mqtt_client = MQTT.MQTT(
    broker=secrets["broker"],
    port=secrets["port"],
    socket_pool=pool,
)
mqtt_client.is_connected()

Catch MMQTTException

I'm trying to catch MMQTTException errors

while True:
    try:
        # Poll the message queue
        mqtt_client.loop()
    except (ValueError, RuntimeError, MMQTTException) as e:
        print("Failed to get data, retrying\n", e)
      
        mqtt_client.reconnect()
        # continue
    time.sleep(1)

But then I received:
NameError: name 'MMQTTException' is not defined

Is there a better way to catch the exceptions?

Occasional MemoryError during Adafruit IO loop()

Adafruit CircuitPython 7.1.1 on 2022-01-14; Adafruit Feather ESP32S2 with ESP32S2

Circup freeze:
Found device at /Volumes/CIRCUITPY, running CircuitPython 7.1.1.
adafruit_datetime==1.1.6
adafruit_htu31d==1.1.3
adafruit_requests==1.10.6
adafruit_ticks==1.0.1
neopixel==6.2.4
adafruit_bus_device==5.1.4
adafruit_io==5.6.1
adafruit_logging==3.7.5
adafruit_minimqtt==5.2.1
adafruit_register==1.9.8

ERROR Traceback (most recent call last):
  File "mcu.py", line 304, in aio_receive
  File "adafruit_io/adafruit_io.py", line 233, in loop
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 805, in loop
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 854, in _wait_for_msg
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 906, in _sock_exact_recv
MemoryError: memory allocation failed, allocating 4294953983 bytes

I'm running io.loop(timeout=0.1) in mcy.py, line 304
Normally this works fine, but every few minutes I get the above exception.
If I ignore the exception, the code continues to run with no obvious issues, but I wanted to report it anyway.

digging into adafruit_minimqtt.py, and adding a print(bufsize) shows me that the error on line 906 occurs when bufsize is less than zero. e.g. -13313. So I can see how the code ended up trying to allocate ~4GB of memory (2^32-13313)

I also notice that the bufsize immediately before the negative one is large and positive e.g. 13365

I could try harder to make a minimum reproducable example if needed.

lines 216 and 220 both modify the broker string, thus not allowing reconnect to work

proto, dummy, self.broker = self.broker.split("/", 2)

self.broker gets modified during connect, however if you do a reconnect, it tries to parse broker again, however broker was already parsed down (removed the photo, and path). the split fails as broker only contains the host address and there are not enough values to unpack. I would have done a request for pull on this, but didn't know how you wanted to handle this situation.

my broker was set to: http://10.0.0.16

unable to connect to MQTT Broker without using SSL

Hello-
This is my first bug reporting using github, so please correct me if you need more information.

I've been testing out the PyPortal and Matrix Portal to connect with a MQTT broker that is not using an SSL based connection. The basic configuration that I've used is:
client = MQTT.MQTT(
broker='SERVER NAME HERE',
port=1883,
)

and the error that I see is:
Traceback (most recent call last):
File "code.py", line 94, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 437, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 235, in _get_connect_socket
AttributeError: 'NoneType' object has no attribute 'info'

People have posted this error too on the forums, but I don't believe it has been folded into the code. There's a few suggestions on how to resolve the issue.

https://forums.adafruit.com/viewtopic.php?f=60&t=176301&p=858740&hilit=mqtt+ssl#p858740

Please advise if there's anything that I should do to further help / assist with resolving this issue and contributing to this code.

I really appreciate all the wonderful work Adafruit team does for the community. Thank you!

Exception tricky to troubleshoot when defaulted to SSL but broker is not SSL

I'm using a MatrixPortal successfully connected to wifi, on a network with a mosquitto mqtt server, no authentication or security on that. circuitpython 6.1.0, latest library bundle (I think - circup isn't working for me today, but I hand-selected the libraries I think I needed and copied them manually from the bundle), latest nina firmware on esp32, mqtt broker is specified by ip (v4) address in the secrets file.

Chopped down code:

import random
import time
from secrets import secrets

import adafruit_esp32spi.adafruit_esp32spi_socket as socket
import adafruit_minimqtt.adafruit_minimqtt as MQTT
import board
import busio
import neopixel
from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
from adafruit_matrixportal.matrixportal import MatrixPortal

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)
status_light = neopixel.NeoPixel(
    board.NEOPIXEL, 1, brightness=0.2
) 
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

print("Connecting to WiFi...")
wifi.connect()
print("Connected!")
# --- Display setup ---
matrixportal = MatrixPortal( debug=True, esp=esp, external_spi=spi)
# Create a new label with the color and text selected
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(0, (matrixportal.graphics.display.height // 2) - 1),
    scrolling=True,
)

# Static 'Connecting' Text
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(2, (matrixportal.graphics.display.height // 2) - 1),
)

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

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker=secrets["broker"],
    log=True
)
PRESSURE_TOPIC = "office/sensor/pressure/state"

last_pressure = 0
def handle_message(client, topic, message):
    global last_pressure
    message = int(float(message))
    if last_pressure != message:
        scd.ambient_pressure = message
        last_pressure = message

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" % PRESSURE_TOPIC)
    
    matrixportal.set_text("Connected", 1)

    client.subscribe(PRESSURE_TOPIC)

mqtt_client.on_message = handle_message
mqtt_client.on_connect = connected

matrixportal.set_text("Connecting", 1)
mqtt_client.connect()

(Yes, I'm initially just trying to just read a pressure from another sensor in my network, since I'm porting some code from the Clue. Just trying to get my scd30 and pm25 sensor submitting to mqtt and thus influxdb and grafana, in addition to a local display :D )

I get this output:

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

code.py output:
Connecting to WiFi...
Connected!
Init display
Init background
Init text area
Init text area
Creating text area with : Connecting
Traceback (most recent call last):
  File "code.py", line 97, in <module>
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 304, in connect
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 304, in connect
MMQTTException: ('Invalid broker address defined.', RuntimeError('Expected 01 but got 00',))

Line 97 is the mqtt connect line at the end of the cut down code.

Resembles #50 but not sure if it's the same. Hardware reset doesn't seem to help.

(Is this the right place for these kinds of "Seems like this should work, but I might be doing it wrong" questions, or would you prefer forums or discord?)

MiniMQTT loop() blocking and throwing exception if no message is published to a subscribed topic during blocking

I'm trying to use Adafruit IO's CircuitPython library on a Feather M4 Express with the Airlift FeatherWing. I want to be able to use MQTT/IO pub/sub to subscribe to a topic and publish to another topic in a loop, but the mqtt_client.loop() functions seems to block waiting for data to be posted to the subscribed topic once, then the loop call continues as expected. If I don't 'kick' the subscribed topic by publishing data to this topic from the adafruit.io Feeds screen once, the loop() will eventually throw

MMQTTException: Unable to receive 1 bytes within 60 seconds.

I'm using the example code in minimqtt_pub_sub_nonblocking_esp32spi.py (from the latest official v6 release of the CircuitPython IO library) at https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/blob/85d9d846f3fd8f9d3ee8ae354c5c34d2e2970c1a/examples/esp32spi/minimqtt_pub_sub_nonblocking_esp32spi.py

while True:
   mqtt_client.loop()
   mqtt_client.publish(topic, value)
   time.sleep(5)

I tried catching and eating the exception, which works intermittently.

while True:
  try:
    mqtt_client.loop()
  except (MQTT.MMQTTException) as e:
    print(e)
  mqtt_client.publish(topic, value)
  time.sleep(5)

Sometimes it'll work (i.e. exception caught, publish works fine, repeat), other times loop() will throw:

Traceback (most recent call last):
File "code.py", line 142, in <module>
File "code.py", line 140, in <module>
File "adafruit_minimqtt/adafruit_minimqtt.py", line 809, in loop
File "adafruit_minimqtt/adafruit_minimqtt.py", line 569, in ping
File "adafruit_esp32spi/adafruit_esp32spi_socket.py", line 82, in send
File "adafruit_esp32spi/adafruit_esp32spi.py", line 721, in socket_write
RuntimeError: Failed to send 2 bytes (sent 0)

Add topic-specific callback methods

Currently, this library returns a message callback containing a topic and payload if a subscription has been updated.

While one could filter for a specific MQTT topic within the on_message method, it is clunky.

This enhancement would add message callbacks which only trigger on a specific topic match.
Note: Topics must be subscribed to, prior to adding the callback handler.

Proposed methods:
message_callback_add(mqtt_topic, callback_method): Connect a user-defined method to a MQTT subscription topic.

message_callback_remove(mqtt_topic, callback_method): Remove a user-defined method from a specific mqtt topic.

Example Usage in Paho MQTT Python: https://github.com/eclipse/paho.mqtt.python/blob/master/examples/client_sub-multiple-callback.py#L45

import failing - cannot perform relative import

I am attempting to use this library from the CircuitPython library download, version 6, but as soon as I include the import (as shown in the examples), I am receiving an error "cannot perform relative import". Line 48 of adafruit_minimqtt.py is relatively loading the matcher.

I ultimately would like to use this library with the Adafruit Azure IoT library, but it can't find the module (even though it's copied to lib) - the import in the IoT lib doesn't match the minimqtt examples.

I'm not familiar enough with the CircuitPython build tools for building bundles, one day I will attempt this myself - but this issue is a bit more pressing at the moment as I wish to include it in a presentation - any assistance is appreciated. Please let me know if you need anything additional, or if I totally missed the boat on something easy :)

image

Connect problems using github example code on an esp32-s2 feather

Hi @brentru @kattni ,

I have been unable to connect to io.adafruit by mqtt, and I wonder if it is the minimqtt library. I am using the example code below with zero changes.

Code: /examples/minimqtt_simpletest.py

Board: Adafruit Esp32-S2 Feather

Firmware: CircuitPython 7.3.2

secrets = {
'ssid' : '[deleted]',
'password' : '[deleted]',
'aio_username' : '[deleted]',
'aio_key' : '[deleted]',
'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
'broker' : 'io.adafruit.com',
'port' : 1883
}

Output:
Connecting to [home network]
Connected to [home network]!
Attempting to connect to io.adafruit.com
Traceback (most recent call last):
File "code.py", line 97, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 441, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 254, in _get_connect_socket
TypeError: extra positional arguments given

I was having similar errors using the Unexpected Maker ESP32-S2 as well.

Possibly related issues?
#3836
#4394
I should note that the issues on these links may be specific to the Unexpected Maker board. I may post a separate issue on this later.

Client Disconnects from Server on new publish

I'm running an Ethernet Feather Wing and the m4 Express Feather. I'm running the current version of adafruit_minimqtt and adafruit_minimqtt with CircuitPython 6.1.0.

After connecting to a local Mosquitto broker, the client will disconnected after I publish (From another client) to the same subscription. I don't see any error or crashes on the serial monitor, but on my broker log, I see:

"Client Test 2 closed its connection"

After what I assume is the timeout period, I receive "

 MQTT Error ----
 PINGRESP not returned from broker.

" on the serial monitor.

Any thoughts or where I should start to debug?

My test code and current version is below:

PS C:\Users\Jason> python3 -m circup freeze
Found device at E:, running CircuitPython 6.1.0.
A newer version of CircuitPython (6.2.0-beta.0) is available.
adafruit_debouncer==1.3.8
adafruit_displayio_ssd1306==1.2.3
adafruit_logging==1.2.5
adafruit_requests==1.9.4
neopixel==6.0.1
adafruit_bitmap_font==1.3.3
adafruit_bus_device==5.0.4
adafruit_led_animation==2.5.2
adafruit_character_lcd==3.3.6
adafruit_display_text==2.12.1
adafruit_minimqtt==5.0.0
adafruit_wiznet5k==1.9.0`

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
 
import board

import time
import terminalio
import sys
import busio
import digitalio
import adafruit_requests as requests
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_minimqtt.adafruit_minimqtt import MMQTTException
import gc


server_ip = "10.21.2.101"
apid = 1




# For Adafruit Ethernet FeatherWing
cs = digitalio.DigitalInOut(board.D10)
# For Particle Ethernet FeatherWing
# cs = digitalio.DigitalInOut(board.D5)
spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)











print(gc.mem_free())
# Initialize ethernet interface with DHCP
dhcp_retries = 3
while dhcp_retries:
  try:
    eth = WIZNET5K(spi_bus, cs, debug=False)
    
    # text_box(row1_label, 0, "Every time you t!", 18)
    break
  except:
    dhcp_retries = dhcp_retries - 1
if not dhcp_retries:
  print("Failed to get IP address from DHCP")

requests.set_socket(socket, eth)
 
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))

# MQTT Begin 
# Setup a feed named 'onoff' for subscribing to changes
onoff_feed = "/feeds/onoff"
#  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 Adafruit IO! Listening for topic changes on %s" % onoff_feed)
    # Subscribe to all changes on the onoff_feed.
    client.subscribe(onoff_feed)

 
 
def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print("Disconnected from Adafruit IO!")
 
 
def message(client, topic, message):
    # This method is called when a topic the client is subscribed to
    # has a new message.
    print("New message on topic {0}: {1}".format(topic, message))
 
 
# Initialize MQTT interface with the ethernet interface
MQTT.set_socket(socket, eth)
 
# Set up a MiniMQTT Client
# NOTE: We'll need to connect insecurely for ethernet configurations.
mqtt_client = MQTT.MQTT(
    broker=server_ip,
    # username=secrets["aio_username"],
    # password=secrets["aio_key"],
    is_ssl=False,
    client_id='Test2',
    # socket_pool=pool,
    # log=True,
    # keep_alive=15
)
# mqtt_client.set_logger_level("DEBUG")
 
# 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 Adafruit IO...")
try:
    mqtt_client.connect()
       
except (ValueError, RuntimeError, MMQTTException) as e:
    print("Failed to get connect, retrying\n", e)
    
    mqtt_client.reconnect()

while True:
    # Keyboard Input
    try:
        mqtt_client.loop()        # Poll the message queue
        
    except (ValueError, RuntimeError, MMQTTException) as e:
        print("MQTT Error ---- \n", e)
    except (AssertionError,) as e:
        print("Assertion Error ----- \n", e)
   
    time.sleep(1)
     

Reconnect gets stuck in an infinite loop

I'm implementing reconnect logic in my Azure IoT library, and I've hit what looks like an in infinite loop in the reconnect logic.

When I call reconnect, the function call never ends. A quick read of the code shows this:

while self._subscribed_topics:
    feed = self._subscribed_topics.pop()
    self.subscribe(feed)

LInk:

while self._subscribed_topics:

The subscribe method puts the topic onto the _subscribed_topics list, so this list is never empty and the while loop never ends.

This lib doesn't seem to work wtih GCP IOT Core

Original documentation of this issue here

On my FeatherS2, this code executes without error on the Receiving CONNACK packet from broker line. It, however, hangs indefinitely on the google_mqtt.connect() call (second to last line in main.py). I also see valid TLS traffic via packet capture on my router - data coming and going. The final print informing of connection success never executes. Ultimately (following the stack left from crtl-C) the hang is here

Enabling logging has also proven difficult as the adafruit_logging module doesn't have a getLogger method.

Notes:

  • from arcti.ntp import NTP comes from here
  • from arcti.logging import Logger comes from here
  • The private key can be generated using the script found in this tutorial
  • This is related to two other MR's that I have open here and here (this rabbit hole has gone deep)
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
main.py output:
2021/10/12 15:31:23 INFO    : Connecting to The Promised LAN
2021/10/12 15:31:26 INFO    : Connected to The Promised LAN
2021/10/12 15:31:26 INFO    : Setting NTP
2021/10/12 15:31:27 INFO    : Generating JWT - this will take a minute... 
2021/10/12 15:31:54 DEBUG   : Your JWT is: eyJ0.....VQ==
2021/10/12 15:31:54 INFO    : Attempting to connect to mqtt.googleapis.com
2021/10/12 15:31:54 DEBUG   : Attempting to establish MQTT connection...
2021/10/12 15:31:54 INFO    : Establishing a SECURE SSL connection to mqtt.googleapis.com:8883
2021/10/12 15:31:56 DEBUG   : Sending CONNECT to broker...
2021/10/12 15:31:56 DEBUG   : Fixed Header: bytearray(b'\x10\xcc\x04\x00')
Variable Header: bytearray(b'\x04MQTT\x04\xc2\x00<')
2021/10/12 15:31:56 DEBUG   : Receiving CONNACK packet from broker
# main.py

import json
import socketpool
import wifi
import ssl

from settings import secrets

from arcti.ntp import NTP
from arcti.logging import Logger

from adafruit_gc_iot_core import MQTT_API, Cloud_Core
import adafruit_minimqtt.adafruit_minimqtt as MQTT

logger = Logger()

logger.info(f"Connecting to {secrets['WIFI_SSID']}")
wifi.radio.connect(secrets["WIFI_SSID"], secrets["WIFI_SSID_PASSWORD"])
logger.info(f"Connected to {secrets['WIFI_SSID']}")

logger.info("Setting NTP")
n = NTP()
n.update_rtc()

socketPool = socketpool.SocketPool(wifi.radio)

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connect(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    logger.info('Connected to Google Cloud IoT!')
    logger.debug(f'Flags: {flags}\nRC: {rc}')
    # Subscribes to commands/# topic
    google_mqtt.subscribe_to_all_commands()


def disconnect(client, userdata, rc):
    # This method is called when the client disconnects
    # from the broker.
    logger.warn('Disconnected from Google Cloud IoT!')


def subscribe(client, userdata, topic, granted_qos):
    # This method is called when the client subscribes to a new topic.
    logger.info(f'Subscribed to {topic} with QOS level {granted_qos}')


def unsubscribe(client, userdata, topic, pid):
    # This method is called when the client unsubscribes from a topic.
    logger.info(f'Unsubscribed from {topic} with PID {pid}')


def publish(client, userdata, topic, pid):
    # This method is called when the client publishes data to a topic.
    logger.info(f'Published to {topic} with PID {pid}')


def message(client, topic, msg):
    # This method is called when the client receives data from a topic.
    try:
        # Attempt to decode a JSON command
        msg_dict = json.loads(msg)
        logger.debug(msg_dict)

    except TypeError:
        # Non-JSON command, print normally
        logger.error(f"Message from {topic}: {msg}")


# Initialize Google Cloud IoT Core interface
google_iot = Cloud_Core(secrets={
    "project_id": secrets.get('GCS_PROJECT_ID'),
    "cloud_region": secrets.get('GCS_PROJECT_REGION'),
    "registry_id": secrets.get('GCS_IOT_REGISTRY'),
    "device_id": secrets.get('GCS_IOT_DEVICE'),
    "private_key": secrets.get('GCS_PRIVATE_KEY')
})

# JSON-Web-Token (JWT) Generation
logger.info("Generating JWT - this will take a minute... ")
jwt = google_iot.generate_jwt()
logger.debug(f"Your JWT is: {jwt}")

# Set up a new MiniMQTT Client
client = MQTT.MQTT(broker=google_iot.broker,
                   # username=google_iot.username,
                   username="ItDoesntMatter",
                   password=jwt,
                   client_id=google_iot.cid,
                   socket_pool=socketPool,
                   ssl_context=ssl.create_default_context())

client.enable_logger(logger)

# Initialize Google MQTT API Client
google_mqtt = MQTT_API(client)

# Connect callback handlers to Google MQTT Client
google_mqtt.on_connect = connect
google_mqtt.on_disconnect = disconnect
google_mqtt.on_subscribe = subscribe
google_mqtt.on_unsubscribe = unsubscribe
google_mqtt.on_publish = publish
google_mqtt.on_message = message

logger.info(f'Attempting to connect to {client.broker}')
google_mqtt.connect()
logger.info('Connected to IOT Core! :D')
# Pertinent settings.py values
# GCS Creds
    "GCS_PROJECT_ID": " project-id",
    "GCS_PROJECT_REGION": "us-east1",
    "GCS_IOT_REGISTRY": "registry_name",
    "GCS_IOT_DEVICE": "iot_core_device_name",
    "GCS_PRIVATE_KEY": (...)

The ctrl-c stack leaves us here

2021/10/13 21:48:04 DEBUG   : _sock_exact_recv bufsize = 1
Traceback (most recent call last):
  File "main.py", line 107, in <module>
  File "/lib/adafruit_gc_iot_core.py", line 131, in connect
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 505, in connect
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 802, in _wait_for_msg
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 890, in _sock_exact_recv
KeyboardInterrupt: 

MiniMQTT client timeout after successful connection with ESP_ATControl

I am trying to connect to mqtt broker via wifi from Particle Argon device using circuitpython minimqtt. Wifi setup was done using ESP AT Control as in this example https://github.com/adafruit/Adafruit_CircuitPython_ESP_ATcontrol/blob/master/examples/esp_atcontrol_simpletest.py.
For testing mqtt publish, I am using minimqtt simple test example https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/blob/master/examples/minimqtt_simpletest.py

on client.connect() I can see from mosquito mqtt broker logs that client is connected. However, it times out and disconnects. Most probably because of no CONNACK. I need help.

1595772303: New client connected from xxx.xxx.xxx.xxx as cpyxxx (xx, xxx, u'<user>').
1595772394: Client cpyxxx has exceeded timeout, disconnecting.
1595772394: Socket error on client cpyxxx, disconnecting.
1595772514: Saving in-memory database to /var/lib/mosquitto/mosquitto.db.

Debug logger messages cause runtime error

Debug messages sent to an adafruit logger that try to format a bytearray with a %x format code crash the logger. Happens at line 513 for sure, possibly also line 637.

sample minimqtt_simpltest.py MQTT connect error

I run minimqtt_simpletest.py and get connect issues.

Error observed:
code.py output:
Connecting to AstroACN3
Connected to AstroACN3!
Attempting to connect to io.adafruit.com
Traceback (most recent call last):
File "code.py", line 44, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 437, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 235, in _get_connect_socket
AttributeError: 'NoneType' object has no attribute 'info'

Code done running.

Code.py on the board:

====================================================

import ssl
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from secrets import secrets
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]

print("Connecting to %s" % secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!" % secrets["ssid"])
#
mqtt_topic = secrets["aio_username"] + '/feeds/temperature'

def connect(mqtt_client, userdata, flags, rc):
    print("Connected to MQTT Broker!")
    print("Flags: {0}\n RC: {1}".format(flags, rc))


def disconnect(mqtt_client, userdata, rc):
    print("Disconnected from MQTT Broker!")


def subscribe(mqtt_client, userdata, topic, granted_qos):
    print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))


def unsubscribe(mqtt_client, userdata, topic, pid):
    print("Unsubscribed from {0} with PID {1}".format(topic, pid))


def publish(mqtt_client, userdata, topic, pid):
    print("Published to {0} with PID {1}".format(topic, pid))


def message(client, topic, message):
    print("New message on topic {0}: {1}".format(topic, message))


pool = socketpool.SocketPool(wifi.radio)

mqtt_client = MQTT.MQTT(
    broker=secrets["broker"],
    port=secrets["port"],
    username=secrets["aio_username"],
    password=secrets["aio_key"],
    socket_pool=pool,
    is_ssl=True,
    ssl_context=ssl.create_default_context(),
)

mqtt_client.on_connect = connect
mqtt_client.on_disconnect = disconnect
mqtt_client.on_subscribe = subscribe
mqtt_client.on_unsubscribe = unsubscribe
mqtt_client.on_publish = publish
mqtt_client.on_message = message

print("Attempting to connect to %s" % mqtt_client.broker)
mqtt_client.connect()

print("Subscribing to %s" % mqtt_topic)
mqtt_client.subscribe(mqtt_topic)

print("Publishing to %s" % mqtt_topic)
mqtt_client.publish(mqtt_topic, "Hello Broker!")

print("Unsubscribing from %s" % mqtt_topic)
mqtt_client.unsubscribe(mqtt_topic)

print("Disconnecting from %s" % mqtt_client.broker)
mqtt_client.disconnect()

====================================================

Secrets.py NEARLY:

#This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'AstroACN3',
'password' : 'xxxxxxxx',
'aio_username' : 'gddeen',
'aio_key' : 'deadbeefdeadbeefdeadbeef',
'aio_port' : "8883", #8883 secure 1883 not secure
'aio_feed' : "gddeen/feeds/temperature",
'port' : "8883", #8883 secure 1883 not secure
'broker' : 'io.adafruit.com',
'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
'openweather_token' : 'deadbeef',
'openweather_location' : 'New York City, US',
'openweather_latlon' : [ '40.7143', '-74.006' ],
'station_id' : '8531680',
'timer' : '1800',
'timer_name' : "GrubHub Delivery",
}

================================================

I have a Metro ESP32_s2 Express
lib has:

pi@RPI4D8G:/media/pi/CIRCUITPY $ dir -l lib
total 47
drwxr-xr-x 2 pi pi  1024 Mar 13 13:56 adafruit_bitmap_font
drwxr-xr-x 3 pi pi  1024 Mar 17 17:05 adafruit_bus_device
drwxr-xr-x 2 pi pi  1024 Mar 13 13:57 adafruit_display_shapes
drwxr-xr-x 2 pi pi   512 Mar 13 13:57 adafruit_display_text
-rw-r--r-- 1 pi pi   705 Mar 13 13:59 adafruit_fakerequests.mpy
-rw-r--r-- 1 pi pi  8786 Mar 13 13:59 adafruit_framebuf.mpy
-rw-r--r-- 1 pi pi   896 Mar 13 13:59 adafruit_il0398.mpy
drwxr-xr-x 4 pi pi   512 Mar 13 13:57 adafruit_imageload
drwxr-xr-x 2 pi pi   512 Mar 13 13:57 adafruit_io
drwxr-xr-x 2 pi pi  1024 Mar 10 20:52 adafruit_magtag
drwxr-xr-x 2 pi pi   512 Mar 10 20:52 adafruit_minimqtt
-rw-r--r-- 1 pi pi 11264 Mar 13 13:59 adafruit_miniqr.mpy
drwxr-xr-x 2 pi pi   512 Mar 10 20:52 adafruit_portalbase
-rw-r--r-- 1 pi pi 13124 Mar 13 13:59 adafruit_requests.mpy
-rw-r--r-- 1 pi pi  1891 Mar 13 13:59 neopixel.mpy
-rw-r--r-- 1 pi pi  2901 Mar 13 13:59 simpleio.mpy

======================================================

I'm running 6.2beta
cat boot_out.txt
Adafruit CircuitPython 6.2.0-beta.3 on 2021-03-04; Adafruit MagTag with ESP32S2

======================================================

The board has an e-ink 4.2" display hooked up at EDP_SPI, I5, 6, 7, 8

========================================================

The user, me, has a single feed temperature with a value of 27

I'm available 24 hours a day...

Dev Branch 'SocketPool' object has no attribute 'timeout'

I found this on the ESP32-S2 Metro using the Dev Branch. It only happens occasionally"

Traceback (most recent call last):
  File "code.py", line 140, in <module>
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 862, in loop
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 874, in _wait_for_msg
AttributeError: 'SocketPool' object has no attribute 'timeout'

Wrong semantics for size in socket.recv() causes sporatic failures in message parsing

The code for socket read has a buffer size param that indicates the maximum number of bytes
that can be returned within a single call:

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/fce466bd2bb70ca86b79e5cb36bbaca00afacfd1/adafruit_esp32spi/adafruit_esp32spi_socket.py#L107-L112

Trouble is, MQTT is using that param as if that would be the minimum number of bytes that should be returned.

Also, ping method tries to drain buffer looking for the PINGRESP message and if that is not the message in the
receive buffer, it never reverts the 1 byte it took out of the buffer.

while True:
op = self._wait_for_msg(0.5)
if op == 208:
ping_resp = self._sock.recv(2)
if ping_resp[0] != 0x00:
raise MMQTTException("PINGRESP not returned from broker.")
return

After that point, MQTT broker and client are 'out of sync', causing any future exchanges to be 'out of whack'.
A better approach would be to flag that a ping response is expected, but handle it together with any other MQTT
message via _wait_for_msg

1106.57: DEBUG - KeepAlive period elapsed - requesting a PINGRESP from the server...
1106.57: DEBUG - Sending PINGREQ
Writing: b'\xc0\x00'
1106.63: DEBUG - Checking PINGRESP
ESPSocket: 27 bytes available
Reading 1 bytes from ESP socket with status 4
ESPSocket: 26 bytes available
Reading 1 bytes from ESP socket with status 4
ESPSocket: 25 bytes available
Reading 2 bytes from ESP socket with status 4
ESPSocket: 23 bytes available
Reading 21 bytes from ESP socket with status 4
ESPSocket: 2 bytes available
Reading 2 bytes from ESP socket with status 4
Traceback (most recent call last):
  File "code.py", line 297, in <module>
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 704, in loop
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 407, in ping
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 734, in _wait_for_msg
UnicodeError: 

Missing Type Annotations

There are missing type annotations for some functions in this library.

The typing module does not exist on CircuitPython devices so the import needs to be wrapped in try/except to catch the error for missing import. There is an example of how that is done here:

try:
    from typing import List, Tuple
except ImportError:
    pass

Once imported the typing annotations for the argument type(s), and return type(s) can be added to the function signature. Here is an example of a function that has had this done already:

def wrap_text_to_pixels(
    string: str, max_width: int, font=None, indent0: str = "", indent1: str = ""
) -> List[str]:

If you are new to Git or Github we have a guide about contributing to our projects here: https://learn.adafruit.com/contribute-to-circuitpython-with-git-and-github

There is also a guide that covers our CI utilities and how to run them locally to ensure they will pass in Github Actions here: https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/check-your-code In particular the pages: Sharing docs on ReadTheDocs and Check your code with pre-commit contain the tools to install and commands to run locally to run the checks.

If you are attempting to resolve this issue and need help, you can post a comment on this issue and tag both @FoamyGuy and @kattni or reach out to us on Discord: https://adafru.it/discord in the #circuitpython-dev channel.

The following locations are reported by mypy to be missing type annotations:

  • adafruit_minimqtt/matcher.py:37
  • adafruit_minimqtt/matcher.py:45
  • adafruit_minimqtt/matcher.py:57
  • adafruit_minimqtt/matcher.py:74
  • adafruit_minimqtt/matcher.py:80
  • adafruit_minimqtt/adafruit_minimqtt.py:76
  • adafruit_minimqtt/adafruit_minimqtt.py:91
  • adafruit_minimqtt/adafruit_minimqtt.py:99
  • adafruit_minimqtt/adafruit_minimqtt.py:108
  • adafruit_minimqtt/adafruit_minimqtt.py:111
  • adafruit_minimqtt/adafruit_minimqtt.py:133
  • adafruit_minimqtt/adafruit_minimqtt.py:207
  • adafruit_minimqtt/adafruit_minimqtt.py:283
  • adafruit_minimqtt/adafruit_minimqtt.py:286
  • adafruit_minimqtt/adafruit_minimqtt.py:325
  • adafruit_minimqtt/adafruit_minimqtt.py:333
  • adafruit_minimqtt/adafruit_minimqtt.py:363
  • adafruit_minimqtt/adafruit_minimqtt.py:373
  • adafruit_minimqtt/adafruit_minimqtt.py:396
  • adafruit_minimqtt/adafruit_minimqtt.py:399
  • adafruit_minimqtt/adafruit_minimqtt.py:409
  • adafruit_minimqtt/adafruit_minimqtt.py:422
  • adafruit_minimqtt/adafruit_minimqtt.py:556
  • adafruit_minimqtt/adafruit_minimqtt.py:638
  • adafruit_minimqtt/adafruit_minimqtt.py:699
  • adafruit_minimqtt/adafruit_minimqtt.py:747
  • adafruit_minimqtt/adafruit_minimqtt.py:768
  • adafruit_minimqtt/adafruit_minimqtt.py:791
  • adafruit_minimqtt/adafruit_minimqtt.py:859
  • adafruit_minimqtt/adafruit_minimqtt.py:869
  • adafruit_minimqtt/adafruit_minimqtt.py:912
  • adafruit_minimqtt/adafruit_minimqtt.py:924
  • adafruit_minimqtt/adafruit_minimqtt.py:939
  • adafruit_minimqtt/adafruit_minimqtt.py:959

Broker from IP address

Hi
I use a locally hosted mqtt broker and using paho it is fine to just provide the IP address of the broker (defaults take care of the rest). However using this lib I am getting an error:

# Set up a MiniMQTT Client
client =  MQTT(socket,
               broker = '192.168.1.64',
               port = 1883,
               network_manager = wifi)
.
.
.
MMQTTException: Invalid broker address defined.

I see the broker address must be a string. What is the recommended approach for using an IP address?
Thanks

.encode not available on STM32F05 build -- potential workaround

The STM32F405 build does not have .encode available.
see this discussion adafruit/circuitpython#2433 (comment)

I have a fix for this -- so it works without .encode
do you want a PR for it?

here are the diffs

jerryneedell@Ubuntu-Macmini:~/projects/stm32f405/airlift$ diff adafruit_minimqtt.py ~/projects/adafruit_github/Adafruit_CircuitPython_MiniMQTT/
121c121
<         if self.password is not None and len(bytes(password,"utf-8")) > MQTT_TOPIC_LENGTH_LIMIT:
---
>         if self.password is not None and len(password.encode('utf-8')) > MQTT_TOPIC_LENGTH_LIMIT:
129c129
<             self.client_id = 'cpy{0}{1}'.format(microcontroller.cpu.uid[randint(0, len(microcontroller.cpu.uid)-1)],
---
>             self.client_id = 'cpy{0}{1}'.format(microcontroller.cpu.uid[randint(0, 15)],
355c355
<             msg = bytes(str(msg),"ascii")
---
>             msg = str(msg).encode('ascii')
357c357
<             msg = bytes(str(msg),"utf-8")
---
>             msg = str(msg).encode('utf-8')
690c690
<             self._sock.send(bytes(string, "utf-8"))
---
>             self._sock.send(str.encode(string, 'utf-8'))
705c705
<         elif len(bytes(topic,'utf-8')) > MQTT_TOPIC_LENGTH_LIMIT:
---
>         elif len(topic.encode('utf-8')) > MQTT_TOPIC_LENGTH_LIMIT:

note the change at line 129 -- the STM build microcontroller.cpu.uid is only 12 bytes so this was failing -- I set it to len() to avoid the error

Broker hostname needs to start with http or https otherwise an error is raised

I'm working against the latest version of this with the breaking changes from #21. I've made the required changes to set the socket and changed the call to __init__.

I'm hitting a bug with connecting. When I call connect I get an error:

Traceback (most recent call last):
  File "code.py", line 120, in <module>
  File "azureiotmqtt.py", line 523, in connect
  File "azureiotmqtt.py", line 219, in _loopAssign
  File "azureiotmqtt.py", line 439, in _mqttConnect
  File "azureiotmqtt.py", line 121, in _createMQTTClient
  File "adafruit_minimqtt.py", line 220, in connect
ValueError: need more than 1 values to unpack

Digging into the code I see this at line 215:

try:
    proto, dummy, self.broker, path = self.broker.split("/", 3)
    # replace spaces in path
    path = path.replace(" ", "%20")
except ValueError:
    proto, dummy, self.broker = self.broker.split("/", 2)
    path = ""
if proto == "http:":
    self.port = MQTT_TCP_PORT
elif proto == "https:":
    self.port = MQTT_TLS_PORT
else:
    raise ValueError("Unsupported protocol: " + proto)

It looks like the code is checking for http:// or https:// at the start of the broker name, and using this to get the port. This looks wrong to me:

  • I've already set the port in the call to __init__ so there is no need to look it up.
  • I'm using MQTT, not over http so my broker name will never start with http:// or 'https://`, so this code will always throw an error.

My workaround is to manually prepend https:// to my broker name, which is less than ideal.

Looking at the previous version of the code, this wasn't there and there was no checking for the port, assuming the user had set it. I guess this is to work around the user not setting the port.

Blocking loop() function and issues with PINGREQ and timeout=0

Hi all,

Thanks for this - am trying to integrate it into some code and having some problems.

One is that I think a reasonable use-case is to call poll() repeatedly in my main loop.

The poll() code documentation states it is non-blocking (as the PAHO call is for example) but it actually blocks for 1s which is not trivial for an embedded system main loop. I suggest timeout should default to 0.

That said, if I set timeout to zero there is a problem as the poll() call will be called multiple times while a ping request is outstanding and this results in multiple ping requests being made. I see 3-5 at a time with DEBUG logging on.

This is because self._timestamp is being reset after self.ping()

The solution seems to be pretty straightforward which is to reset self._timestamp() before sending the ping()

e.g.

def loop(self, timeout=1):
        """Non-blocking message loop. Use this method to
        check incoming subscription messages.
        Returns response codes of any messages received.
        :param int timeout: Socket timeout, in seconds.
        """
        if self._timestamp == 0:
            self._timestamp = time.monotonic()
        current_time = time.monotonic()
        if current_time - self._timestamp >= self.keep_alive:
            self._timestamp = 0
            # Handle KeepAlive by expecting a PINGREQ/PINGRESP from the server
            if self.logger is not None:
                self.logger.debug(
                    "KeepAlive period elapsed - requesting a PINGRESP from the server..."
                )
            rcs = self.ping()
            return rcs
        self._sock.settimeout(timeout)
        rc = self._wait_for_msg()
        return [rc] if rc else None

Publish fails on large MQTT message sizes

When the length of the message and topic is longer than 125, an IndexError is thrown on the pkt bytearray.

test code:
for i in range(500): print("publishing string of length {}".format(i)) client.publish("testing/message", "a" * i)

result:
Published to testing/message with PID 0 6351.95: DEBUG - Sending PUBACK publishing string of length 111 Traceback (most recent call last): File "code.py", line 121, in <module> File "adafruit_minimqtt.py", line 424, in publish IndexError: bytearray index out of range

This looks like it could be similar to issue #3 in that the payload is unintentionally limited to 127 characters in size.

Dev Branch Error on Unsubscribe on HA MQTT

Here's what I got using Home Assistant's MQTT Broker. It had previously done some successful unsubscribes with a response of bytearray(b'0'), but here's the info for the crash.

New message on topic cmnd/dining-light/POWER: OFF
Unsubscribing from cmnd/dining-light/POWER
Resp:  bytearray(b'\xb0')
Traceback (most recent call last):
  File "code.py", line 127, in <module>
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 862, in loop
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 913, in _wait_for_msg
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 417, in _handle_on_message
  File "code.py", line 89, in message
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 808, in unsubscribe
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 913, in _wait_for_msg
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 417, in _handle_on_message
  File "code.py", line 89, in message
  File "/lib/adafruit_minimqtt/adafruit_minimqtt.py", line 815, in unsubscribe
AssertionError: 

Code done running.

OSError: [Errno 9] EBADF

Folks, i followed the instructions ( https://learn.adafruit.com/mqtt-in-circuitpython/circuitpython-wifi-usage ) to the T and yet i can not figure out what is the reason for the error. I am using FeatherS2 and Thonny (Mu refuses to work on my machine). Here is the full printout of the results

Connecting to Adafruit IO...
Connected to Adafruit IO! Listening for topic changes on efremovru/feeds/onoff
Sending photocell value: 0...
Traceback (most recent call last):
File "", line 86, in
File "adafruit_minimqtt/adafruit_minimqtt.py", line 628, in publish
OSError: [Errno 9] EBADF

line 86 from the "main" code is
mqtt_client.publish(photocell_feed, photocell_val)

which of course goes to the library adafruit_minimqtt
where on line 628 i have

self._sock.send(pub_hdr_fixed)

which obviously is within the "publish" function

any advice will be greatly appreciated. thanks in advance!

Posting to mqtt gets OSErrors

I'm running on a Magtag using "Adafruit CircuitPython 6.2.0-beta.4 on 2021-03-18; Adafruit MagTag with ESP32S2" and libraries from 3/20/2021, except for a modified adafruit_tlv493d which has a pull request in. I'm reading data from the tlv and posting the data to a dedicated raspberry pi broker in a loop without delays. Within a handful of seconds I get OSErrors such as:

Traceback (most recent call last):
  File "code.py", line 99, in <module>
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 616, in publish
OSError: [Errno 11] EAGAIN

and

Traceback (most recent call last):
  File "code.py", line 99, in <module>
  File "adafruit_minimqtt/adafruit_minimqtt.py", line 614, in publish
OSError: [Errno 104] ECONNRESET

I had gotten an error like this posting values once a second overnight, which is why I tried running with no delay.

Also MQTT Explorer on Windows sometimes reports scrambled results after the crash, such as:

{"z": 0.0, "y":06�dryertest/doo

import board
import busio
import wifi
import socketpool
import ssl
import time
import json
import binascii
import struct
import adafruit_minimqtt.adafruit_minimqtt as MQTT
import adafruit_tlv493d

DOOR_MQTT_TOPIC = "dryertest/door"

print("starting up")

# Hardware I2C setup:
#i2c = busio.I2C(board.SCL, board.SDA, frequency = 400000)
i2c = busio.I2C(board.SCL, board.SDA)
tlv = adafruit_tlv493d.TLV493D(i2c)

# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and
# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other
# source control.
# pylint: disable=no-name-in-module,wrong-import-order
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!" % secrets["ssid"])

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker=secrets["mqtt_broker"],
    port=secrets["mqtt_port"],
    username=secrets["mqtt_username"],
    password=secrets["mqtt_password"],
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

print("Attempting to connect to %s" % mqtt_client.broker)
mqtt_client.connect()

readcount = 0

while True:
    door = tlv.magnetic

    door_output = {
    "x" : door[0],
    "y" : door[1],
    "z" : door[2],
    }

    readcount += 1
    if (readcount % 100 == 1) :
        print('.', end='')
    if (readcount % 1000 == 1) :
        print("\n read: : ", readcount)
    mqtt_client.publish(DOOR_MQTT_TOPIC, json.dumps(door_output))

#    time.sleep(1)

loop() doesn't fetch all messages for a topic

Still trying to suss out what is going on here.

At the simplest level, I'm seeing that mqtt_client.loop() usually only fetches a single message from the queue (for a particular topic), even if there are many messages waiting. I assume this is incorrect behaviour

I have seen that sometimes it does a 'catch up' and fetch all of the messages!
Not sure if this is a quirk of AdafruitIO's MQTT broker or this library.

e.g. if I run loop() every 5 seconds, expecting a message from AIO's time/seconds topic

MQTT time: 1649762379
sleep 5
MQTT time: 1649762380
sleep 5
MQTT time: 1649762381
sleep 5
MQTT time: 1649762382
sleep 5
MQTT time: 1649762383
sleep 5
MQTT time: 1649762384
sleep 5
MQTT time: 1649762385
sleep 5
MQTT time: 1649762386
sleep 5
MQTT time: 1649762387
sleep 5
MQTT time: 1649762388
sleep 5
MQTT time: 1649762389
sleep 5
MQTT time: 1649762390 <--------- Here it is very out of sync with the actual time, but then it suddenly does a catch up
sleep 5
MQTT time: 1649762391
MQTT time: 1649762392
MQTT time: 1649762393
MQTT time: 1649762394
....
MQTT time: 1649762436
MQTT time: 1649762437
MQTT time: 1649762438
MQTT time: 1649762439
sleep 5
MQTT time: 1649762440
sleep 5
MQTT time: 1649762441
sleep 5

It looks like if the queue builds up too much, it can cause other issues.
e.g. I'm trying to use MQTT and HTTP requests in the same code, there is an interaction where I see errors like:

Traceback (most recent call last):
  File "ssl_debug.py", line 44, in <module>
  File "adafruit_requests.py", line 752, in get
  File "adafruit_requests.py", line 693, in request
  File "adafruit_requests.py", line 546, in _get_socket
RuntimeError: Sending request failed

Which seem to be associated with the MQTT queue not being cleared

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.