From bad50b982394c3407fe768887ff72ec61eaf15f8 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 12:21:49 -0400 Subject: [PATCH 01/15] remove dep for wifi, breaks initialization signature --- adafruit_minimqtt.py | 53 +++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index e900f5f..c2a36b9 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -73,11 +73,33 @@ const(0x04) : 'Connection Refused - Incorrect username/password', const(0x05) : 'Connection Refused - Unauthorized'} +_the_interface = None # pylint: disable=invalid-name +_the_sock = None # pylint: disable=invalid-name + class MMQTTException(Exception): """MiniMQTT Exception class.""" # pylint: disable=unnecessary-pass #pass +def set_socket(sock, iface=None): + """Helper to set the global socket and optionally set the global network interface. + :param sock: socket object. + :param iface: internet interface object + + """ + global _the_sock # pylint: disable=invalid-name, global-statement + _the_sock = sock + if iface: + global _the_interface # pylint: disable=invalid-name, global-statement + _the_interface = iface + _the_sock.set_interface(iface) + print(_the_sock) + +def unpretty_ip(ip): # pylint: disable=no-self-use, invalid-name + """Converts a dotted-quad string to a bytearray IP address""" + octets = [int(x) for x in ip.split('.')] + return bytes(octets) + class MQTT: """MQTT Client for CircuitPython :param socket: Socket object for provided network interface @@ -92,19 +114,13 @@ class MQTT: :param int keep_alive: KeepAlive interval between the broker and the MiniMQTT client. """ # pylint: disable=too-many-arguments,too-many-instance-attributes, not-callable, invalid-name, no-member - def __init__(self, socket, broker, port=None, username=None, - password=None, network_manager=None, client_id=None, + def __init__(self, broker, port=None, username=None, + password=None, client_id=None, is_ssl=True, log=False, keep_alive=60): - # network management - self._socket = socket - network_manager_type = str(type(network_manager)) - if 'ESPSPI_WiFiManager' in network_manager_type: - self._wifi = network_manager - else: - raise TypeError("This library requires a NetworkManager object.") + self._sock = None # broker try: # set broker IP - self.broker = self._wifi.esp.unpretty_ip(broker) + self.broker = unpretty_ip(broker) except ValueError: # set broker URL self.broker = broker # port/ssl @@ -187,11 +203,10 @@ def connect(self, clean_session=True): """Initiates connection with the MQTT Broker. :param bool clean_session: Establishes a persistent session. """ - self._set_interface() - if self.logger is not None: - self.logger.debug('Creating new socket') - self._sock = self._socket.socket() - self._sock.settimeout(10) + global _the_interface # pylint: disable=global-statement, invalid-name + global _the_sock # pylint: disable=global-statement, invalid-name + + if self.port == 8883: try: if self.logger is not None: @@ -201,14 +216,15 @@ def connect(self, clean_session=True): raise MMQTTException("Invalid broker address defined.") else: if isinstance(self.broker, str): - addr = self._socket.getaddrinfo(self.broker, self.port)[0][-1] + addr = _the_sock.getaddrinfo(self.broker, self.port)[0] else: addr = (self.broker, self.port) try: if self.logger is not None: self.logger.debug('Attempting to establish insecure MQTT connection...') - #self._sock.connect((self.broker, self.port), TCP_MODE) - self._sock.connect(addr, TCP_MODE) + self._sock = _the_sock.socket(addr[0], 0x21, addr[2]) + self._sock.settimeout(15) + self._sock.connect(addr[-1], TCP_MODE) except RuntimeError as e: raise MMQTTException("Invalid broker address defined.", e) @@ -641,6 +657,7 @@ def _wait_for_msg(self, timeout=30): """ res = self._sock.recv(1) self._sock.settimeout(timeout) + print("RES: ", res) if res in [None, b""]: return None if res == MQTT_PINGRESP: From 7f202f38dc80fd49d7de7b5693e08245b0c89a65 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 12:31:22 -0400 Subject: [PATCH 02/15] add adafruitio_ethernet mmqt example --- examples/minimqtt_adafruitio_eth.py | 87 +++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 examples/minimqtt_adafruitio_eth.py diff --git a/examples/minimqtt_adafruitio_eth.py b/examples/minimqtt_adafruitio_eth.py new file mode 100755 index 0000000..ea5ce75 --- /dev/null +++ b/examples/minimqtt_adafruitio_eth.py @@ -0,0 +1,87 @@ +# Adafruit MiniMQTT Pub/Sub Example +# Written by Tony DiCola for Adafruit Industries +# Modified by Brent Rubell for Adafruit Industries +import time +import board +import busio +from digitalio import DigitalInOut + +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket + +import adafruit_minimqtt as MQTT + +# Get Adafruit IO details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("Adafruit IO secrets are kept in secrets.py, please add them there!") + raise + +cs = DigitalInOut(board.D10) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface with DHCP +eth = WIZNET5K(spi_bus, cs) + +### Feeds ### + +# Setup a feed named 'photocell' for publishing to a feed +photocell_feed = secrets['aio_username'] + '/feeds/photocell' + +# Setup a feed named 'onoff' for subscribing to changes +onoff_feed = secrets['aio_username'] + '/feeds/onoff' + +### Code ### + +# Define callback methods which are called when events occur +# pylint: disable=unused-argument, redefined-outer-name +def connected(client, userdata, flags, rc): + # This function will be called when the client is connected + # successfully to the broker. + print('Connected to 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 = 'io.adafruit.com', + username = secrets['aio_username'], + password = secrets['aio_key'], + is_ssl = False) + +# 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...') +mqtt_client.connect() + +photocell_val = 0 +while True: + # Poll the message queue + mqtt_client.loop() + + # Send a new message + print('Sending photocell value: %d...' % photocell_val) + mqtt_client.publish(photocell_feed, photocell_val) + print('Sent!') + photocell_val += 1 + time.sleep(5) From 1d648df6ce5e586d3562fff68ea2f63d208997d1 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 12:46:21 -0400 Subject: [PATCH 03/15] parse out full URLs properly! --- adafruit_minimqtt.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index c2a36b9..28ce025 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -102,7 +102,6 @@ def unpretty_ip(ip): # pylint: disable=no-self-use, invalid-name class MQTT: """MQTT Client for CircuitPython - :param socket: Socket object for provided network interface :param str broker: MQTT Broker URL or IP Address. :param int port: Optional port definition, defaults to 8883. :param str username: Username for broker authentication. @@ -112,6 +111,7 @@ class MQTT: :param bool is_ssl: Sets a secure or insecure connection with the broker. :param bool log: Attaches a logger to the MQTT client, defaults to logging level INFO. :param int keep_alive: KeepAlive interval between the broker and the MiniMQTT client. + """ # pylint: disable=too-many-arguments,too-many-instance-attributes, not-callable, invalid-name, no-member def __init__(self, broker, port=None, username=None, @@ -177,6 +177,7 @@ def __exit__(self, exception_type, exception_value, traceback): def deinit(self): """De-initializes the MQTT client and disconnects from the mqtt broker. + """ self.disconnect() @@ -186,6 +187,7 @@ def last_will(self, topic=None, message=None, qos=0, retain=False): :param str message: Last will disconnection message. :param int qos: Quality of Service level. :param bool retain: Specifies if the message is to be retained when it is published. + """ if self._is_connected: raise MMQTTException('Last Will should be defined before connect() is called.') @@ -202,10 +204,25 @@ def last_will(self, topic=None, message=None, qos=0, retain=False): def connect(self, clean_session=True): """Initiates connection with the MQTT Broker. :param bool clean_session: Establishes a persistent session. + """ - global _the_interface # pylint: disable=global-statement, invalid-name - global _the_sock # pylint: disable=global-statement, invalid-name + 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) + if ":" in self.broker: + self.broker, port = self.broker.split(":", 1) + port = int(port) if self.port == 8883: try: @@ -218,7 +235,7 @@ def connect(self, clean_session=True): if isinstance(self.broker, str): addr = _the_sock.getaddrinfo(self.broker, self.port)[0] else: - addr = (self.broker, self.port) + addr = (self.broker, 0x21, self.port) try: if self.logger is not None: self.logger.debug('Attempting to establish insecure MQTT connection...') From 771652b2487393e6d7d5b1ffbf39a59e5accd314 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 13:20:35 -0400 Subject: [PATCH 04/15] trim unused wifi methods out of library --- adafruit_minimqtt.py | 77 ++++------------------------ examples/minimqtt_adafruitio_wifi.py | 50 +++++------------- 2 files changed, 25 insertions(+), 102 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index 28ce025..442a4e5 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -224,23 +224,20 @@ def connect(self, clean_session=True): self.broker, port = self.broker.split(":", 1) port = int(port) + addr = _the_sock.getaddrinfo(self.broker, self.port)[0] + self._sock = _the_sock.socket(addr[0], _the_sock.SOCK_STREAM, addr[2]) + self._sock.settimeout(15) if self.port == 8883: try: if self.logger is not None: self.logger.debug('Attempting to establish secure MQTT connection...') - self._sock.connect((self.broker, self.port), TLS_MODE) - except RuntimeError: - raise MMQTTException("Invalid broker address defined.") + self._sock.connect(addr[-1], TLS_MODE) + except RuntimeError as e: + raise MMQTTException("Invalid broker address defined.", e) else: - if isinstance(self.broker, str): - addr = _the_sock.getaddrinfo(self.broker, self.port)[0] - else: - addr = (self.broker, 0x21, self.port) try: if self.logger is not None: self.logger.debug('Attempting to establish insecure MQTT connection...') - self._sock = _the_sock.socket(addr[0], 0x21, addr[2]) - self._sock.settimeout(15) self._sock.connect(addr[-1], TCP_MODE) except RuntimeError as e: raise MMQTTException("Invalid broker address defined.", e) @@ -571,49 +568,6 @@ def unsubscribe(self, topic): self._subscribed_topics.remove(t) return - @property - def is_wifi_connected(self): - """Returns if the ESP module is connected to - an access point, resets module if False""" - if self._wifi: - return self._wifi.esp.is_connected - raise MMQTTException("MiniMQTT Client does not use a WiFi NetworkManager.") - - # pylint: disable=line-too-long, protected-access - @property - def is_sock_connected(self): - """Returns if the socket is connected.""" - return self.is_wifi_connected and self._sock and self._wifi.esp.socket_connected(self._sock._socknum) - - def reconnect_socket(self): - """Re-establishes the socket's connection with the MQTT broker. - """ - try: - if self.logger is not None: - self.logger.debug("Attempting to reconnect with MQTT Broker...") - self.reconnect() - except RuntimeError as err: - if self.logger is not None: - self.logger.debug('Failed to reconnect with MQTT Broker, retrying...', err) - time.sleep(1) - self.reconnect_socket() - - def reconnect_wifi(self): - """Reconnects to WiFi Access Point and socket, if disconnected. - """ - while not self.is_wifi_connected: - try: - if self.logger is not None: - self.logger.debug('Connecting to WiFi AP...') - self._wifi.connect() - except (RuntimeError, ValueError): - if self.logger is not None: - self.logger.debug('Failed to reset WiFi module, retrying...') - time.sleep(1) - # we just reconnected, is the socket still connected? - if not self.is_sock_connected: - self.reconnect_socket() - def reconnect(self, resub_topics=True): """Attempts to reconnect to the MQTT broker. :param bool resub_topics: Resubscribe to previously subscribed topics. @@ -634,27 +588,19 @@ def loop_forever(self): """Starts a blocking message loop. Use this method if you want to run a program forever. Code below a call to this method will NOT execute. - Network reconnection is handled within this call. + + NOTE: Network reconnection is not handled within this call and + must be handled by your code for each interface. """ while True: - # Check WiFi and socket status - if self.is_sock_connected: - try: - self.loop() - except (RuntimeError, ValueError): - if self._wifi: - # Reconnect the WiFi module and the socket - self.reconnect_wifi() - continue + if self._sock.connected: + self.loop() def loop(self): """Non-blocking message loop. Use this method to check incoming subscription messages. - This method does NOT handle networking or - network hardware management, use loop_forever - or handle in code instead. """ if self._timestamp == 0: self._timestamp = time.monotonic() @@ -674,7 +620,6 @@ def _wait_for_msg(self, timeout=30): """ res = self._sock.recv(1) self._sock.settimeout(timeout) - print("RES: ", res) if res in [None, b""]: return None if res == MQTT_PINGRESP: diff --git a/examples/minimqtt_adafruitio_wifi.py b/examples/minimqtt_adafruitio_wifi.py index 47f2a1c..20b126e 100644 --- a/examples/minimqtt_adafruitio_wifi.py +++ b/examples/minimqtt_adafruitio_wifi.py @@ -11,41 +11,19 @@ import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_minimqtt import MQTT -### WiFi ### -# Get wifi details and more from a secrets.py file +# Get Adafruit IO details and more from a secrets.py file try: from secrets import secrets except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") + print("Adafruit IO secrets are kept in secrets.py, please add them there!") raise -# If you are using a board with pre-defined ESP32 Pins: -esp32_cs = DigitalInOut(board.ESP_CS) -esp32_ready = DigitalInOut(board.ESP_BUSY) -esp32_reset = DigitalInOut(board.ESP_RESET) - -# If you have an externally connected ESP32: -# esp32_cs = DigitalInOut(board.D9) -# esp32_ready = DigitalInOut(board.D10) -# esp32_reset = DigitalInOut(board.D5) - -spi = busio.SPI(board.SCK, board.MOSI, board.MISO) -esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -"""Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -# Uncomment below for an externally defined RGB LED -# import adafruit_rgbled -# from adafruit_esp32spi import PWMOut -# RED_LED = PWMOut.PWMOut(esp, 26) -# GREEN_LED = PWMOut.PWMOut(esp, 27) -# BLUE_LED = PWMOut.PWMOut(esp, 25) -# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager( - esp, secrets, status_light) +cs = DigitalInOut(board.D10) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface with DHCP +eth = WIZNET5K(spi_bus, cs) ### Feeds ### @@ -78,15 +56,15 @@ def message(client, topic, message): print('New message on topic {0}: {1}'.format(topic, message)) -# Connect to WiFi -wifi.connect() +# Initialize MQTT interface with the ethernet interface +MQTT.set_socket # Set up a MiniMQTT Client -mqtt_client = MQTT(socket, - broker='io.adafruit.com', - username=secrets['aio_username'], - password=secrets['aio_key'], - network_manager=wifi) +# NOTE: We'll need to connect insecurely for ethernet configurations. +mqtt_client = MQTT.MQTT(broker = 'io.adafruit.com', + username = secrets['aio_username'], + password = secrets['aio_key'], + is_ssl = False) # Setup the callback methods above mqtt_client.on_connect = connected From e7304f98f2f4a9eaa83d5df286355920d9b538e9 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 13:23:31 -0400 Subject: [PATCH 05/15] lintin --- adafruit_minimqtt.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index 442a4e5..386136e 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -93,7 +93,6 @@ def set_socket(sock, iface=None): global _the_interface # pylint: disable=invalid-name, global-statement _the_interface = iface _the_sock.set_interface(iface) - print(_the_sock) def unpretty_ip(ip): # pylint: disable=no-self-use, invalid-name """Converts a dotted-quad string to a bytearray IP address""" @@ -200,7 +199,7 @@ def last_will(self, topic=None, message=None, qos=0, retain=False): self._lw_msg = message self._lw_retain = retain - # pylint: disable=too-many-branches, too-many-statements + # pylint: disable=too-many-branches, too-many-statements, too-many-locals def connect(self, clean_session=True): """Initiates connection with the MQTT Broker. :param bool clean_session: Establishes a persistent session. @@ -380,7 +379,7 @@ def publish(self, topic, msg, retain=False, qos=0): # check msg/qos kwargs if msg is None: raise MMQTTException('Message can not be None.') - elif isinstance(msg, (int, float)): + if isinstance(msg, (int, float)): msg = str(msg).encode('ascii') elif isinstance(msg, str): msg = str(msg).encode('utf-8') @@ -588,7 +587,7 @@ def loop_forever(self): """Starts a blocking message loop. Use this method if you want to run a program forever. Code below a call to this method will NOT execute. - + NOTE: Network reconnection is not handled within this call and must be handled by your code for each interface. @@ -608,7 +607,8 @@ def loop(self): if current_time - self._timestamp >= self.keep_alive: # 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...') + self.logger.debug('KeepAlive period elapsed - \ + requesting a PINGRESP from the server...') self.ping() self._timestamp = 0 self._sock.settimeout(0.1) @@ -677,7 +677,7 @@ def _check_topic(topic): if topic is None: raise MMQTTException('Topic may not be NoneType') # [MQTT-4.7.3-1] - elif not topic: + if not topic: raise MMQTTException('Topic may not be empty.') # [MQTT-4.7.3-3] elif len(topic.encode('utf-8')) > MQTT_TOPIC_LENGTH_LIMIT: From 3bdef9cba42413e12756155644f629975aaf85f2 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 13:24:37 -0400 Subject: [PATCH 06/15] remove is_ssl, code will detect http vs https now! --- examples/minimqtt_adafruitio_eth.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/minimqtt_adafruitio_eth.py b/examples/minimqtt_adafruitio_eth.py index ea5ce75..e993f42 100755 --- a/examples/minimqtt_adafruitio_eth.py +++ b/examples/minimqtt_adafruitio_eth.py @@ -60,10 +60,9 @@ def message(client, topic, message): # Set up a MiniMQTT Client # NOTE: We'll need to connect insecurely for ethernet configurations. -mqtt_client = MQTT.MQTT(broker = 'io.adafruit.com', +mqtt_client = MQTT.MQTT(broker = 'http://io.adafruit.com', username = secrets['aio_username'], - password = secrets['aio_key'], - is_ssl = False) + password = secrets['aio_key']) # Setup the callback methods above mqtt_client.on_connect = connected From 1d8d6ad02ee1175de9112e131c3c68fd909cf565 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 14:49:52 -0400 Subject: [PATCH 07/15] tested on TLS/SSL esp32 implementation --- adafruit_minimqtt.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index 386136e..805c018 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -94,11 +94,6 @@ def set_socket(sock, iface=None): _the_interface = iface _the_sock.set_interface(iface) -def unpretty_ip(ip): # pylint: disable=no-self-use, invalid-name - """Converts a dotted-quad string to a bytearray IP address""" - octets = [int(x) for x in ip.split('.')] - return bytes(octets) - class MQTT: """MQTT Client for CircuitPython :param str broker: MQTT Broker URL or IP Address. @@ -119,7 +114,7 @@ def __init__(self, broker, port=None, username=None, self._sock = None # broker try: # set broker IP - self.broker = unpretty_ip(broker) + self.broker = _the_interface.unpretty_ip(broker) except ValueError: # set broker URL self.broker = broker # port/ssl @@ -223,20 +218,22 @@ def connect(self, clean_session=True): self.broker, port = self.broker.split(":", 1) port = int(port) - addr = _the_sock.getaddrinfo(self.broker, self.port)[0] - self._sock = _the_sock.socket(addr[0], _the_sock.SOCK_STREAM, addr[2]) + addr = _the_sock.getaddrinfo(self.broker, self.port, 0, _the_sock.SOCK_STREAM)[0] + self._sock = _the_sock.socket(addr[0], addr[1], addr[2]) self._sock.settimeout(15) if self.port == 8883: try: if self.logger is not None: self.logger.debug('Attempting to establish secure MQTT connection...') - self._sock.connect(addr[-1], TLS_MODE) + self._sock.connect((self.broker, self.port), _the_interface.TLS_MODE) except RuntimeError as e: raise MMQTTException("Invalid broker address defined.", e) else: try: if self.logger is not None: self.logger.debug('Attempting to establish insecure MQTT connection...') + if hasattr(self.broker, "extend"): + self.broker = _the_interface.pretty_ip(self.broker) self._sock.connect(addr[-1], TCP_MODE) except RuntimeError as e: raise MMQTTException("Invalid broker address defined.", e) @@ -680,7 +677,7 @@ def _check_topic(topic): if not topic: raise MMQTTException('Topic may not be empty.') # [MQTT-4.7.3-3] - elif len(topic.encode('utf-8')) > MQTT_TOPIC_LENGTH_LIMIT: + if len(topic.encode('utf-8')) > MQTT_TOPIC_LENGTH_LIMIT: raise MMQTTException('Topic length is too large.') @staticmethod From 66eb7b9ea6ed62cd60aabe9b63c3d09224f39ccd Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 14:54:11 -0400 Subject: [PATCH 08/15] remove unnecessary broker ip --- adafruit_minimqtt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index 805c018..b58960f 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -232,8 +232,6 @@ def connect(self, clean_session=True): try: if self.logger is not None: self.logger.debug('Attempting to establish insecure MQTT connection...') - if hasattr(self.broker, "extend"): - self.broker = _the_interface.pretty_ip(self.broker) self._sock.connect(addr[-1], TCP_MODE) except RuntimeError as e: raise MMQTTException("Invalid broker address defined.", e) From 0141714500f9eda7d81e76bf3fdfae677ce0c5e6 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 15:57:01 -0400 Subject: [PATCH 09/15] redo example! --- examples/minimqtt_adafruitio_wifi.py | 58 ++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/examples/minimqtt_adafruitio_wifi.py b/examples/minimqtt_adafruitio_wifi.py index 20b126e..caf8cbd 100644 --- a/examples/minimqtt_adafruitio_wifi.py +++ b/examples/minimqtt_adafruitio_wifi.py @@ -9,21 +9,44 @@ from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT -# Get Adafruit IO details and more from a secrets.py file +### WiFi ### + +# Get wifi details and more from a secrets.py file try: from secrets import secrets except ImportError: - print("Adafruit IO secrets are kept in secrets.py, please add them there!") + print("WiFi secrets are kept in secrets.py, please add them there!") raise -cs = DigitalInOut(board.D10) -spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - -# Initialize ethernet interface with DHCP -eth = WIZNET5K(spi_bus, cs) +# If you are using a board with pre-defined ESP32 Pins: +esp32_cs = DigitalInOut(board.ESP_CS) +esp32_ready = DigitalInOut(board.ESP_BUSY) +esp32_reset = DigitalInOut(board.ESP_RESET) + +# If you have an externally connected ESP32: +# esp32_cs = DigitalInOut(board.D9) +# esp32_ready = DigitalInOut(board.D10) +# esp32_reset = DigitalInOut(board.D5) + +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset, debug=True) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel( + board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager( + esp, secrets, status_light) ### Feeds ### @@ -56,15 +79,18 @@ def message(client, topic, message): print('New message on topic {0}: {1}'.format(topic, message)) -# Initialize MQTT interface with the ethernet interface -MQTT.set_socket +# Connect to WiFi +print("Connecting to WiFi...") +wifi.connect() +print("Connected!") + +# Initialize MQTT interface with the esp interface +MQTT.set_socket(socket, esp) # Set up a MiniMQTT Client -# NOTE: We'll need to connect insecurely for ethernet configurations. -mqtt_client = MQTT.MQTT(broker = 'io.adafruit.com', - username = secrets['aio_username'], - password = secrets['aio_key'], - is_ssl = False) +mqtt_client = MQTT.MQTT(broker='http://io.adafruit.com', + username=secrets['aio_username'], + password=secrets['aio_key']) # Setup the callback methods above mqtt_client.on_connect = connected @@ -85,4 +111,4 @@ def message(client, topic, message): mqtt_client.publish(photocell_feed, photocell_val) print('Sent!') photocell_val += 1 - time.sleep(1) + time.sleep(5) \ No newline at end of file From 0ed9b558863a9af7b9442b504e3748f2269af92b Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 16:13:28 -0400 Subject: [PATCH 10/15] lint all the examples, update ALL the examples! --- examples/minimqtt_adafruitio_wifi.py | 2 +- examples/minimqtt_certificate.py | 15 +++++++++------ examples/minimqtt_pub_sub_blocking.py | 18 +++++++++++------- examples/minimqtt_pub_sub_nonblocking.py | 15 +++++++++------ examples/minimqtt_simpletest.py | 15 +++++++++------ 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/examples/minimqtt_adafruitio_wifi.py b/examples/minimqtt_adafruitio_wifi.py index caf8cbd..d63fe92 100644 --- a/examples/minimqtt_adafruitio_wifi.py +++ b/examples/minimqtt_adafruitio_wifi.py @@ -111,4 +111,4 @@ def message(client, topic, message): mqtt_client.publish(photocell_feed, photocell_val) print('Sent!') photocell_val += 1 - time.sleep(5) \ No newline at end of file + time.sleep(5) diff --git a/examples/minimqtt_certificate.py b/examples/minimqtt_certificate.py index 471e176..0b30cf3 100644 --- a/examples/minimqtt_certificate.py +++ b/examples/minimqtt_certificate.py @@ -6,7 +6,7 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT ### WiFi ### @@ -93,14 +93,17 @@ def publish(client, userdata, topic, pid): esp.set_private_key(DEVICE_KEY) # Connect to WiFi +print("Connecting to WiFi...") wifi.connect() +print("Connected!") + +# Initialize MQTT interface with the esp interface +MQTT.set_socket(socket, esp) # Set up a MiniMQTT Client -client = MQTT(socket, - broker = secrets['broker'], - username = secrets['user'], - password = secrets['pass'], - network_manager = wifi) +client = MQTT.MQTT(broker = secrets['broker'], + username = secrets['user'], + password = secrets['pass']) # Connect callback handlers to client client.on_connect = connect diff --git a/examples/minimqtt_pub_sub_blocking.py b/examples/minimqtt_pub_sub_blocking.py index 5831466..7d6ba7f 100644 --- a/examples/minimqtt_pub_sub_blocking.py +++ b/examples/minimqtt_pub_sub_blocking.py @@ -1,5 +1,5 @@ # CircuitPython MiniMQTT Library -# Adafruit IO SSL/TLS Example for WiFi (ESP32SPI) +# Adafruit IO SSL/TLS Example for WiFi import board import busio from digitalio import DigitalInOut @@ -7,7 +7,7 @@ from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT ### WiFi ### @@ -72,14 +72,17 @@ def message(client, topic, message): print('New message on topic {0}: {1}'.format(topic, message)) # Connect to WiFi +print("Connecting to WiFi...") wifi.connect() +print("Connected!") + +# Initialize MQTT interface with the esp interface +MQTT.set_socket(socket, esp) # Set up a MiniMQTT Client -mqtt_client = MQTT(socket, - broker = secrets['broker'], - username = secrets['user'], - password = secrets['pass'], - network_manager = wifi) +mqtt_client = MQTT.MQTT(broker = secrets['broker'], + username = secrets['user'], + password = secrets['pass']) # Setup the callback methods above mqtt_client.on_connect = connected @@ -87,6 +90,7 @@ def message(client, topic, message): mqtt_client.on_message = message # Connect the client to the MQTT broker. +print('Connecting to MQTT broker...') mqtt_client.connect() # Start a blocking message loop diff --git a/examples/minimqtt_pub_sub_nonblocking.py b/examples/minimqtt_pub_sub_nonblocking.py index fdde9a7..2fe287c 100644 --- a/examples/minimqtt_pub_sub_nonblocking.py +++ b/examples/minimqtt_pub_sub_nonblocking.py @@ -6,7 +6,7 @@ from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT ### WiFi ### @@ -70,14 +70,17 @@ def message(client, topic, message): print('New message on topic {0}: {1}'.format(topic, message)) # Connect to WiFi +print("Connecting to WiFi...") wifi.connect() +print("Connected!") + +# Initialize MQTT interface with the esp interface +MQTT.set_socket(socket, esp) # Set up a MiniMQTT Client -mqtt_client = MQTT(socket, - broker = secrets['broker'], - username = secrets['user'], - password = secrets['pass'], - network_manager = wifi) +mqtt_client = MQTT.MQTT(broker = secrets['broker'], + username = secrets['user'], + password = secrets['pass']) # Setup the callback methods above mqtt_client.on_connect = connected diff --git a/examples/minimqtt_simpletest.py b/examples/minimqtt_simpletest.py index a600c69..617a3cb 100644 --- a/examples/minimqtt_simpletest.py +++ b/examples/minimqtt_simpletest.py @@ -6,7 +6,7 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT ### WiFi ### @@ -80,14 +80,17 @@ def publish(client, userdata, topic, pid): print('Published to {0} with PID {1}'.format(topic, pid)) # Connect to WiFi +print("Connecting to WiFi...") wifi.connect() +print("Connected!") + +# Initialize MQTT interface with the esp interface +MQTT.set_socket(socket, esp) # Set up a MiniMQTT Client -client = MQTT(socket, - broker = secrets['broker'], - username = secrets['user'], - password = secrets['pass'], - network_manager = wifi) +client = MQTT.MQTT(broker = secrets['broker'], + username = secrets['user'], + password = secrets['pass']) # Connect callback handlers to client client.on_connect = connect From 6c3ae7827916ffc7c0d4d1b443aad6f273ec1f37 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 16:34:23 -0400 Subject: [PATCH 11/15] _eth simpletest --- examples/minimqtt_simpletest_eth.py | 86 +++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 examples/minimqtt_simpletest_eth.py diff --git a/examples/minimqtt_simpletest_eth.py b/examples/minimqtt_simpletest_eth.py new file mode 100644 index 0000000..d24441d --- /dev/null +++ b/examples/minimqtt_simpletest_eth.py @@ -0,0 +1,86 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket + +import adafruit_minimqtt as MQTT + +# Get MQTT details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("MQTT secrets are kept in secrets.py, please add them there!") + raise + +cs = DigitalInOut(board.D10) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface with DHCP +eth = WIZNET5K(spi_bus, cs) +### Topic Setup ### + +# MQTT Topic +# Use this topic if you'd like to connect to a standard MQTT broker +mqtt_topic = 'test/topic' + +# Adafruit IO-style Topic +# Use this topic if you'd like to connect to io.adafruit.com +# mqtt_topic = 'aio_user/feeds/temperature' + +### Code ### + +# 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. + print('Connected to MQTT Broker!') + print('Flags: {0}\n RC: {1}'.format(flags, rc)) + +def disconnect(client, userdata, rc): + # This method is called when the client disconnects + # from the broker. + print('Disconnected from MQTT Broker!') + +def subscribe(client, userdata, topic, granted_qos): + # This method is called when the client subscribes to a new feed. + print('Subscribed to {0} with QOS level {1}'.format(topic, granted_qos)) + +def unsubscribe(client, userdata, topic, pid): + # This method is called when the client unsubscribes from a feed. + print('Unsubscribed from {0} with PID {1}'.format(topic, pid)) + +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print('Published to {0} with PID {1}'.format(topic, pid)) + +# Initialize MQTT interface with the ethernet interface +MQTT.set_socket(socket, eth) + +# Set up a MiniMQTT Client +client = MQTT.MQTT(broker = secrets['broker'], + username = secrets['user'], + password = secrets['pass']) + +# Connect callback handlers to client +client.on_connect = connect +client.on_disconnect = disconnect +client.on_subscribe = subscribe +client.on_unsubscribe = unsubscribe +client.on_publish = publish + +print('Attempting to connect to %s'%client.broker) +client.connect() + +print('Subscribing to %s'%mqtt_topic) +client.subscribe(mqtt_topic) + +print('Publishing to %s'%mqtt_topic) +client.publish(mqtt_topic, 'Hello Broker!') + +print('Unsubscribing from %s'%mqtt_topic) +client.unsubscribe(mqtt_topic) + +print('Disconnecting from %s'%client.broker) +client.disconnect() From f784d1901a65ca5675bc1c0b0ebfe57320438a6e Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 17:11:30 -0400 Subject: [PATCH 12/15] move blocking logic into the client-side, tested --- examples/minimqtt_pub_sub_blocking.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/minimqtt_pub_sub_blocking.py b/examples/minimqtt_pub_sub_blocking.py index 7d6ba7f..7f5c4f9 100644 --- a/examples/minimqtt_pub_sub_blocking.py +++ b/examples/minimqtt_pub_sub_blocking.py @@ -1,5 +1,6 @@ # CircuitPython MiniMQTT Library # Adafruit IO SSL/TLS Example for WiFi +import time import board import busio from digitalio import DigitalInOut @@ -93,8 +94,19 @@ def message(client, topic, message): print('Connecting to MQTT broker...') mqtt_client.connect() -# Start a blocking message loop -# If you only want to listen to incoming messages, -# you'll want to loop_forever as it handles network reconnections -# No code below this line will execute. -mqtt_client.loop_forever() +# Start a blocking message loop... +# NOTE: NO code below this loop will execute +# NOTE: Network reconnection is handled within this loop + +counter = 0 +while True: + print("Iteration #", counter) + try: + mqtt_client.loop() + except (ValueError, RuntimeError) as e: + print("Failed to get data, retrying\n", e) + wifi.reset() + mqtt_client.reconnect() + continue + counter += 1 + time.sleep(1) From d53359e6ed581429500ca89f17ee328fa172b1bc Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 17:14:15 -0400 Subject: [PATCH 13/15] add depreciation notice to loop_forever --- adafruit_minimqtt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/adafruit_minimqtt.py b/adafruit_minimqtt.py index b58960f..2b5afb6 100644 --- a/adafruit_minimqtt.py +++ b/adafruit_minimqtt.py @@ -583,8 +583,10 @@ def loop_forever(self): method if you want to run a program forever. Code below a call to this method will NOT execute. - NOTE: Network reconnection is not handled within this call and - must be handled by your code for each interface. + NOTE: This method is depreciated and will be removed in the + next major release. Please see examples/minimqtt_pub_sub_blocking.py + for an example of creating a blocking loop which can handle wireless + network events. """ while True: From 483e1351f23438e6a9ea64acdacb9ccd0a780dae Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 17:23:15 -0400 Subject: [PATCH 14/15] remove broken pyportal example, update with tested new one --- ..._pyportal => minimqtt_pub_sub_pyportal.py} | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) rename examples/{minimqtt_pub_sub_pyportal => minimqtt_pub_sub_pyportal.py} (80%) diff --git a/examples/minimqtt_pub_sub_pyportal b/examples/minimqtt_pub_sub_pyportal.py similarity index 80% rename from examples/minimqtt_pub_sub_pyportal rename to examples/minimqtt_pub_sub_pyportal.py index f15f786..95b0ec9 100644 --- a/examples/minimqtt_pub_sub_pyportal +++ b/examples/minimqtt_pub_sub_pyportal.py @@ -1,13 +1,13 @@ import time from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_minimqtt import MQTT +import adafruit_minimqtt as MQTT import adafruit_pyportal pyportal = adafruit_pyportal.PyPortal() - + ### WiFi ### - + # Get wifi details and more from a secrets.py file try: from secrets import secrets @@ -15,12 +15,13 @@ print("WiFi secrets are kept in secrets.py, please add them there!") raise -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(pyportal._esp, +# pylint: disable=protected-access +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(pyportal._esp, secrets, None) - + # ------------- MQTT Topic Setup ------------- # mqtt_topic = 'test/topic' - + ### Code ### # Define callback methods which are called when events occur # pylint: disable=unused-argument, redefined-outer-name @@ -29,11 +30,11 @@ def connected(client, userdata, flags, rc): # successfully to the broker. print('Subscribing to %s' % (mqtt_topic)) client.subscribe(mqtt_topic) - + def disconnected(client, userdata, rc): # This method is called when the client is disconnected print('Disconnected from MQTT Broker!') - + def message(client, topic, message): """Method callled when a client's subscribed feed has a new value. @@ -41,31 +42,35 @@ def message(client, topic, message): :param str message: The new value """ print('New message on topic {0}: {1}'.format(topic, message)) - + # Connect to WiFi +print("Connecting to WiFi...") wifi.connect() - +print("Connected!") + +# Initialize MQTT interface with the esp interface +# pylint: disable=protected-access +MQTT.set_socket(socket, pyportal._esp) + # Set up a MiniMQTT Client -mqtt_client = MQTT(socket, - broker=secrets['broker'], - username=secrets['user'], - password=secrets['pass'], - is_ssl=False, - network_manager=wifi) - +mqtt_client = MQTT.MQTT(broker=secrets['broker'], + username=secrets['user'], + password=secrets['pass'], + is_ssl=False) + # 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. mqtt_client.connect() - + photocell_val = 0 while True: # Poll the message queue mqtt_client.loop() - + # Send a new message print('Sending photocell value: %d' % photocell_val) mqtt_client.publish(mqtt_topic, photocell_val) From 6ccedab9156a0e0be11e6e5d742d30f19ee853f8 Mon Sep 17 00:00:00 2001 From: brentru Date: Thu, 12 Mar 2020 18:41:55 -0400 Subject: [PATCH 15/15] remove debug counter.. --- examples/minimqtt_pub_sub_blocking.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/minimqtt_pub_sub_blocking.py b/examples/minimqtt_pub_sub_blocking.py index 7f5c4f9..10143bc 100644 --- a/examples/minimqtt_pub_sub_blocking.py +++ b/examples/minimqtt_pub_sub_blocking.py @@ -97,10 +97,7 @@ def message(client, topic, message): # Start a blocking message loop... # NOTE: NO code below this loop will execute # NOTE: Network reconnection is handled within this loop - -counter = 0 while True: - print("Iteration #", counter) try: mqtt_client.loop() except (ValueError, RuntimeError) as e: @@ -108,5 +105,4 @@ def message(client, topic, message): wifi.reset() mqtt_client.reconnect() continue - counter += 1 time.sleep(1)