MicroPython的MQTT实现(asyncio协程版)

一、改自:umqtt.simple代码

https://github.com/micropython/micropython-lib/tree/master/umqtt.simple

二、免费MQTT测试服务器

https://www.emqx.cn/mqtt/public-mqtt5-broker
在这里插入图片描述
https://www.emqx.cn/mqtt/mqtt-websocket-toolkit
在这里插入图片描述

三、源代码(lib\umqtt\cw_simple.py)

import ustruct as struct
import uasyncio as asyncio
from ubinascii import hexlify
import ussl as ssl

class MQTTException(Exception):
    pass

class MQTTClient:

    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,
                 ssl=False, ssl_params={}):
        if port == 0:
            port = 8883 if ssl else 1883
        self.client_id = client_id
        self.server = server
        self.port = port
        self.ssl = ssl
        self.ssl_params = ssl_params
        self.pid = 0
        self.cb = None
        self.user = user
        self.pswd = password
        self.keepalive = keepalive
        self.lw_topic = None
        self.lw_msg = None
        self.lw_qos = 0
        self.lw_retain = False
        self.r = None
        self.w = None

    async def _recv_bytes(self, bytes_len):
        bs =  await self.r.read(bytes_len)
        return bs
    
    async def _send_bytes(self, bytes_data, bytes_len=None):
        if bytes_len == None:
            self.w.write(bytes_data)
        else:
            self.w.write(bytes_data[0:bytes_len])
        await self.w.drain()
            
    async def _send_str(self, s):
        await self._send_bytes(struct.pack("!H", len(s)))
        await self._send_bytes(s)

    async def _recv_len(self):
        n = 0
        sh = 0
        while 1:
            bn = await self._recv_bytes(1)
            b  = bn[0]
            n |= (b & 0x7f) << sh
            if not b & 0x80:
                return n
            sh += 7

    def set_callback(self, f):
        self.cb = f

    def set_last_will(self, topic, msg, retain=False, qos=0):
        assert 0 <= qos <= 2
        assert topic
        self.lw_topic = topic
        self.lw_msg = msg
        self.lw_qos = qos
        self.lw_retain = retain

    async def connect(self, clean_session=True):
        self.r, self.w = await asyncio.open_connection(host=self.server, port=self.port)
 
        if self.ssl:
            ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            ssl_context.check_hostname = False
            ssl_context.load_default_certs()      
            self.r, self.w = await asyncio.open_connection(host=self.server, port=self.port, ssl=ssl_context)
        
        premsg = bytearray(b"\x10\0\0\0\0\0")
        msg = bytearray(b"\x04MQTT\x04\x02\0\0")

        sz = 10 + 2 + len(self.client_id)
        msg[6] = clean_session << 1
        if self.user is not None:
            sz += 2 + len(self.user) + 2 + len(self.pswd)
            msg[6] |= 0xC0
        if self.keepalive:
            assert self.keepalive < 65536
            msg[7] |= self.keepalive >> 8
            msg[8] |= self.keepalive & 0x00FF
        if self.lw_topic:
            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
            msg[6] |= self.lw_retain << 5

        i = 1
        while sz > 0x7f:
            premsg[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        premsg[i] = sz
        
        await self._send_bytes(premsg, i + 2)
        await self._send_bytes(msg)
        #print(hex(len(msg)), hexlify(msg, ":"))
        await self._send_str(self.client_id)
        if self.lw_topic:
            await self._send_str(self.lw_topic)
            await self._send_str(self.lw_msg)
        if self.user is not None:
            await self._send_str(self.user)
            await self._send_str(self.pswd)

        resp = await self._recv_bytes(4)
        assert resp[0] == 0x20 and resp[1] == 0x02
        if resp[3] != 0:
            raise MQTTException(resp[3])
        
        return resp[2] & 1

    async def disconnect(self):
        await self._send_bytes(b"\xe0\0")
        self.w.close()
        await self.w.wait_closed()

    async def ping(self):
        await self._send_bytes(b"\xc0\0")

    async def publish(self, topic, msg, retain=False, qos=0):
        pkt = bytearray(b"\x30\0\0\0")
        pkt[0] |= qos << 1 | retain
        sz = 2 + len(topic) + len(msg)
        if qos > 0:
            sz += 2
        assert sz < 2097152
        i = 1
        while sz > 0x7f:
            pkt[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        pkt[i] = sz
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        await self._send_bytes(pkt, i + 1)
        await self._send_str(topic)
        if qos > 0:
            self.pid += 1
            pid = self.pid
            struct.pack_into("!H", pkt, 0, pid)
            await self._send_bytes(pkt, 2)
        await self._send_bytes(msg)
        if qos == 1:
            while 1:
                op = await self.wait_msg()
                if op == 0x40:
                    sz = await self._recv_bytes(1)
                    assert sz == b"\x02"
                    rcv_pid = await self._recv_bytes(2)
                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
                    if pid == rcv_pid:
                        return
        elif qos == 2:
            assert 0

    def subscribe(self, topic, qos=0):
        assert self.cb is not None, "Subscribe callback is not set"
        pkt = bytearray(b"\x82\0\0\0")
        self.pid += 1
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        await self._send_bytes(pkt)
        await self._send_str(topic)
        await self._send_bytes(qos.to_bytes(1, "little"))
        while 1:
            op = await self.wait_msg()
            if op == 0x90:
                resp = await self._recv_bytes(4)
                #print(resp)
                assert resp[1] == pkt[2] and resp[2] == pkt[3]
                if resp[3] == 0x80:
                    raise MQTTException(resp[3])
                return

    # Wait for a single incoming MQTT message and process it.
    # Subscribed messages are delivered to a callback previously
    # set by .set_callback() method. Other (internal) MQTT
    # messages processed internally.
    def wait_msg(self):
        res = await self._recv_bytes(1)
        if res is None:
            return None
        if res == b"":
            print("res is empty!")
            #raise OSError(-1)
            return None            
        if res == b"\xd0":  # PINGRESP
            sz_n = await self._recv_bytes(1)
            sz = sz_n[0]
            print('sz = {0}'.format(sz))
            assert sz == 0
            return None
        op = res[0]
        if op & 0xf0 != 0x30:
            return op
        sz = await self._recv_len()
        topic_len = await self._recv_bytes(2)
        topic_len = (topic_len[0] << 8) | topic_len[1]
        topic = await self._recv_bytes(topic_len)
        sz -= topic_len + 2
        if op & 6:
            pid = await self._recv_bytes(2)
            pid = pid[0] << 8 | pid[1]
            sz -= 2
        msg = await self._recv_bytes(sz)
        await self.cb(topic, msg)
        if op & 6 == 2:
            pkt = bytearray(b"\x40\x02\0\0")
            struct.pack_into("!H", pkt, 2, pid)
            await self._send_bytes(pkt)
        elif op & 6 == 4:
            assert 0
        
        return op

四、源代码(test_mqtt.py)

import ujson
import time
import dht
from machine import Pin, ADC, Timer
import uasyncio as asyncio
from umqtt.cw_simple import MQTTClient

class MyIotPrj:
    def __init__(self):
        client_id = "slim_id"
        self.mserver   = 'broker.emqx.io'
        port           = 1883
        self.client = MQTTClient(client_id, self.mserver, 0)
        self.isconn = False

        self.topic_ctl = b'demo_ctl'
        self.topic_sta = b'demo_sta'

    async def sub_callback(self, topic, msg):
        print((topic, msg))

    async def mqtt_main_thread(self):

        try:
            self.client.set_callback(self.sub_callback)

            conn_ret_code = await self.client.connect()
            if conn_ret_code != 0:
                return
                            
            print('conn_ret_code = {0}'.format(conn_ret_code))
            
            await self.client.subscribe(self.topic_ctl)
            print("Connected to %s, subscribed to %s topic" % (self.mserver, self.topic_ctl))
            
            self.isconn = True

            while True:
                await self.client.wait_msg()
                await asyncio.sleep(1)
                print('wait_msg')
        finally:
            if self.client is not None:
                print('off line')
                await self.client.disconnect()
        
        self.isconn = False

    async def mqtt_upload_thread(self):
#        my_dht = dht.DHT11(Pin(14, Pin.IN))
        dht_data = {
            'temperature':0,
            'humidity':0
            }
        
        while True:
            if self.isconn == True:
#                my_dht.measure()
#                dht_data['temperature'] = my_dht.temperature()
#                dht_data['humidity']    = my_dht.humidity()
                print(dht_data)
                await self.client.publish(self.topic_sta, ujson.dumps(dht_data), retain=True)
            
            await asyncio.sleep(1)

        while True:
            if self.isconn == True:
                await self.client.ping()
            await asyncio.sleep(5)
            
def main():
    mip = MyIotPrj()
    
    loop = asyncio.get_event_loop()
    loop.create_task(mip.mqtt_main_thread())
    loop.create_task(mip.mqtt_upload_thread())
    loop.run_forever()
 
if __name__ == '__main__':
    main()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值