mqtt subscribe client that runs under micropython

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
slzatz
Posts: 92
Joined: Mon Feb 09, 2015 1:09 am

mqtt subscribe client that runs under micropython

Post by slzatz » Sat Apr 16, 2016 8:26 pm

Not sure this is of interest to anyone but this is a stripped down version of the paho.mqtt python client tweaked to run under micropython. It uses epoll (v. select) and all the threading code is stripped out and right now the publish code is commented out as well so it is only useful for subscribing to a topic. The code is located at https://gist.github.com/slzatz/a12e0468 ... 1cceea8472

Elsewhere in the forum are the 15 or so lines of code that enable a simple mqtt publish capability that has been tested on the esp8266.

The mqtt client code runs fine under the unix/linux version of micropython but at about 1400 lines of code I doubt it's much use on most hardware but at least demonstrates that the code works under micropython and there is a lot of additional code that can be stripped out or compressed. As the esp8266 kickstarter goals include an mqtt client this may or may not prove of any use for creating that but it was instructive to me in terms of the capabilities of micropython. (I am sure that Damien and/or Paul will create the equivalent in 25 lines or less.)

The client does use errno.py, ffilib.py, os.py, select.py, socket.py and stat.py from micropython-lib. Next step is to get something that does work on the esp8266.

For anyone interested the code below can be used to test the client:

Code: Select all

import umqtt as mqtt
import utime as time

def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))
    client.subscribe("test")
    
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect(<host>, 1883, 60)

while 1:
    client.loop()
    time.sleep(1)

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: mqtt subscribe client that runs under micropython

Post by pfalcon » Fri Apr 22, 2016 5:04 pm

slzatz wrote: it was instructive to me in terms of the capabilities of micropython.
Thumbs up for going that way, in the sense that it's important direction to follow for MicroPython's progress (among all the others) and there's indeed a whole topic dedicated to that: http://forum.micropython.org/viewtopic.php?f=15&t=104
(I am sure that Damien and/or Paul will create the equivalent in 25 lines or less.)
Well, I'm not sure about 25 lines, but we'll definitely start from scratch and let it evolve from a feature to a feature, stopping at right time (while letting other people to go further if they need it).
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

slzatz
Posts: 92
Joined: Mon Feb 09, 2015 1:09 am

Re: mqtt subscribe client that runs under micropython

Post by slzatz » Sun May 01, 2016 3:00 am

Well it turns out that you can create an mqtt client that can subscribe to a topic in about 25 lines or less. The following is by far not a work of art but it does actually work. To make life easier I set the QoS to 0 but easy to modify the subscribe function to handle non-zero QoS. So no polling or select but just sending and receiving on a socket that works fine for messages coming in periodically. In the demo, the esp8266 is subscribed to an mqtt broker running on an AWS EC2 instance and in the little linked video I am publishing a message to that broker (using the chrome app MQTTLens) and you see that message show up on the OLED screen of the Adafruit FeatherWing attached to the esp8266. Nothing too remarkable but shows how easy it is to get an esp running micropython to listen to mqtt topics.

The video is at https://www.youtube.com/watch?v=8dB0InYhh9s

The code is below:

Code: Select all

# Subscribe function added to code from Andrew http://forum.micropython.org/viewtopic.php?t=1101#p6545

from time import sleep
import socket
import network
from config import host, ssid, pw
from ssd1306_min import SSD1306 as SSD
from machine import Pin, I2C

i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)

d = SSD(i2c)
d.init_display()
d.draw_text(0, 0, "HELLO")
d.display()

# mqtt functions

def mtStr(s):
 return bytes([len(s) >> 8, len(s) & 255]) + s.encode('utf-8')

def mtPacket(cmd, variable, payload):
  #returns 2 bytes and also a variable section and a payload
  return bytes([cmd, len(variable) + len(payload)]) + variable + payload

def mtpConnect(name):
  return mtPacket(
    0b00010000,     #connect command byte 
    mtStr("MQTT") + # protocol name part of variable header
    b'\x04' +       # protocol level part of variable header
    b'\x00' +       # connect flag part of variable header
    b'\xFF\xFF',    # keepalive part of variable header
    mtStr(name)     # payload
                 )

def mtpSubscribe(topic):
  return mtPacket(
    0b10000010, #subscribe command byte
    bytes([0x00,0x00]), #MSB, LSB for packet identifier; test case no identifier
    mtStr(topic) + bytes([0x00]) #last two bits of last byte is for QoS; test case = 0
                 )

###########################################################

def run():
  wlan = network.WLAN(network.STA_IF)
  wlan.active(True)
  if not wlan.isconnected():
    print('connecting to network...')
    wlan.connect(ssid, pw)
    while not wlan.isconnected():
      pass
  print('network config:', wlan.ifconfig())     

  s = socket.socket()
  s.connect((host, 1883))
  s.send(mtpConnect("somename"))
  m = s.recv(100)
  print("CONNACK = ", m)
  sleep(1) 
  s.send(mtpSubscribe("test"))
  m = s.recv(100)
  print("SUBACK = ", m)

  while 1:
    m = s.recv(1024)
    if m:
      topic = m[4:4+m[3]]
      msg = m[4+m[3]:]
      msg = msg.decode('utf-8')
      
     # to display on OLED; not using micropython git repository driver but my own
      d.clear()
      d.display()
      d.draw_text(0, 0, msg)
      d.display()
    sleep(1)
    
run()

Post Reply