{"id":13500223,"url":"https://github.com/peterhinch/micropython-mqtt","last_synced_at":"2025-05-15T07:05:57.926Z","repository":{"id":22002768,"uuid":"94690722","full_name":"peterhinch/micropython-mqtt","owner":"peterhinch","description":"A 'resilient' asynchronous MQTT driver. Recovers from WiFi and broker outages.","archived":false,"fork":false,"pushed_at":"2025-05-07T11:40:27.000Z","size":3841,"stargazers_count":629,"open_issues_count":19,"forks_count":138,"subscribers_count":33,"default_branch":"master","last_synced_at":"2025-05-07T12:40:24.174Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterhinch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-18T14:00:48.000Z","updated_at":"2025-05-07T11:40:31.000Z","dependencies_parsed_at":"2024-06-07T11:27:23.270Z","dependency_job_id":"807a8070-790e-448b-b36a-1551c25269bc","html_url":"https://github.com/peterhinch/micropython-mqtt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-mqtt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-mqtt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-mqtt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-mqtt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterhinch","download_url":"https://codeload.github.com/peterhinch/micropython-mqtt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254292042,"owners_count":22046426,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T22:00:53.754Z","updated_at":"2025-05-15T07:05:52.906Z","avatar_url":"https://github.com/peterhinch.png","language":"Python","funding_links":[],"categories":["Python","Libraries"],"sub_categories":["Communications"],"readme":"# MicroPython Asynchronous MQTT\n\nMQTT is an easily used networking protocol designed for IOT (internet of\nthings) applications. It is well suited for controlling hardware devices and\nfor reading sensors across a local network or the internet.\n\nIt is a means of communicating between multiple clients. A single server, also\nknown as a broker, manages the network. Clients may include ESP8266, ESP32 and\nPyboard D modules and other networked computers. Typical server hardware is a\nRaspberry Pi or other small Linux machine which may be left running 24/7. An\neffective PC server is [mosquitto](https://mosquitto.org/). Public brokers\n[also exist](https://github.com/mqtt/mqtt.github.io/wiki/public_brokers).\n\nMQTT Packets are passed between clients using a publish/subscribe model. They\nconsist of a topic and a message string. Clients subscribe to a topic and will\nreceive all packets published by any client under that topic.\n\nThe protocol supports three \"quality of service\" (qos) levels. Level 0 offers\nno guarantees. Level 1 ensures that a packet is communicated to the recipient\nbut duplication can occur. Level 2 avoids duplication; it is not supported by\nthe official driver or by this module. Duplicates can readily be handled at the\napplication level.\n\n###### [Main README](../README.md)\n\n### Warning: firmware \u003e= V1.22.0\n\nV1.22.0 included a changed IDF version 5.0.4: on ESPx the package should be\nreplaced with the latest version, otherwise recovery from an outage may not\noccur.\n\n# 1. Contents\n\n 1. [Contents](./README.md#1-contents)  \n  1.1 [Rationale](./README.md#11-rationale)  \n  1.2 [Overview](./README.md#12-overview)  \n  1.3 [Project Status](./README.md#13-project-status)  \n  1.4 [ESP8266 limitations](./README.md#14-esp8266-limitations)  \n  1.5 [ESP32 Issues](./README.md#15-esp32-issues)  \n  1.6 [Pyboard D](./README.md#16-pyboard-d)  \n  1.7 [Arduino Nano RP2040 Connect](./README.md#17-arduino-nano-rp2040-connect)  \n  1.8 [RP2 Pico W](./README.md#18-rp2-pico-w)  \n  1.9 [Limitations](./README.md#19-limitations) Please read this.  \n  1.10 [MQTTv5](./README.md#110-mqttv5) Which version should you use?  \n 2. [Getting started](./README.md#2-getting_started)  \n  2.1 [Program files](./README.md#21-program-files)  \n  2.2 [Installation](./README.md#22-installation)  \n  2.3 [Example Usage](./README.md#23-example-usage) Using the event interface.  \n  2.4 [Usage with callbacks](./README.md#24-usage-with-callbacks)  \n 3. [MQTTClient class](./README.md#3-mqttclient-class)  \n  3.1 [Constructor](./README.md#31-constructor) Describes the MQTT configuration dictionary.  \n  3.2 [Methods](./README.md#32-methods)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.1 [connect](./README.md#321-connect)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.2 [publish](./README.md#322-publish)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.3 [subscribe](./README.md#323-subscribe)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.4 [unsubscribe](./README.md#324-unsubscribe)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.5 [isconnected](./README.md#325-isconnected)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.6 [disconnect](./README.md#326-disconnect)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.7 [close](./README.md#327-close)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.8 [broker_up](./README.md#328-broker_up)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.9 [wan_ok](./README.md#329-wan_ok)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.2.10 [dprint](./README.md#3210-dprint)  \n  3.3 [Class Variables](./README.md#33-class-variables)  \n  3.4 [Module Attribute](./README.md#34-module-attribute)  \n  3.5 [Event based interface](./README.md#35-event-based-interface)  \n  3.6 [MQTTv5 Support](./README.md#36-mqttv5-support)  \n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.6.1 [Configuration and Migration from MQTTv3.1.1](./README.md#361-configuration-and-migration-from-mqttv311)  \n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.6.2 [MQTTv5 Properties](./README.md#362-mqttv5-properties)  \n    \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.6.3 [Unsupported Features](./README.md#363-unsupported-features)  \n 4. [Notes](./README.md#4-notes)  \n  4.1 [Connectivity](./README.md#41-connectivity)  \n  4.2 [Client publications with qos == 1](./README.md#42-client-publications-with-qos-1)  \n  4.3 [Client subscriptions with qos == 1](./README.md#43-client-subscriptions-with-qos-1)  \n  4.4 [Application Design](./README.md#44-application-design)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;4.4.1 [Publication Timeouts](./README.md#441-publication-timeouts)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;4.4.2 [Behaviour on power up](./README.md#442-behaviour-on-power-up)  \n  \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;4.4.3 [Optimisations](./README.md#443-optimisations) RAM use, large incoming messages.  \n  4.5 [Alternative design approach](./README.md#45-alternative-design-approach) Continue the MQTT paradigm into the application.  \n 5. [Non standard applications](./README.md#5-non-standard-applications) Usage in specialist and micropower applications.  \n  5.1 [deepsleep](./README.md#51-deepsleep)  \n  5.2 [lightsleep and disconnect](./README.md#52-lightsleep-and-disconnect)  \n  5.3 [Ultra low power consumption](./README.md#53-ultra-low-power-consumption) For ESP8266 and ESP32.  \n 6. [References](./README.md#6-references)  \n 7. [Connect Error Codes](./README.md#7-connect-error-codes)  \n 8. [Hive MQ](./README.md#8-hive-mq) A secure, free, broker.  \n 9. [The ssl_params dictionary](./README.md#9-the-ssl_params-dictionary) Plus user notes on SSL/TLS.  \n\n## 1.1 Rationale\n\nThe official \"robust\" MQTT client has the following limitations.\n\n 1. It is unable reliably to resume operation after a temporary WiFi outage.\n\n 2. It uses blocking sockets which can cause execution to pause for arbitrary\n periods when accessing a slow broker. It can also block forever in the case of\n qos == 1 publications while it waits for a publication acknowledge which never\n arrives; this can occur on a WiFi network if an outage occurs at this point in\n the sequence.\n\n 3. This blocking behaviour implies limited compatibility with asynchronous\n applications since pending coroutines will not be scheduled for the duration.\n\n 4. Its support for qos == 1 is partial. It does not support retransmission in\n the event of a publication acknowledge being lost. This can occur on a WiFi\n network especially near the limit of range or in the presence of interference.\n\n 5. Its partial qos == 1 support and inability reliably to resume after a WiFi\n outage places a limit on the usable WiFi range. To achieve reliable operation\n a client must be well within range of the access point (AP).\n\n 6. As a synchronous solution it has no mechanism to support the \"keepalive\"\n mechanism of MQTT. This prevents the \"last will\" system from working. It also\n makes subscribe-only clients problematic: the broker has no means of \"knowing\"\n whether the client is still connected.\n\nThis module aims to address these issues, at the cost of significant code size.\nIt has been tested on the following platforms.\n\n 1. ESP8266\n 2. ESP32, ESP32-S2 and ESP32-S3\n 3. Pyboard D\n 4. Arduino Nano Connect\n 5. Raspberry Pi Pico W\n\nThe principal features of this driver are:  \n 1. Non-blocking operation for applications using uasyncio.\n 2. Automatic recovery from WiFi and broker outages.\n 3. True `qos == 1` operation with retransmission.\n 4. Improved WiFi range because of its tolerance of poor connectivity.\n\nIt has the drawback of increased code size which is an issue on the ESP8266.\nRun as frozen bytecode it uses about 50% of the RAM on the ESP8266. On ESP32\nand Pyboard D it may be run as a standard Python module.\n\n## 1.2 Overview\n\nThis module provides a \"resilient\" non-blocking MQTT driver. In this context\n\"resilient\" means capable of reliable operation in the presence of poor WiFi\nconnectivity and dropouts. Clearly during a dropout or broker outage\ncommunication is impossible but when connectivity resumes the driver recovers\ntransparently.\n\nNear the limit of WiFi range communication delays may be incurred owing to\nretransmissions and reconnections but nonblocking behaviour and qos == 1\nintegrity are maintained.\n\nIt supports qos levels 0 and 1. In the case of packets with qos == 1\nretransmissions will occur until the packet has successfully been transferred.\nIf the WiFi fails (e.g. the device moves out out of range of the AP) the\ncoroutine performing the publication will pause until connectivity resumes.\n\nThe driver requires the `asyncio` library and is intended for applications\nthat use it. It uses nonblocking sockets and does not block the scheduler. The\ndesign is based on the official `umqtt` library but it has been substantially\nmodified for resilience and for asynchronous operation.\n\nIt is primarily intended for applications which open a link to the MQTT broker\naiming to maintaining that link indefinitely. Applications which close and\nre-open the link (e.g. for power saving purposes) are subject to limitations\ndetailed in [Non standard applications](./README.md#5-non-standard-applications).\n\nHardware support: Pyboard D, ESP8266, ESP32, ESP32-S3, ESP32-S2, Pico W and\nArduino Nano RP2040 Connect.  \nFirmware support: Official MicroPython firmware V1.19 or later.  \nBroker support: Mosquitto is preferred for its excellent MQTT compliance.  \nProtocol: The module supports a subset of MQTT revision 3.1.1.\n\n## 1.3 Project Status\n\nInitial development was by Peter Hinch. Thanks are due to Kevin Köck for\nproviding and testing a number of bugfixes and enhancements. Also to other\ncontributors, some mentioned below.\n\nNote that in firmware prior to 1.21 `asyncio` was named `uasyncio`.\n\n7 Mar 2025 V0.8.3 Fix unsubscribe bug. Fix decode of large variable byte integers.  \n24 Oct 2024 V0.8.2 Socket reads use pre-allocated buffer for performance.  \n18 Aug 2024 V0.8.1 Reconfigured as a Python package. Bugfix in V5 support.  \n9 Aug 2024 V0.8.0 Partial MQTTv5 support contributed by Bob Veringa.  \n15 Feb 2024 V0.7.2 Make compliant with firmware V1.22.0 and later.  \n12 Nov 2022 V0.7.0 Provide alternative callback-free Event interface.  \n2 Nov 2022 Rename `config.py` to `mqtt_local.py`, doc improvements.  \n8 Aug 2022 V0.6.6 Support unsubscribe (courtesy of Kevin Köck's fork).  \n11 July 2022 V0.6.5 Support RP2 Pico W  \n5 July 2022 V0.6.4 Implement enhancements from Bob Veringa. Fix bug where tasks\ncould fail to be stopped on a brief outage. Subscription callbacks now receive\nbytearrays rather than bytes objects.  \n10 June 2022 Lowpower demo removed as it required an obsolete version of\n`asyncio`. Improved handling of `clean_init` (issue #40).  \n21 May 2022 SSL/TLS ESP8266 support contributed by @SooOverpowered: see\n`tls8266.py`.  \n22 Apr 2022 Support added for Arduino Nano RP2040 Connect. See note below.  \n2 Aug 2021 SSL/TLS on ESP32 has now been confirmed working.\n[Reference](https://github.com/peterhinch/micropython-mqtt/pull/58).\n\n## 1.4 ESP8266 limitations\n\nThe module is too large to compile on the ESP8266 and should be precompiled or\npreferably frozen as bytecode. On the reference board with `mqtt_as` frozen,\nthe demo script `range_ex` reports 27.4K of free RAM while running. The code\ndisables automatic sleep: this reduces reconnects at cost of increased power\nconsumption.\n\nNotes on the Sonoff Basic R3 may be found [here](../sonoff/SONOFF.md).\n\n## 1.5 ESP32 issues\n\nFirmware must now be official firmware as described above. The Loboris port\nhas been abandoned by its author and is no longer supported.\n\n## 1.6 Pyboard D\n\nThe library has been tested successfully with the Pyboard D SF2W and SF6W. In\ntesting it has clocked up eight weeks of continuous runtime and nearly 1M\nmessages without failure or data loss.\n\n## 1.7 Arduino Nano RP2040 Connect\n\nNINA firmware must up to date otherwise MicroPython produces error messages.\nSee\n[this doc](https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-upgrading-nina-firmware).\nReading RSSI seems to break the WiFi link so should be avoided - the\n`range_ex.py` demo disables this on this platform.\n\n## 1.8 RP2 Pico W\n\nThe `mqtt_as` code should be V0.6.5 or later to avoid very slow recovery from\noutages.\n\n## 1.9 Limitations\n\nThe MQTT 3.1 protocol supports extremely long messages. On a microcontroller\nmessage length is limited by available RAM. The actual limit will depend on the\nplatform and user code but it is wise to design on the basis of a maximum of\naround 1KiB.\n\nAvoid unrealistic expectations of performance: latency can be significant,\nespecially when using a TLS connection to a broker located on the internet.\nWith a non-encrypted connection to a local broker it is feasible to use one\nMicroPython client to control another. I haven't measured latency but I would\nguess at ~100ms.\n\nSome platforms - notably ESP32 - are unhelpful when dealing with gross errors\nsuch as incorrect WiFi credentials. Initial connection will only fail after a\none minute timeout. Other platforms enable an immediate bail-out.\n\n## 1.10 MQTTv5\nThe addition of MQTTv5 support does not affect existing applications which will\nrun unchanged. It is expected that most microcontroller users will continue with\nMQTT V3.1.1. The use of MQTTv5 uses additinal RAM (~3KiB) and requires some\nknowledge of the protocol. See [MQTTv5 Support](./README.md#36-mqttv5-support)\nfor more details.\n\n###### [Contents](./README.md#1-contents)\n\n# 2. Getting started\n\n## 2.1 Program files\n\nThe library is configured as a Python package and is installed to an `mqtt_as`\ndirectory. This is typically located on the Python path. Demo scripts are then\nrun with (e.g.):\n```py\n\u003e\u003e\u003e import mqtt_as.range\n```\nThe user-configured `mqtt_local.py` (see below) is located on the Python path.\n\n### Required file\n\n 1. `__init__.py` The main module.\n 2. `mqtt_v5_properties.py` Only required if using MQTTv5.\n\n### Required by demo scripts\n\n 1. `mqtt_local.py` Holds local configuration details such as WiFi credentials.\n Place on Python path (usually `/` or `/lib/`).\n\n### Test/demo scripts\n\nThe first two of these demonstrate the event interface. Others use callbacks.\n\n 1. `range.py` For WiFi range testing. Good general demo.\n 2. `range_ex.py` As above but also publishes RSSI and free RAM. See code\n comments for limitations on Pico W and Arduino nano connect.\n 3. `clean.py` Test/demo program using MQTT Clean Session.\n 4. `unclean.py` Test/demo program with MQTT Clean Session `False`.\n 5. `main.py` Example for auto-starting an application.\n 6. `tls.py` Demo of SSL/TLS connection to a public broker. This runs on a\n Pyboard D. Publishes every 20s and subscribes to same topic. Connection to\n this public broker, though encrypted, is insecure because anyone can\n subscribe.\n 7. `tls8266.py` SSL/TLS connection for ESP8266. Shows how to use keys and\n certificates. For obvious reasons it requires editing to run.\n 8. `sub_unsub.py` Messages with topic `sub_topic` control subscriptions to\n another topic.\n\n Test scripts for MQTTv5:  \n 1. `basic.py` Demo of user properties under MQTTv5.\n\n Bash scripts (may be run on PC to publish periodically):\n 1. `pubtest` Bash script illustrating publication with Mosquitto.\n 2. `pubtest_v5` Bash script illustrates various publication properties.\n\n### Quick install\n\nESP8266: please read [Installation](./README.md#22-installation). On other\nplatforms the main module, demos 1 to 3 and the sample `mqtt_local_example.py`\nmay be installed from a connected PC with :\n```bash\n$ mpremote mip install github:peterhinch/micropython-mqtt\n```\nThe file `mqtt_local_example.py` should be edited for local WiFi authentication\nand renamed `mqtt_local.py`.\n\nFor MQTTv5, demos may be added with\n```bash\n$ mpremote mip install github:peterhinch/micropython-mqtt/mqtt_as/v5\n```\nAn alternative is to use `mip` at the REPL with WiFi connected:\n```python\n\u003e\u003e\u003e import mip\n\u003e\u003e\u003e mip.install(\"github:peterhinch/micropython-mqtt\")\n```\n\nThe Bash scripts `pubtest` and `pubtest_v5` should be copied to the PC.\n\n### Configuration\n\nThe MQTT client is configured using a dictionary. An instance named `config`\nis defined in the [MQTTClient class](./README.md#3-mqttclient-class) and\npopulated with common default values. The user can populate this in any manner.\nThe approach used in the test scripts is as follows. The main `__init__.py`\nmodule instantiates `config` with typical defaults. Then `mqtt_local.py` adds\nlocal settings common to all nodes, e.g. WiFi credentials and broker details.\nFinally the application adds application specific settings like subscriptions.\n\nIn a typical project `mqtt_local.py` will be edited then deployed to all nodes.\n\nThe ESP8266 stores WiFi credentials internally: if the ESP8266 has connected to\nthe LAN prior to running there is no need explicitly to specify these. On other\nplatforms, or to have the capability of running on an ESP8266 which has not\npreviously connected, `mqtt_local.py` should be edited to provide them. This is\na  sample cross-platform file:\n```python\nfrom mqtt_as import config\n\nconfig['server'] = '192.168.0.10'  # Change to suit e.g. 'iot.eclipse.org'\n\n# Required on Pyboard D and ESP32. On ESP8266 these may be omitted (see above).\nconfig['ssid'] = 'my_WiFi_SSID'\nconfig['wifi_pw'] = 'my_password'\n```\n\n###### [Contents](./README.md#1-contents)\n\n## 2.2 Installation\n\nThe module is too large to compile on the ESP8266. It must either be cross\ncompiled or (preferably) built as frozen bytecode: copy `__init__.py` to\n`esp8266/modules` in the source tree, build and deploy. Copy `mqtt_local.py` to\nthe filesystem for ease of making changes.\n\nOn other platforms simply copy the Python source to the filesystem (items 1 and\n2 above as a minimum).\n\nIf an application is to auto-run on power-up it can be necessary to add a short\ndelay in main.py:\n```python\nimport time\ntime.sleep(5)  # Could probably be shorter\nimport range  # Your application\n```\nThis is platform dependent and gives the hardware time to initialise.\n\n## 2.3 Example Usage\n\nThe library offers two alternative ways to handle events such as the arrival of\na message. One uses traditional callbacks. The following uses `Event` instances\nand an asynchronous iterator. If a PC client publishes a message with the topic\n`foo_topic` the topic and message are printed. The code periodically publishes\nan incrementing count under the topic `result`.\n```python\nfrom mqtt_as import MQTTClient, config\nimport asyncio\n\n# Local configuration\nconfig['ssid'] = 'your_network_name'  # Optional on ESP8266\nconfig['wifi_pw'] = 'your_password'\nconfig['server'] = '192.168.0.10'  # Change to suit e.g. 'iot.eclipse.org'\n\nasync def messages(client):  # Respond to incoming messages\n    # If MQTT V5is used this would read\n    # async for topic, msg, retained, properties in client.queue:\n    async for topic, msg, retained in client.queue:\n        print(topic.decode(), msg.decode(), retained)\n\nasync def up(client):  # Respond to connectivity being (re)established\n    while True:\n        await client.up.wait()  # Wait on an Event\n        client.up.clear()\n        await client.subscribe('foo_topic', 1)  # renew subscriptions\n\nasync def main(client):\n    await client.connect()\n    for coroutine in (up, messages):\n        asyncio.create_task(coroutine(client))\n    n = 0\n    while True:\n        await asyncio.sleep(5)\n        print('publish', n)\n        # If WiFi is down the following will pause for the duration.\n        await client.publish('result', '{}'.format(n), qos = 1)\n        n += 1\n\nconfig[\"queue_len\"] = 1  # Use event interface with default queue size\nMQTTClient.DEBUG = True  # Optional: print diagnostic messages\nclient = MQTTClient(config)\ntry:\n    asyncio.run(main(client))\nfinally:\n    client.close()  # Prevent LmacRxBlk:1 errors\n```\nThe code may be tested by running `pubtest` in one terminal and, in another,\n`mosquitto_sub -h 192.168.0.10 -t result` (change the IP address to match your\nbroker).\n\n## 2.4 Usage with callbacks\n\nThe alternative callback-based interface may be run as follows:\n```python\nfrom mqtt_as import MQTTClient, config\nimport asyncio\n\n# Local configuration\nconfig['ssid'] = 'your_network_name'  # Optional on ESP8266\nconfig['wifi_pw'] = 'your_password'\nconfig['server'] = '192.168.0.10'  # Change to suit e.g. 'iot.eclipse.org'\n\ndef callback(topic, msg, retained, properties=None):  # MQTT V5 passes properties\n    print((topic.decode(), msg.decode(), retained))\n\nasync def conn_han(client):\n    await client.subscribe('foo_topic', 1)\n\nasync def main(client):\n    await client.connect()\n    n = 0\n    while True:\n        await asyncio.sleep(5)\n        print('publish', n)\n        # If WiFi is down the following will pause for the duration.\n        await client.publish('result', '{}'.format(n), qos = 1)\n        n += 1\n\nconfig['subs_cb'] = callback\nconfig['connect_coro'] = conn_han\n\nMQTTClient.DEBUG = True  # Optional: print diagnostic messages\nclient = MQTTClient(config)\ntry:\n    asyncio.run(main(client))\nfinally:\n    client.close()  # Prevent LmacRxBlk:1 errors\n```\nAs above, testing is done by running `pubtest` in one terminal and, in another,\n`mosquitto_sub -h 192.168.0.10 -t result` (change the IP address to match your\nbroker).\n\n###### [Contents](./README.md#1-contents)\n\n# 3. MQTTClient class\n\nThe module provides a single class: `MQTTClient`.\n\n## 3.1 Constructor\n\nThis takes a dictionary as argument. The default is `mqtt_as.config` which is\npopulated with default values listed below. A typical application imports this\nand modifies selected entries as required. Entries are as follows (default\nvalues shown in []):\n\n### WiFi Credentials\n\nThese are required for platforms other than ESP8266 where they are optional. If\nthe ESP8266 has previously connected to the required LAN the chip can reconnect\nautomatically. If credentials are provided, an ESP8266 which has no stored\nvalues or which has stored values which don't match any available network will\nattempt to connect to the specified LAN.\n\n'**ssid**' [`None`]  \n'**wifi_pw**' [`None`]  \n\n### MQTT parameters\n\n'**client_id**' [auto-generated unique ID] Must be a `bytes` instance.  \n'**server**' [`None`] Broker IP address (mandatory).  \n'**port**' [`0`] 0 signifies default port (1883 or 8883 for SSL).  \n'**user**' [`''`] MQTT credentials (if required).  \n'**password**' [`''`] If a password is provided a user must also exist.  \n'**keepalive**' [`60`] Period (secs) before broker regards client as having died.  \n'**ping_interval**' [`0`] Period (secs) between broker pings. 0 == use default.  \n'**ssl**' [`False`] If `True` use SSL.  \n'**ssl_params**' [`{}`] See below.  \n'**response_time**' [`10`] Time in which server is expected to respond (s). See note\nbelow.  \n'**clean_init**' [`True`] Clean Session state on initial connection. (Ignored if\nMQTT V5 is in use).  \n'**clean**' [`True`] Clean session state on reconnection. (Known as `Clean\nStart` in MQTT V5).  \n'**max_repubs**' [`4`] Maximum no. of republications before reconnection is\n attempted.  \n'**will**' : [`None`] A list or tuple defining the last will (see below).  \n\n### Interface definition\n\n'**queue_len**' [`0`] If a value \u003e 0 is passed the Event-based interface is\nengaged. This replaces the callbacks defined below with a message queue and\n`Event` instances. See [section 3.5](./README.md#35-event-based-interface).\n\n### Callback based interface  \n\nThis interface is optional. It is retained for compatibility with existing\ncode. In new designs please consider the event based interface which replaces\ncallbacks with a more asyncio-friendly approach.\n\n'**subs_cb**' [a null lambda function] Subscription callback. Runs when a message\nis received whose topic matches a subscription. The callback must take three or\nfour args, `topic`, `message`, `retained` and `properties=None`. The first two\nare `bytes` instances, `retained` is a `bool`, `True` if the message is a\nretained message. `properties` is a dict (or `None`) if MQTT V5 is in use.  \n'**wifi_coro**' [a null coro] A coroutine. Defines a task to run when the network\nstate changes. The coro receives a single `bool` arg being the network state.  \n'**connect_coro**' [a null coro] A coroutine. Defines a task to run when a\nconnection to the broker has been established. This is typically used to\nregister and renew subscriptions. The coro receives a single argument, the\nclient instance.\n\n### MQTT V5 extensions\n\nSee [MQTTv5 Support](./README.md#36-mqttv5-support)  \n'**mqttv5**' [`False`]  \n'**mqttv5_con_props**' [`None`]  \n\n### Notes\n\nThe `response_time` entry works as follows. If a read or write operation times\nout, the connection is presumed dead and the reconnection process begins. If a\n`qos==1` publication is not acknowledged in this period, republication will\noccur. May need extending for slow internet connections.\n\nThe `will` entry defines a publication which the broker will issue if it\ndetermines that the connection has timed out. This is a tuple or list comprising\n[`topic` (string), `msg` (string), `retain` (bool), `qos` (0 or 1)]. If the arg\nis provided all elements are mandatory.\n\nClean sessions: If `clean` is set, messages from the server during an outage\nwill be lost regardless of their qos level.\n\nIf `clean` is `False` messages sent from the server with qos == 1 will be\nreceived when connectivity resumes. This is standard MQTT behaviour (MQTT spec\nsection 3.1.2.4). If the outage is prolonged this can imply a substantial\nbacklog. On the ESP8266 this can cause buffer overflows in the Espressif WiFi\nstack causing `LmacRxBlk:1` errors to appear.\n[see this doc](http://docs.micropython.org/en/latest/esp8266/esp8266/general.html).\n\n`clean_init` should normally be `True`. If `False` the system will attempt to\nrestore a prior session on the first connection. This may result in a large\nbacklog of `qos==1` messages being received, for example if a client is taken\nout of service for a long time. This can have the consequences described above.\nSee MQTT spec 3.1.2.4. This is described further below in\n[section 4.4.2 behaviour on power up](./README.md#442-behaviour-on-power-up).\n\n### SSL/TLS\n\nPopulating the `ssl_params` dictionary is something of a black art. Some sites\nrequire certificates: see [this post](https://forum.micropython.org/viewtopic.php?f=18\u0026t=11906#p65746)\nfor details on how to specify these. See [Hive MQ](./README.md#8-hive-mq) for\ndetails of connecting to a secure, free broker service. This may provide hints\nfor connecting to other TLS brokers.\n\n###### [Contents](./README.md#1-contents)\n\n## 3.2 Methods\n\nNote re data types. Messages and topics may be strings provided that all\ncharacters have ordinal values \u003c= 127 (Unicode single byte characters).\nOtherwise the string `encode` method should be used to convert them to `bytes`\nobjects.\n\n### 3.2.1 connect\n\nAsynchronous.\n\nKeyword only arg:  \n * `quick=False` Setting `quick=True` saves power in some battery applications.\n It does this on (re)connect by skipping the check on WiFi integrity. This check\n is intended for mobile clients that may attempt to reconnect under conditions\n of poor signal strength. In conditions of good signal strength this check may\n be skipped.  \n See [Non standard applications](./README.md#5-non-standard-applications).\n\nConnects to the specified broker. The application should call `connect` once on\nstartup. If this fails (due to WiFi or the broker being unavailable) an\n`OSError` will be raised: see\n[Connect Error Codes](./README.md#7-connect-error-codes). Subsequent\nreconnections after outages are handled automatically.\n\n### 3.2.2 publish\n\nAsynchronous.\n\nIf connectivity is OK the coro will complete immediately, else it will pause\nuntil the WiFi/broker are accessible.\n[Section 4.2](./README.md#42-client-publications-with-qos-1) describes qos == 1\noperation.\n\nArgs:\n 1. `topic` A bytes or bytearray object. Or ASCII string as described above.\n 2. `msg` A bytes or bytearray object.\n 3. `retain=False` Boolean.\n 4. `qos=0` Integer.\n 5. `properties=None` See [MQTTv5 Support](./README.md#36-mqttv5-support).\n\n### 3.2.3 subscribe\n\nAsynchronous.\n\nSubscriptions should be created in the connect coroutine to ensure they are\nre-established after an outage.\n\nThe coro will pause until a `SUBACK` has been received from the broker, if\nnecessary reconnecting to a failed network.\n\nArgs:\n 1. `topic` A bytes or bytearray object. Or ASCII string as described above.\n 2. `qos=0` Integer.\n\nIt is possible to subscribe to multiple topics but there can only be one\nsubscription callback.\n\n### 3.2.4 unsubscribe\n\nAsynchronous.\n\nThe coro will pause until an `UNSUBACK` has been received from the broker, if\nnecessary reconnecting to a failed network.\n\nArg:\n 1. `topic` A bytes or bytearray object. Or ASCII string as described above.\n\nIf there is no subscription in place with the passed topic name the method will\ncomplete normally. This is in accordance with MQTT spec 3.10.4 Response.\n\n### 3.2.5 isconnected\n\nSynchronous. No args.\n\nReturns `True` if connectivity is OK otherwise it returns `False` and schedules\nreconnection attempts.\n\n### 3.2.6 disconnect\n\nAsynchronous. No args.\n\nSends a `DISCONNECT` packet to the broker, closes socket. Disconnection\nsuppresses the Will (MQTT spec. 3.1.2.5). This may be done prior to a power\ndown or deepsleep. For restrictions on the use of this method see\n[lightsleep and disconnect](./README.md#52-lightsleep-and-disconnect).\n\n### 3.2.7 close\n\nSynchronous. No args.\n\nShuts down the WiFi interface and closes the socket. Its main use is in\ndevelopment to prevent ESP8266 `LmacRxBlk:1` failures if an application raises\nan exception or is terminated with ctrl-C (see\n[Example Usage](./README.md#23-example-usage).\n\n### 3.2.8 broker_up\n\nAsynchronous. No args.\n\nUnless data was received in the last second it issues an MQTT ping and waits\nfor a response. If it times out (`response_time` exceeded) with no response it\nreturns `False` otherwise it returns `True`.\n\n### 3.2.9 wan_ok\n\nAsynchronous.\n\nReturns `True` if internet connectivity is available, else `False`. It first\nchecks current WiFi and broker connectivity. If present, it sends a DNS query\nto '8.8.8.8' and checks for a valid response.\n\nThere is a single arg `packet` which is a bytes object being the DNS query. The\ndefault object queries the Google DNS server.\n\nPlease note that this is merely a convenience method. It is not used by the\nclient code and its use is entirely optional.\n\n### 3.2.10 dprint\n\nIf the class variable `DEBUG` is true, debug messages are output via `dprint`.\nThis method can be redefined in a subclass, for example to log debug output to\na file. The method takes an arbitrary number of positional args as per `print`.\n\n## 3.3 Class Variables\n\n 1. `DEBUG` If `True` causes diagnostic messages to be printed.\n 2. `REPUB_COUNT` For debug purposes. Logs the total number of republications\n with the same PID which have occurred since startup.\n\n## 3.4 Module Attribute\n\n 1. `VERSION` A 3-tuple of ints (major, minor, micro) e.g. (0, 5, 0).\n\n## 3.5 Event based interface\n\nThis is invoked by setting `config[\"queue_len\"] = N` where `N \u003e 0`. In this\nmode there are no callbacks. Incoming messages are queued and may be accessed\nwith an asynchronous iterator. The module reports connectivity changes by\nsetting bound `.up` and `.down` `Event` instances. The demos `range.py` and\n`range_ex.py` use this interface. The following code fragments illustrate its use.\n\nReading messages:\n```python\nasync def messages(client):\n    # If MQTT V5 is in use\n    # async for topic, msg, retained, properties in client.queue:\n    async for topic, msg, retained in client.queue:\n        print(f'Topic: \"{topic.decode()}\" Message: \"{msg.decode()}\" Retained: {retained}')\n```\nHandling connect events:\n```python\nasync def up(client):  # (re)connection.\n    while True:\n        await client.up.wait()\n        client.up.clear()\n        print('We are connected to broker.')\n        await client.subscribe('foo_topic', 1)  # Re-subscribe after outage\n```\nOptional outage handler:\n```python\nasync def down(client):\n    while True:\n        await client.down.wait()  # Pause until outage\n        client.down.clear()\n        print('WiFi or broker is down.')\n```\nInitialisation with a default small queue:\n```python\nconfig[\"queue_len\"] = 1\nclient = MQTTClient(config)\n```\nIn applications where incoming messages arrive slowly and the `clean` flag is\n`False`, messages will not accumulate and the queue length can be small. A\nvalue of 1 provides a minimal queue. The queue comes into play when bursts of\nmessages arrive too quickly for the application to process them. This can occur\nif multiple clients independently publish to the same topic: the broker may\nforward them to subscribers at a high rate. Another case is when the `clean`\nflag is `False` and a long wifi outage occurs: when the outage ends there may\nbe a large backlog of messages. Such cases may warrant a larger queue.\n\nIn the event of the queue overflowing, the oldest messages will be discarded.\nThis policy prioritises resilience over the `qos==1` guarantee. The bound\nvariable `client.queue.discards` keeps a running total of lost messages. In\ndevelopment this can help determine the optimum queue length.\n\nIt is possible (though seldom useful) to have multiple tasks waiting on\nmessages. These must yield control after each message to allow the others to be\nscheduled. Messages will be distributed between waiting tasks in a round-robin\nfashion. Multiple instances of this task may be created:\n```python\nasync def messages(client):\n    async for topic, msg, retained in client.queue:\n        await asyncio.sleep(0)  # Allow other instances to be scheduled\n        # handle message\n```\nIn applications RAM is at a premium, in testing the callback-based interface\noffers somewhat (~1.3KiB) lower consumption than the minimal queue case.\n\n###### [Contents](./README.md#1-contents)\n\n## 3.6 MQTTv5 Support\n\nApplication designers should consider whether V5 is appropriate. Relevant\nfactors include:\n* V5 is essential if the client is to subscribe to messages published by a V5\nclient where the massage includes properties which must be received.\n* V5 offers major benefits if the client is to run with clean session `False`.\nThe intention here is to ensure that, where connectivity is unreliable, messages\nare not lost during brief outages. In V3.1.1 this has the unfortunate\nconsequence that, where a prolonged outage occurs, there can be a large backlog.\nV5 messages may be published with a \"message expiry interval\" property, offering\nan option to limit the size of the backlog. This is further enhanced by a\n\"session expiry interval\" set on connection.\n* V5 is essential if there is a requirement to publish messages with properties.\n* Where RAM is limited, V5 adds about 3KiB to the library's usage.\n* V5 enables a request/response protocol where an incoming message can be\nassociated with a specific publication.\n\n### 3.6.1 Configuration and Migration from MQTTv3.1.1\nMQTTv5 is supported and can be configured by setting `mqttv5` to `True` in the\nthe `config` dictionary. The default is `False`. V5 applications should not use\nthe `clean_init` config value - message backlogs should be controlled using\nsession and message expiry intervals. Properties on connect are supported, these\nneed to be passed in the configuration dictionary. See 3.6.2 for more\ninformation on properties, and how to format them.\n```python\nfrom mqtt_as import MQTTClient, config\nconfig['mqttv5'] = True\n\n# Optional: Set the properties for the connection\nconfig['mqttv5_con_props'] = {\n    0x11: 3600,  # Session Expiry Interval\n}\n\n# The rest of the configuration\nclient = MQTTClient(config)\n```\n\nThere are modifications to the API to support MQTTv5 features. The most\nsignificant is the addition of the `properties` argument that is provided as\nan additional argument to both the event and callback-based message handlers.\n\n```python\n# For MQTT 3.1.1 support\ndef callback(topic, msg, retained):\n    print((topic, msg, retained))\n\n# For MQTT 5 and 3.1.1 support\ndef callback(topic, msg, retained, properties=None):\n    print((topic, msg, retained, properties))\n```\n\nAllowing properties as an optional argument allows you to switch between\nMQTT 3.1.1 and MQTT 5 support without changing the callback signature.\n\n```python\nasync def messages(client):\n    async for topic, msg, retained, properties in client.queue:\n        await asyncio.sleep(0)  # Allow other instances to be scheduled\n        # handle message\n```\n\nThe `properties` argument is a dictionary that contains the properties of the\nmessage. The properties are defined in the MQTTv5 specification. If you\ninclude properties in published messages, while using MQTTv3.1.1, the properties\nwill be ignored.\n\n### 3.6.2 MQTTv5 Properties\nProperties are a new and important feature of MQTTv5. They are used to provide\nadditional information about the message, and allow for more advanced features\nsuch as message expiry, user properties, and response information.\n\nIncoming properties are formatted as a dictionary using the property identifier\nas the key. The property identifier is an integer that is defined in the MQTTv5\nspecification, there are no constants defined in the module for these values.\nThe property identifier is defined in the [MQTTv5 specification](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html).\n\nSending properties must be done in the right format. The MQTTv5 specification\nmakes a distinction between binary and text properties. It is important to\nensure that the properties are sent in the correct format. For reference,\nrefer to [section 2.2.2.2](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901029)\nof the MQTTv5 specification.\n\n```python\nproperties = {\n    0x26: {'value': 'test'},       # User Property (UTF-8 string pair)\n    0x09: b'correlation_data',     # Correlation Data (binary)\n    0x08: 'response_topic',        # Response Topic (UTF-8 string)\n    0x02: 60,                      # Message Expiry Interval (integer)\n}\n\nawait client.publish('topic/test', 'message', False, 0, properties=properties)\n```\nIn the following tables of properties types are defined as Python variable\ntypes; \"string\" is a utf8-encoded `str`. The V5 protocol provides for an\noptional request/response exchange. This is described in the V5 specification\n[section 4.10](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901252)\n\n##### Outgoing properties\n\nThe following is a summary of properties relevant to `client.publish()`.\n| Key   | Value       | Destination | Name                     | Meaning              |\n|:------|:------------|:------------|:-------------------------|:---------------------|\n| 0x01  | byte        | subscriber  | payload format indicator | 0=binary 1=utf8  |\n| 0x02  | int         | broker      | Message Expiry Interval  | Lifetime in seconds  |\n| 0x03  | string      | subscriber  | Content Type             | Application defined  |\n| 0x08  | string      | subscriber  | Response Topic           | Request/response     |\n| 0x09  | bytes       | subscriber  | Correlation Data         | Request/response     |\n| 0x23  | int         | broker      | topic alias              |                      |\n| 0x26  | string pair | subscriber  | user property            | Application defined  |\n\nProperties relevant to `client.subscribe()`:\n| Key   | Value       | Name                     | Meaning              |\n|:------|:------------|:-------------------------|:---------------------|\n| 0x0B  | int         | Subscription Identifier  | See below            |\n| 0x26  | string pair | user property            | Application defined  |\n\nThe subscription identifier enables a client application to pass its current\nstate to the broker: responses to that subscription will include that state. See\nspec [section 3.8.4](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901170)\n\nProperties relevant to `client.connect()`. Note that connection properties are\nprovided in the configuration dictionary (`config[mqttv5_con_props]`). All are\ninstructions to the broker. All except 0x11 and 0x22 are somewhat esoteric and\nrequire study of the spec.\n\n| Key   | Value       | Name                           | Meaning              |\n|:------|:------------|:-------------------------------|:---------------------|\n| 0x11  | int         | Session expiry interval (secs) | See below            |\n| 0x17  | byte 0/1    | Request Problem Information    | Spec 3.1.2.11.7      |\n| 0x19  | byte 0/1    | Request Response Information   | Spec 3.1.2.11.6      |\n| 0x21  | int         | Receive Maximum                | Spec section 4.9 etc |\n| 0x22  | int         | Topic Alias Maximum            | Max alias value      |\n| 0x26  | string pair | user property                  | Application defined  |\n| 0x27  | int         | Maximum Packet Size            | Spec 3.2.2.3.6       |\n\nThe Session Expiry Interval defines how long the broker will retain session\nstate after a disconnection. Setting it to zero and setting `clean=True` is\nequivalent to Clean Session status in MQTT3.1.1: publications occurring during\nthe outage will be missed. A long expiry interval enables such messages to be\nreceived, at risk of a large backlog after a prolonged outage.\n\n##### Incoming properties\n\nIncoming message:\n| Key   | Value       | Source     | Name                     | Meaning              |\n|:------|:------------|:-----------|:-------------------------|:---------------------|\n| 0x01  | byte        | publisher  | payload format indicator | 0=binary 1=utf8  |\n| 0x02  | int         | publisher  | Message Expiry Interval  | Lifetime in seconds  |\n| 0x03  | string      | publisher  | Content Type             | Application defined  |\n| 0x08  | string      | publisher  | Response Topic           | Request/response     |\n| 0x09  | bytes       | publisher  | Correlation Data         | Request/response     |\n| 0x0B  | int         | publisher  | Subscription Identifier  |\n| 0x26  | string pair | publisher  | user property            | Application defined  |\n\nOther packets received from the broker may contain properties. Apart from\n`CONNACK` the `mosquitto` broker seems to send these only under error\nconditions. In any event the client prints these if `MQTTClient.DEBUG` is\n`True`. Example packets are `CONNACK`, `PUBACK`, `SUBACK`, `UNSUBACK` and\n`DISCONNECT`.\n\n##### Topic Alias\n\nThe idea behind the topic alias is to reduce outgoing message size. A\npublication is made with the full topic name with the topic alias set to a\nnonzero  integer. Subsequent publications may pass a topic of `\"\"` with the\ntopic alias property set to that integer. It is essential that the broker\nreceives the message setting the alias as it can disconnect if an unknown alias\nis received. A problem also arises if an outage occurs while publishing aliased\nmessages. It seems that the broker does not store aliases after an outage. It is\nthe responsibility of the application to re-establish any aliases on reconnect.\nPlease study\n[the spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113)\nbefore using this feature.\n\n### 3.6.3 Unsupported Features\nIn the interest of keeping the library lightweight and well tested, some\nfeatures of MQTTv5 are not supported.\n1. Enhanced Authentication: [Enhanced Authentication](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901256)\nis a new part of the MQTT specification that allows for more advanced\nauthentication methods. This feature is not supported by the library.\n`AUTH` packet is not implemented and is not handled.\n2. Will Properties: [Will Properties](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901060)\nwith the introduction of properties in MQTTv5 messages can now have properties.\nThis includes the will message. This feature is NOT supported, so properties\ncannot be sent with the will message.\n3. Multiple User Properties: [User Properties](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054)\nthe spec allows for multiple user properties to be sent with a message. In the\ncurrent implementation, only one user property is supported. This applied to\nboth sending and receiving messages. When receiving messages, only the last user\nproperty is returned. If you include more than 1 key-value pair in the user\nproperties dictionary when sending a message, only the first key-value pair will\nbe sent.\n4. Subscription options: [Subscription Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169)\nin MQTTv5 subscription options were introduced (in addition to the QoS level).\nThese options cannot be set when subscribing to a topic. The following options\nare not available:\n    - No Local (NL)\n    - Retain As Published (RAP)\n    - Retain Handling\n5. Not all properties in the `CONNACK` packet are exposed.\n6. Properties on operations other than `CONNECT` and `PUBLISH` are not\nreturned to the user. For more information, see this\n[comment](https://github.com/peterhinch/micropython-mqtt/issues/127#issuecomment-2273742368)\n7. The client does not store incoming Topic Alias properties.\n\nNOTE: Most of these features could be implemented with some effort.\nThese features were not implemented, to keep the current implementation simple\nand reduce the scope of testing required.\n\n###### [Contents](./README.md#1-contents)\n\n# 4. Notes\n\n## 4.1 Connectivity\n\nIf `keepalive` is defined in the constructor call, the broker will assume that\nconnectivity has been lost if no messages have been received in that period.\nThe module attempts to keep the connection open by issuing an MQTT ping up to\nfour times during the keepalive interval. (It pings if the last response from\nthe broker was over 1/4 of the keepalive period). More frequent pings may be\ndesirable to reduce latency of outage detection. This may be done using the\n`ping_interval` configuration option. The point here is that while WiFi\nfailures are detected fast, upstream failure can only be detected by an absence\nof communication from the broker. With a long ping interval, the broker could\nbe unreachable for a long time before the client detects it and initiates a\nreconnection attempt.\n\nIf the broker times out it will issue the \"last will\" publication (if any).\nThis will be received by other clients subscribed to the topic.\n\nIf the client determines that connectivity has been lost it will close the\nsocket and periodically attempt to reconnect until it succeeds.\n\nIn the event of failing connectivity client and server publications with\nqos == 0 may be lost. The behaviour of qos == 1 packets is described below.\n\n## 4.2 Client publications with qos 1\n\nThese behave as follows. The client waits for `response_time`. If no\nacknowledgment has been received it re-publishes it, up to `MAX_REPUBS` times.\nIn the absence of acknowledgment the network is presumed to be down. The client\nreconnects as described above. The publication is then attempted again as a new\nmessage with a different PID. (The new PID proved necessary for Mosquitto to\nrecognise the message).\n\nThis effectively guarantees the reception of a qos == 1 publication, with the\nproviso that the publishing coroutine will block until reception has been\nacknowledged.\n\nIt is permissible for qos == 1 publications to run concurrently with each\npaused pending acknowledgement, however this has implications for resource\nconstrained devices. See [Section 4.4](./README.md#44-application-design).\n\n## 4.3 Client subscriptions with qos 1\n\nWhere the client is subscribed to a topic with qos == 1 and a publication with\nqos == 1 occurs the broker will re-publish until an acknowledgment is\nreceived. If the broker deems that connectivity has failed it waits for the\nclient to reconnect. If the client was configured with `clean` set `True`,\nqos == 1 messages published during the outage will be lost. Otherwise they will\nbe received in quick succession (which can overflow the buffer on an ESP8266\nresulting in `LmacRxBlk:1` messages).\n\n## 4.4 Application design\n\nThe module allows concurrent publications and registration of subscriptions.\n\nWhen using qos == 1 publications on hardware with limited resources such as\nESP8266 it is wise to avoid concurrency by implementing a single publication\ntask. In such cases if a publication queue is required it should be implemented\nby the application.\n\nOn capable hardware it is valid to have multiple coroutines performing qos == 1\npublications asynchronously, but there are implications where connectivity with\nthe broker is slow: an accumulation of tasks waiting on PUBACK packets implies\nconsumption of resources.\n\nThe WiFi and Connect coroutines should run to completion quickly relative to\nthe time required to connect and disconnect from the network. Aim for 2 seconds\nmaximum. Alternatively the Connect coro can run indefinitely so long as it\nterminates if the `isconnected()` method returns `False`.\n\nThe subscription callback will block publications and the reception of further\nsubscribed messages and should therefore be designed for a fast return.\n\n### 4.4.1 Publication Timeouts\n\nA contributor (Kevin Köck) was concerned that, in the case of a connectivity\noutage, a publication might be delayed to the point where it was excessively\noutdated. He wanted to implement a timeout to cancel the publication if an\noutage caused high latency. This is arguably a limitation of MQTT3.1.1 - please\nsee [MQTTv5 Support](./README.md#36-mqttv5-support).\n\nThe following notes are a discussion of workrounds for V3.1.1.\n\nSimple cancellation of a publication task is not recommended because it can\ndisrupt the MQTT protocol. There are several ways to address this:  \n 1. Send a timestamp as part of the publication with subscribers taking\n appropriate action in the case of delayed messages.\n 2. Check connectivity before publishing. This is not absolutely certain as\n connectivity might fail between the check and publication commencing.\n 3. Subclass the `MQTTClient` and acquire the `self.lock` object before issuing\n the cancellation. The `self.lock` object protects a protocol sequence so that\n it cannot be disrupted by another task. This was the method successfully\n adopted and can be seen in [mqtt_as_timeout.py](./mqtt_as_timeout.py).\n\nThis was not included in the library mainly because most use cases are covered\nby use of a timestamp. Other reasons are documented in the code comments.\n\n### 4.4.2 Behaviour on power up\n\nThe library aims to handle connectivity outages transparently, however power\ncycling of the client must be considered at application level. When the\napplication calls the client's `connect` method any failure will cause an\n`OSError` to be raised. This is by design because the action to be taken is\napplication-dependent. A check on WiFi or broker function may be required.\nThere may be a need to fall back to a different network. In other applications\nbrief power outages may be expected: when power resumes the client will simply\nreconnect. If an error occurs the application might wait for a period before\nre-trying.\n\nWhen that initial connection has been achieved, subsequent connections caused\nby network outages are handled transparently to the application.\n\nThe behaviour of \"clean session\" should be considered in this context. If the\n`clean` flag is `False` and a long power outage occurs there may be a large\nbacklog of messages. This can cause problems on resource constrained clients,\nnotably if the client has been taken out of service for a few days. MQTTv5\nhandles this elegantly - please see\n[MQTTv5 Support](./README.md#36-mqttv5-support).\n\nFor those using MQTTv3 this module addresses this by enabling behaviour which\ndiffers between the power up case and the case of a network outage.\n\nThe `clean_init` flag determines behaviour on power up, while `clean` defines\nbehaviour after a connectivity outage. If `clean_init` is `True` and `clean` is\n`False`, on power up prior session state is discarded. The client reconnects\nwith `clean==False`. It reconnects similarly after connectivity outages. Hence,\nafter power up, subscriptions will meet the `qos==1` guarantee for messages\npublished during connectivity outages.\n\nIf both flags are `False` normal non-clean behaviour ensues with the potential\nfor substantial backlogs after long power outages.\n\nIf on power up both flags are `True` the broker will discard session state\nduring connectivity (and hence power) outages. This implies a loss of messages\npublished during connectivity outages(MQTT spec 3.1.2.4 Clean Session).\n\nAlso discussed [here](https://github.com/peterhinch/micropython-mqtt/issues/40).\n\n## 4.5 Alternative design approach\n\nThe following approach extends the MQTT publish-subscribe model into the asyncio\napplication. This offers an alternative way to design an application where\nmessage passing is the principal control mechanism. A\n[message broker](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/DRIVERS.md#9-message-broker)\nis instantiated. Incoming MQTT messages are forwarded to the message broker.\nTasks can subscribe to the `Broker` instance such that a message triggers an\naction. See `async_message.py` for an example. To run the code it is necessary\nto install the asyncio primitives:\n```bash\n$ mpremote mip install \"github:peterhinch/micropython-async/v3/primitives\"\n```\nIn this demo MQTT messages are published to topics \"red_topic\" and \"blue_topic\".\nMessages are \"on\" or \"off\". The receiving `messages` task forwards all incoming\nmessages to the `Broker` instance. The application can subscribe to the topics\nin a variety of ways. In this demo it subscribes a function `led_handler` to the\ntwo topics; this controls the passed LED in response to the message text.\n\nThe following illustrates the way `async_message.py` does this:\n```py\nfrom mqtt_as import MQTTClient\nfrom mqtt_local import wifi_led, blue_led, config\nimport asyncio\nfrom primitives import Broker\n\n# Incoming \"red_topic\" and \"blue_topic\" messages are directed to led_handler\ndef led_handler(topic, message, led):\n    led(message == \"on\")\n\nbroker = Broker()\n# Subscribe led_handler function to the two topics\nbroker.subscribe(\"blue_topic\", led_handler, blue_led)\nbroker.subscribe(\"red_topic\", led_handler, wifi_led)\n\n# All incoming MQTT messages are forwarded to the Broker\nasync def messages(client):\n    async for topic, msg, retained in client.queue:\n        broker.publish(topic.decode(), msg.decode())\n\nconfig[\"queue_len\"] = 1  # Must use event interface\n```\nIt is possible to subscribe objects other than functions, including coroutines,\nmethods, queues, Event instances and user defined class instances.\n\n###### [Contents](./README.md#1-contents)\n\n### 4.4.3 Optimisations\n\nVersion 0.8.2 introduced an optimisation whereby incoming messages are read into\na pre-allocated buffer. This avoids allocation and improves performance. The\nchange was done in a way that avoids breaking existing code. Allocation may be\nfurther reduced by setting two module variables. These are (with defaults):\n\n`IBUFSIZE` = 50\n`MSG_BYTES` = True\n\nAny changes should be made before instantiating the client, e.g.:\n```py\nimport mqtt_as\n\nmqtt_as.IBUFSIZE = 5_000\nclient = MQTTClient(config)\n```\n##### IBUFSIZE\n\nSocket reads are into a pre-allocated buffer. If a message arrives which is too\nlarge, the buffer is extended to accept it. This implies allocation. Consider a\ncase where a long message arrives after a long period where only short messages\nare received. By this time the RAM may have become fragmented, making the large\nallocation fail. If it is known that large messages may arrive, setting a large\nbuffer size at the outset - prior to fragmentation - will avoid this problem.\n\n##### MSG_BYTES\n\nBy default, incoming messages are copied before being made available to the\napplication. This implies allocation. It is done to ensure message integrity\nunder all conditions. If the event interface is used, copying occurs regardless\nof `MSG_BYTES`.\n\nIn the case of the callback interface where `MSG_BYTES` is `False`, a\n`memoryview` of the buffer is passed to the callback, avoiding allocation.\nThe following code is safe where a `memoryview` is returned:\n```py\n# Subscription callback\ndef sub_cb(topic, msg, retained):\n    # Synchronous code handles the message\n```\nHowever this presents a hazard if a `memoryview` is returned:\n```py\n# Subscription callback\ndef sub_cb(topic, msg, retained):\n    asyncio.create_task(process_message(topic, msg))\n```\nA fault arises if another message arrives before `process_message` is complete.\nThe buffer contents will change, causing corruption.\n\n###### [Contents](./README.md#1-contents)\n\n# 5. Non standard applications\n\nNormal operation of `mqtt_as` is based on attempting to keep the link up as\nmuch as possible. This assures minimum latency for subscriptions but implies\npower draw. The `machine` module supports two power saving modes: `lightsleep`\nand `deepsleep`. Currently `asyncio` supports neither of these modes. The\nnotes below may be relevant to any application which deliberately closes and\nre-opens the link to the broker.\n\n## 5.1 deepsleep\n\nMaximum power savings may be achieved by periodically connecting, handling\npublications and pending subscriptions, and entering `deepsleep`. With suitable\nhardware it is possible to produce an MQTT client with very low average power\nconsumption. This is done by keeping the application run time short and using\n`machine.deepsleep` to sleep for a period. When the period expires the board\nresets and `main.py` re-starts the application.\n\nHardware tested was the [UM Feather S2](https://feathers2.io/) available from\n[Adafruit](https://www.adafruit.com/product/4769). My sample consumes only 66μA\nin deepsleep mode. It has a switchable LDO regulator allowing external sensors\nto be powered down when the host is in deepsleep. It also supports battery\noperation via a LiPo cell with USB charging. A Pyboard D with WBUS-DIP28 has\nsimilar properties.\n\nThe test script\n[lptest_min.py](https://github.com/peterhinch/micropython-mqtt/blob/master/mqtt_as/lptest_min.py)\nwakes up periodically and connects to WiFi. It publishes the value from the\nonboard light sensor, and subscribes to the topic \"foo_topic\". Any matching\npublications which occurred during deepsleep are received and revealed by\nflashing the blue LED.\n\nNote that `deepsleep` disables USB. This is inconvenient in development. The\nscript has a test mode in which deepsleep is replaced by `time.sleep` and\n`machine.soft_reset` keeping the USB link active. An alternative approach to\ndebugging is to use a UART with an FTDI adaptor. Such a link can survive a\ndeep sleep.\n\nEach time the client goes into deepsleep it issues `.disconnect()`. This sends\nan MQTT `DISCONNECT` packet to the broker suppressing the last will as per MQTT\nspec para 3.1.2.5. The reasoning is that deepsleep periods are likely to be\nmuch longer than the keepalive time. Using `.disconnect()` ensures that a last\nwill message is only triggered in the event of a failure such as a program\ncrash.\n\nIn applications which close the connection and deepsleep, power consumption may\nbe further reduced by setting the `quick` arg to `.connect`. On connecting or\nre-connecting after an outage a check is made to ensure that WiFi connectivity\nis stable. Quick connection skips this check on initial connection only, saving\nseveral seconds. The reasoning here is that any error in initial connection\nmust be handled by the application. The test script sleeps for `retry` seconds\nbefore re-trying the connection.\n\n## 5.2 lightsleep and disconnect\n\nThe library is not designed for use in cases where the system goes into\nlightsleep. Firstly `asyncio` does not support lightsleep on all platforms -\nnotably on STM where the `ticks_ms` clock (crucial to task scheduling) stops\nfor the duration of lightsleep.\n\nSecondly the library has no mechanism to ensure all tasks are shut down cleanly\nafter issuing `.disconnect`. This calls into question any application that\nissues `.disconnect` and then attempts to reconnect. This issue does not arise\nwith `deepsleep` because the host effectively powers down. When the sleep\nends, `asyncio` and necessary tasks start as in a power up event.\n\nThese problems have been resolved by users for specific applications with forks\nof the library. Given the limitations of `asyncio` I do not plan to write a\ngeneral solution.\n\n## 5.3 Ultra low power consumption\n\n[This document](./GATEWAY.md)\ndescribes an MQTT client for ESP32 or ESP8266 which uses ESPNOw to communicate\nwith a gateway running `mqtt_as`. The client does not need to connect to WiFi\neach time it wakes, saving power. The gateway can be shared between multiple\nclients.\n\nDrawbacks are the need for an always-on gateway, and the fact that only a\nsubset of MQTT V3.1.1 capabilities is supported.\n\n###### [Contents](./README.md#1-contents)\n\n# 6. References\n\n[mqtt introduction](http://mosquitto.org/man/mqtt-7.html)  \n[mosquitto server](http://mosquitto.org/man/mosquitto-8.html)  \n[mosquitto client publish](http://mosquitto.org/man/mosquitto_pub-1.html)  \n[mosquitto client subscribe](http://mosquitto.org/man/mosquitto_sub-1.html)  \n[MQTT 3.1.1 spec](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718048)  \n[MQTTv5 spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html)  \n[python client for PC's](https://www.eclipse.org/paho/clients/python/)  \n[Unofficial MQTT FAQ](https://forum.micropython.org/viewtopic.php?f=16\u0026t=2239)  \n[List of public brokers](https://github.com/mqtt/mqtt.github.io/wiki/public_brokers)  \n\n###### [Contents](./README.md#1-contents)\n\n# 7. Connect Error Codes\n\nOn the initial connection attempt the broker may reject the attempt. In this\ninstance an `OSError` will be raised showing two numbers. The first number\nshould be `0x2002` which is the MQTT `CONNACK` fixed header. The second\nis `CONNACK` variable header byte 2 which indicates the reason for failure as\nfollows:\n\n| Value | Reason                                       |\n|:------|:---------------------------------------------|\n| 1     | Unacceptable protocol version.               |\n| 2     | Client identifier rejected.                  |\n| 3     | MQTT service unavailable.                    |\n| 4     | Username or password have an invalid format. |\n| 5     | Client is not authorised to connect.         |\n\nSee MQTT spec section 3.2.2.\n\n###### [Contents](./README.md#1-contents)\n\n# 8. Hive MQ\n\nThe [Hive MQ](https://www.hivemq.com/) site offers a free web-based broker\nwhich is more secure than public brokers. With a public broker anyone can\ndetect and subscribe to your publications. Hive MQ gives you a unique broker\ninternet address which requires a password to access. TLS is mandatory but does\nnot require certificates.\n\nA simple GitHub registration gets you:\n * The unique broker address.\n * You specify a username.\n * The website supplies a password.\n\nTypical usage:\n```python\nconfig['user'] = 'my_username'\nconfig['password'] = 'my_password'\nbroker = 'unique broker address'  # e.g long_hex_string.s2.eu.hivemq.cloud\nconfig['server'] = broker\nconfig['ssl'] = True\nconfig['ssl_params'] = {\"server_hostname\": broker}\n```\nThe free service is scalable (at cost) to large commercial deployments.\n###### [Contents](./README.md#1-contents)\n\n# 9. The ssl_params dictionary\n\nThe following are the allowable keys:\n\n * 'key'\n * 'cert'\n * 'server_side'\n * 'server_hostname'\n * 'do_handshake'\n * 'cert_reqs' mbedtls only\n * 'cadata' mbedtls only\n\nAccording to [this post](https://github.com/orgs/micropython/discussions/10559#discussioncomment-4820939)\nthe following platforms use mbedtls:\n\n * esp32 port\n * pico w\n * unix port\n * stm32\n\nSee [this post](https://github.com/orgs/micropython/discussions/10801#discussioncomment-5071764)\nfor details of how to use client certificates with a `mosquitto` broker.\n\nNote also that TLS with client certificates requires the cliet's clock to be\napproximately correct. This can be achieved with an NTP query. If `mosquitto`\nis run on a local server it also runs the NTP daemon. A high availability\noption is to run the NTP query against the local server. See\n[this doc](https://github.com/peterhinch/micropython-samples/blob/master/README.md#414-ntp-time),\nalso [the official ntptime module](https://github.com/micropython/micropython-lib/blob/master/micropython/net/ntptime/ntptime.py).\n\nSee [this link](https://github.com/JustinS-B/Mosquitto_CA_and_Certs) for\ninformation on creating client certificates and a Bash script for doing so.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterhinch%2Fmicropython-mqtt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterhinch%2Fmicropython-mqtt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterhinch%2Fmicropython-mqtt/lists"}