Micropython development esp32 entry notes--MQTT articles


1. Introduction to MQTT

insert image description here

MQTT (Message Queuing Telemetry Transport, Message Queue Telemetry Transport Protocol), is a "lightweight" communication protocol based on the client-server message publish/subscribe (publish/subscribe) mode, the underlying layer of which is the TCP/IP protocol . The biggest advantage of MQTT is that it can provide real-time and reliable message services for connected remote devices with very little code and limited bandwidth. As a low-overhead, low-bandwidth instant messaging protocol, it has a wide range of applications in the Internet of Things, small devices, and mobile applications.

The MQTT protocol is lightweight, simple, open and easy to implement. These characteristics make it widely applicable. In many cases, including constrained environments such as machine-to-machine (M2M) communication and the Internet of Things (IoT). It is used extensively in sensors communicating via satellite links, medical devices that occasionally dial, smart homes, and some miniaturized devices.

2. The principle of MQTT data flow

The most important thing about MQTT is the mode of publishing and subscribing. The server is the root node and plays the role of the leadership center; the client is the child node and plays the role of a subordinate. Each role can act as a publisher and a subscriber.

insert image description here

The publisher (Publisher)
is responsible for publishing the message to the topic. The publisher can only send data to one topic at a time, and the publisher does not need to care whether the subscriber is online or not when publishing the message.

Subscriber (Subscriber)
Subscribers receive messages by subscribing to topics, and can subscribe to multiple topics at a time. MQTT also supports load balancing of subscriptions among multiple subscribers by sharing subscriptions.

Brokers
are responsible for receiving messages from publishers and forwarding messages to eligible subscribers. In addition, the agent also needs to be responsible for handling the connection, disconnection, subscription, unsubscription and other requests initiated by the client.

Topic (Topic)
Topic is the basis of MQTT for message routing. It is similar to URL path, using slashes for hierarchy, such as sensor/1/temperature. A topic can have multiple subscribers, and the broker will forward messages under the topic to all subscribers; a topic can also have multiple publishers, and the broker will forward messages in the order they arrive. Subscribers subscribe to the agent for topics they are interested in, and all messages published by the publisher will contain their own topics, and the agent judges which subscribers to forward the message to according to the topic of the message.

The content (payload)
can be understood as the content of the message, which refers to the specific content that the subscriber wants to use. Subscribers define the conditions of the messages they are interested in. Only when the properties or content of the message meet the conditions defined by the subscriber, the message will be delivered to the subscriber.

Easy to understand, imagine a leader holding a year-end summary meeting for representatives of different departments. The representatives of each department want to know about the business situation of other departments, then they will tell the leader the topics they are interested in, and then the leader will share relevant information. The Department Representative's published Business Situation Summary informs other Department Representatives who are interested.

3. ESP32 development - based on MQTT protocol

1. Principle of ultrasonic sensor

insert image description here

Ultrasonic sensors are used to measure the distance of objects. First of all, the ultrasonic sensor will emit a group of high-frequency sound waves, generally 40-45KHz, when the sound waves encounter objects, they will be bounced back and received. By calculating the time from launch to return of the sound wave, multiplied by the speed of propagation of the sound wave in the medium (344 m/s, in air). The distance value of the object relative to the sensor can be obtained.

insert image description here

The ultrasonic sensor we use is HC − SR 04 HC-SR04HCSR 04 module, according to the above timing diagram, the overall implementation idea is:
(1) Use IO port TRIG to trigger ranging, first let the pin of ESP32 give TRIG high level.
(2) The module automatically sends 8 square waves of 40khz, and automatically detects whether there is a signal return; (
3) Detects whether there is a signal return from ECHO. When the signal returns, ECHO will output a high level. The time from launch to return, so the high level of the ECHO pin starts timing, and converts the time into distance after calculation

2. Micropython realizes ultrasonic ranging

When Micropython is implemented, it must first encapsulate several modules, network connection function, message callback function, distance measurement function, and then call the networking function and then instantiate the MQTT client. When instantiating, the client name and MQTT server IP must be passed, and then Set the callback function and establish the connection between the client and the server, subscribe to the "Data" topic. If the content of the topic is "Distance", then send the value returned by the ranging function to the server, and other clients subscribing to the topic will receive the distance data

# umqttsimple.py

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 _recv_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)
        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._recv_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()
# main.py

import time
import network
from umqttsimple import MQTTClient
from machine import Pin, PWM


def do_connect():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect('SSID', 'password')
        i = 1
        while not wlan.isconnected():
            print("正在链接...{}".format(i))
            i += 1
            time.sleep(1)
    print('network config:', wlan.ifconfig())


def sub_cb(topic, msg, distance): # 回调函数,收到服务器消息后会调用这个函数
    print(topic, msg)
    
    if topic.decode("utf-8") == "data" and msg.decode("utf-8") == "distance":
        c.publish(b"data", str(distance))
        
            
    
def measure(trig, echo):
    trig.value(1)
    time.sleep_us(10)
    trig.value(0)
    while echo.value() == 0:
        t1 = time.ticks_us()
        print("---------------------")
        print(t1)
    # 检测回响信号,为高电平时,测距开始
    while self.echo.value() == 1:
        # 开始不断递增的微秒计数器 2
        t2 = time.ticks_us()
    
        print(t2)
        t3 = time.ticks_diff(t2, t1) / 10000
        print(t3, t2-t1)
        return t3 * 340 / 2

    
            # 1. 联网
do_connect()
trig = Pin(14, Pin.OUT)
echo = Pin(12, Pin.IN)

# 2. 创建mqt
c = MQTTClient("umqtt_client", "192.168.31.195")  # 建立一个MQTT客户端
c.set_callback(sub_cb)  # 设置回调函数
c.connect()  # 建立连接
c.subscribe(b"data")

while True:
    distance = measure(trig, echo)
    c.check_msg()
    time.sleep(1)

3. Create MQTT server and other MQTT clients

The blogger used docker to pull the image of EMQX to create an MQTT server on the Raspberry Pi 4B before. Of course, you can also pull the images of mosquitto and rabbitmq and create a local MQTT server. The IP address is the IP of the Raspberry Pi.

insert image description here

After creating the MQTT server, visit https://mqttx.app/zh and download the MQTT client on the computer

insert image description here

Create a client after downloading, create the Name and Client ID by yourself, fill in the address of the MQTT server for the Host address, the default port is 1883, and then connect

insert image description here
After the connection is complete, just send the corresponding topic and content. For example, if you send the "Distance" subscription topic on the computer client, you will receive the distance data back; another example is if you send the "led" release topic and "on" The data on the esp32 will be on, so that remote control in the LAN can be realized.

Summarize

The above is the content of Micropython's development of esp32 introductory notes. This article briefly introduces the development of esp32 peripherals based on the MQTT protocol. The MQTT protocol is one of the most popular protocols under the IOT framework of the Internet of Things. It provides a great development space for the interconnection of small devices and the realization of one-to-many and many-to-one information transmission.

Guess you like

Origin blog.csdn.net/m0_55202222/article/details/129371125