Hi3861使用micropython实现mqtt功能 原创 精华

再见南丫岛
发布于 2022-3-4 11:45
浏览
6收藏

1、MQTT协议介绍

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。它工作在 TCP/IP协议族上。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。

2、MQTT客户端

MQTT.fx软件,可以实现mqtt的协议测试。
Hi3861使用micropython实现mqtt功能-鸿蒙开发者社区

3、测试服务器

可以使用以下服务,进行测试。
http://tongxinmao.com/App/Detail/id/126
服务器:mq.tongxinmao.com TCP端口18830 WS端口18832 用户/pwd:空 。
Hi3861使用micropython实现mqtt功能-鸿蒙开发者社区

4、Openharmony的移植

网上有相关的paho_mqtt移植,在此不多描述。如果需要在micropython上使用该库,需要自己编写接口即可。

STATIC const mp_rom_map_elem_t mqtt_locals_dict_table[] = {
    { MP_ROM_QSTR(MP_QSTR_set_info), MP_ROM_PTR(&mqtt_set_info_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_id), MP_ROM_PTR(&mqtt_set_id_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_server), MP_ROM_PTR(&mqtt_set_server_obj) },
    { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&mqtt_connect_obj) },
    { MP_ROM_QSTR(MP_QSTR_publish), MP_ROM_PTR(&mqtt_publish_obj) },
    { MP_ROM_QSTR(MP_QSTR_subscribe), MP_ROM_PTR(&mqtt_subscribe_obj)},
    { MP_ROM_QSTR(MP_QSTR_waitmsg), MP_ROM_PTR(&mqtt_waitmsg_obj)},
    { MP_ROM_QSTR(MP_QSTR_test), MP_ROM_PTR(&mqtt_test_obj) },
    { MP_ROM_QSTR(MP_QSTR_set_cb), MP_ROM_PTR(&mqtt_set_cb_obj) },
};

5、使用python库函数

加载这三个库

import usocket as socket
import ustruct as struct
from ubinascii import hexlify

然后添加该py文件,这样就可以引用该文件实现mqtt功能。

import usocket as socket
import ustruct as struct
from ubinascii import hexlify

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.sock = None
        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

    def _send_str(self, s):
        self.sock.write(struct.pack("!H", len(s)))
        self.sock.write(s)

    def _read_len(self):
        n = 0
        sh = 0
        while 1:
            b = self.sock.read(1)[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

    def connect(self, clean_session=True):
        self.sock = socket.socket()
        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
        self.sock.connect(addr)
        if self.ssl:
            import ussl
            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
        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

        self.sock.write(premsg, i + 2)
        self.sock.write(msg)
        #print(hex(len(msg)), hexlify(msg, ":"))
        self._send_str(self.client_id)
        if self.lw_topic:
            self._send_str(self.lw_topic)
            self._send_str(self.lw_msg)
        if self.user is not None:
            self._send_str(self.user)
            self._send_str(self.pswd)
        resp = self.sock.read(4)
        #print(resp)
        assert resp[0] == 0x20 and resp[1] == 0x02
        if resp[3] != 0:
            raise MQTTException(resp[3])
        return resp[2] & 1

    def disconnect(self):
        self.sock.write(b"\xe0\0")
        self.sock.close()

    def ping(self):
        self.sock.write(b"\xc0\0")

    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, ":"))
        self.sock.write(pkt, i + 1)
        self._send_str(topic)
        if qos > 0:
            self.pid += 1
            pid = self.pid
            struct.pack_into("!H", pkt, 0, pid)
            self.sock.write(pkt, 2)
        self.sock.write(msg)
        if qos == 1:
            while 1:
                op = self.wait_msg()
                if op == 0x40:
                    sz = self.sock.read(1)
                    assert sz == b"\x02"
                    rcv_pid = self.sock.read(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, ":"))
        self.sock.write(pkt)
        self._send_str(topic)
        self.sock.write(qos.to_bytes(1, "little"))
        while 1:
            op = self.wait_msg()
            if op == 0x90:
                resp = self.sock.read(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 = self.sock.read(1)
        self.sock.setblocking(True)
        if res is None:
            return None
        if res == b"":
            raise OSError(-1)
        if res == b"\xd0":  # PINGRESP
            sz = self.sock.read(1)[0]
            assert sz == 0
            return None
        op = res[0]
        if op & 0xf0 != 0x30:
            return op
        sz = self._read_len()
        topic_len = self.sock.read(2)
        topic_len = (topic_len[0] << 8) | topic_len[1]
        topic = self.sock.read(topic_len)
        sz -= topic_len + 2
        if op & 6:
            pid = self.sock.read(2)
            pid = pid[0] << 8 | pid[1]
            sz -= 2
        msg = self.sock.read(sz)
        self.cb(topic, msg)
        if op & 6 == 2:
            pkt = bytearray(b"\x40\x02\0\0")
            struct.pack_into("!H", pkt, 2, pid)
            self.sock.write(pkt)
        elif op & 6 == 4:
            assert 0

    # Checks whether a pending message from server is available.
    # If not, returns immediately with None. Otherwise, does
    # the same processing as wait_msg.
    def check_msg(self):
        self.sock.setblocking(False)
        return self.wait_msg()

mqtt的使用

from umqtt import MQTTClient

ssid='your ssid'
passwd='your passwd'
client_id = "slim_id"
mserver = 'mq.tongxinmao.com'
port=18830

topic_ctl = b'/public/TEST/jwebcli'#设备订阅的主题,客户端推送消息的主题
topic_sta = b'/public/TEST/jwebcli'#客户端订阅的主题,设备推送消息的主题

def sub_callback(topic, msg):
    """
    收到订阅消息回调
    """
    print(msg)
    
client = MQTTClient(client_id,mserver,port)
client.set_callback(sub_callback)
client.connect()
client.subscribe(topic_ctl)
client.publish(topic_sta, 'Device online', retain=True)
print("Connected to %s, subscribed to %s topic" % (mserver, topic_ctl))
while True:
        client.wait_msg()

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
umqtt.py.zip 1.85K 50次下载
已于2022-3-4 11:46:11修改
7
收藏 6
回复
举报
2条回复
按时间正序
/
按时间倒序
鸿联
鸿联

很实用的功能

回复
2022-3-4 20:05:21
清风彬彬
清风彬彬

nice,又学习了一波

回复
2022-3-8 08:02:23
回复
    相关推荐