使用 Docker 搭建 EMQX,并通过 MicroPython 实现 MQTT 控制 ESP32 LED

使用 Docker 搭建 EMQX,并通过 MicroPython 实现 MQTT 控制 ESP32 LED

在物联网项目中,MQTT 是最常用的消息通信协议之一。本文将通过一个完整示例,演示如何:

  1. 使用 Docker 快速部署 EMQX
  2. 解决端口冲突问题
  3. 使用 MicroPython 作为 MQTT 客户端
  4. 通过 MQTT 消息 远程控制 ESP32 LED

一、使用 Docker 快速启动 EMQX

1. 基础启动命令

最基础的 EMQX 容器启动方式如下:

bash 复制代码
docker run -itd \
  --name emqx \
  -p 1883:1883 \
  -p 8083:8083 \
  -p 8084:8084 \
  -p 8883:8883 \
  -p 18083:18083 \
  emqx/emqx

容器启动成功后会返回类似 ID:

text 复制代码
185326ea778710a12c77ad3b3e24800ce7591c30f4feb0196742bfd70f655d14

这样:

  • 宿主机:1884
  • 容器内部:1883(MQTT 默认端口)

二、访问 EMQX 控制台

浏览器访问(HTTPS):

复制代码
https://服务器IP:18083

默认账号密码:

text 复制代码
用户名:admin
密码:public

控制台界面如下:


三、MicroPython MQTT 客户端实现

ESP32 使用 MicroPython ,MQTT 客户端代码基于 simple.py 实现。


1. MQTT 客户端库(simple.py)

文件名:simple.py

python 复制代码
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=None,
    ):
        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.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):
        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)
        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:
            sz += 2 + len(self.user) + 2 + len(self.pswd)
            msg[6] |= 0xC0

        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)
        self._send_str(self.client_id)
        if self.user:
            self._send_str(self.user)
            self._send_str(self.pswd)

        resp = self.sock.read(4)
        if resp[3] != 0:
            raise MQTTException(resp[3])

    def subscribe(self, topic, qos=0):
        self.cb is not None
        pkt = bytearray(b"\x82\0\0\0")
        self.pid += 1
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
        self.sock.write(pkt)
        self._send_str(topic)
        self.sock.write(qos.to_bytes(1, "little"))

    def check_msg(self):
        self.sock.setblocking(False)
        try:
            res = self.sock.read(1)
        except:
            return
        if res == b"\x30":
            self.sock.read(1)
            tlen = self.sock.read(2)
            tlen = (tlen[0] << 8) | tlen[1]
            topic = self.sock.read(tlen)
            msg = self.sock.read()
            self.cb(topic, msg)

四、ESP32 主程序(控制 LED)

文件名:main.py

python 复制代码
from simple import MQTTClient
from machine import Pin
import network

led = Pin(2, Pin.OUT)

SSID = "wifi名称"
PASSWORD = "wifi密码"

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
    wlan.connect(SSID, PASSWORD)
    while not wlan.isconnected():
        pass

print("Network config:", wlan.ifconfig())

SERVER = "192.168.31.219"
PORT = 1884
CLIENT_ID = "my_micropython_client"

def sub(topic, msg):
    print(topic, msg)
    if topic == b"led002":
        if msg == b"on":
            led.value(0)
        elif msg == b"off":
            led.value(1)

client = MQTTClient(
    CLIENT_ID,
    SERVER,
    PORT,
    "test",
    "123456"
)

client.set_callback(sub)
client.connect()
client.subscribe(b"led002")

while True:
    client.check_msg()

五、测试效果

通过 EMQX 控制台或其他 MQTT 客户端:

  • Topic:led002

  • Payload:

    • on → LED 亮
    • off → LED 灭

设备成功订阅并响应:

相关推荐
正经教主6 小时前
【FDE系列】阶段2:Day 42(UP详细版):Dockerfile 实战 — 把你的应用打包成镜像
人工智能·docker·fde
刘马想放假6 小时前
Copyparty 轻量文件共享:Docker Compose 部署、权限与多协议使用全指南
docker·容器
岁寒良木9 小时前
麒麟 V10 (x86_64) 离线/联网部署 Docker 26.1.4 与 MaxKB v2.10 LTS 实战指南
python·docker·kylin
weixin_4043806310 小时前
MQTT 上云验收:ESP32-S3 从局域网控灯到远程网页控制
物联网·mqtt·esp32
正经教主13 小时前
【FDE系列】阶段2:Day 43(UP详细版):Docker Compose — 多容器一键编排
运维·docker·容器
csdn_aspnet13 小时前
构建、运行与管理容器的 18 个常用 Docker 命令
docker·容器·eureka·command
木雷坞13 小时前
AnythingLLM Docker部署:中文资料上传后无引用的检查路径
运维·docker·容器
maochongsandai11014 小时前
Centos上运行docker应用命令汇总
linux·docker·centos
艾伦_耶格宇14 小时前
【kubernetes - 1.28版本】-4 containerd与docker-ce 的区别
docker·容器·kubernetes
正经教主15 小时前
【FDE系列】阶段2:Day 45(up详细版):综合实战 — Docker + Nginx + Git 完整部署与本周收官
人工智能·git·nginx·docker·fde