W55RP20-EVB-MKR MicroPython 实战(15):MQTT协议与ThingSpeak平台对接

本文为 WIZnet W55RP20 开发板 MicroPython 教程,基于官方最新固件编写,聚焦MQTT协议与ThingSpeak平台对接,流程均经过实际验证,代码可自行编写适配。

前言

上一篇教程中,我们已经完成了 MQTT 协议与ONENET 的开发,实现了设备上云、物模型数据定时上报与云端监控,而Thingspeak凭借注册即用,无需配置服务器,双协议支持,数据格式灵活等特点受到越来越多人的喜爱。

本文将带你从零开始,完成W55RP20的MicroPython环境配置、MQTT协议调试,以及与ThingSpeak物联网平台的对接,实现自定义温湿度数据的定时上报。

学完本文,你将掌握:

W55RP20的硬件了解与开发环境配置

MicroPython中SPI接口初始化与网络配置(DHCP/静态IP)

MQTT协议基础配置与ThingSpeak平台对接

常见故障排查与调试技巧

系列教程学习路径

本专栏共 16 篇,循序渐进覆盖 W55RP20-EVB-Pico 模块 MicroPython 开发全流程:

  1. 第 1 篇:静态 IP 配置与网络基础
  2. 第 2 篇:DHCP 自动联网与网络诊断
  3. 第 3 篇:TCP Client 客户端通信
  4. 第 4 篇:TCP Server 服务端通信
  5. 第 5 篇:UDP 单播数据通信
  6. 第 6 篇:UDP 组播/广播数据通信
  7. 第 7 篇:DNS 域名解析
  8. 第 8 篇:NTP 从网络获取时间
  9. 第 9 篇:HTTP Client 客户端请求
  10. 第 10 篇:HTTP Server 服务端搭建
  11. 第 11 篇:HTTP 协议与 OneNET 平台数据上云
  12. 第 12 篇:MQTT 协议基础通信验证
  13. 第 13 篇:MQTT 协议与阿里云平台对接
  14. 第 14 篇:MQTT 协议与 OneNET 平台对接
  15. 第 15 篇:MQTT 协议与 ThingSpeak 平台对接 (本文)
  16. 第 16 篇:Modbus 工业协议通信

建议收藏本专栏,跟随教程逐步学习,所有代码均会同步更新至官方 Gitee 仓库

目录

前言

系列教程学习路径

[1. 准备工作](#1. 准备工作)

[1.1 软件准备](#1.1 软件准备)

[1.2 硬件准备](#1.2 硬件准备)

[2. 烧录 W55RP20-EVB-MKR 模块专属 MicroPython 固件](#2. 烧录 W55RP20-EVB-MKR 模块专属 MicroPython 固件)

[3. 硬件连接与开发环境配置](#3. 硬件连接与开发环境配置)

[3.1 硬件连接](#3.1 硬件连接)

[3.1.1 基础连接(供电+调试)](#3.1.1 基础连接(供电+调试))

[3.1.2 以太网连接](#3.1.2 以太网连接)

[3.1.3 模块与开发板接线](#3.1.3 模块与开发板接线)

[3.2 Thonny 开发环境配置](#3.2 Thonny 开发环境配置)

[4. ThingSpeak平台操作流程](#4. ThingSpeak平台操作流程)

[5. 示例流程讲解](#5. 示例流程讲解)

[5.1 程序流程图](#5.1 程序流程图)

[5.2 测试准备](#5.2 测试准备)

[5.3 连接方式](#5.3 连接方式)

[5.4 相关代码](#5.4 相关代码)

[5.5 烧录验证](#5.5 烧录验证)

[6. 常见问题一站式排查指南](#6. 常见问题一站式排查指南)

[6.1 烧录相关问题](#6.1 烧录相关问题)

[6.2 网络连接问题](#6.2 网络连接问题)

[6.3 MQTT对接问题](#6.3 MQTT对接问题)

[7. W55RP20核心优势](#7. W55RP20核心优势)

[8. 典型应用场景](#8. 典型应用场景)

[9. 系列预告与资源获取](#9. 系列预告与资源获取)

9.1系列预告

[9.2 资源获取](#9.2 资源获取)

1. 准备工作

1.1 软件准备

所需软件均为免费版本,按要求下载安装即可,无需额外付费。

软件名称 版本要求 下载地址 说明
Thonny 4.0 及以上 Thonny 官方下载 轻量级 MicroPython IDE,支持代码编辑、烧录与串口调试,新手友好
W55RP20-EVB-MKR 模块MicroPython语言驱动库 最新稳定版 WIZnet 官方固件/驱动库下载 专为 W55RP20-EVB-MKR 模块编写,已集成 WIZnet 硬件驱动、TCP/IP协议栈及MQTT客户端库
串口调试助手(如SecureCRT) 任意版本 官方下载或第三方工具 用于查看串口输出的运行日志、调试信息,定位连接和数据上传问题
ThingSpeak物联网平台 在线版 ThingSpeak物联网平台官网 创建通道、获取MQTT设备凭证,查看上传的温湿度数据及趋势图表

1.2 硬件准备

  • W55RP20-EVB-MKR × 1
  • Micro USB 数据线(必须支持数据传输,不能使用纯充电线)× 1
  • 标准网线 × 1
  • 开启 DHCP 功能的路由器 / 交换机 × 1(用于获取网络参数,实现 DNS 解析)

W55RP20-EVB-MKR 模块已集成以太网相关器件,无需额外焊接飞线,配合 RP2040 开发板可快速搭建开发环境,大幅降低接线错误和硬件故障概率。

2. 烧录 W55RP20-EVB-MKR 模块专属 MicroPython 固件

W55RP20-EVB-Pico 模块 完全兼容树莓派 Pico 的 UF2 固件烧录方式,操作简单无需额外烧录器,新手可快速上手:

  1. 按住 RP2040 开发板上的 BOOTSEL 按键不放;
  2. 使用 Micro USB 数据线连接开发板与电脑;
  3. 待电脑识别出名为 RPI-RP2 的 U 盘后,松开 BOOTSEL 按键;
  4. 将下载好的 W5500_RP2040_firmware.uf2 固件文件拖拽到 U 盘中;
  5. 开发板会自动重启,固件烧录完成。

注意:如果电脑没有识别出 RPI-RP2 U 盘,请尝试更换 USB 数据线、重新插拔开发板,或更换电脑 USB 接口(优先使用 USB 2.0 接口)。

3. 硬件连接与开发环境配置

3.1 硬件连接

W55RP20-EVB-MKR 模块连接分为两步,分别实现供电/调试和以太网连接,操作简单,无需复杂接线:

3.1.1 基础连接(供电+调试)

使用 Micro USB 数据线连接 RP2040 开发板与电脑,用于开发板供电、代码烧录和串口调试。

3.1.2 以太网连接

使用网线连接 W55RP20-EVB-MKR 模块的以太网接口与路由器的 LAN 口(或直接连接电脑网口,需手动配置电脑 IP 与开发板同网段)。

3.1.3 模块与开发板接线

若使用分离式模块与开发板,需按以下引脚对应连接(SPI 通信):

3.2 Thonny 开发环境配置

打开 Thonny 软件,按以下步骤配置开发环境,确保代码能正常烧录和运行:

  1. 点击顶部菜单栏「运行」→「配置解释器」;
  2. 切换到「解释器」选项卡;
  3. 在「解释器」下拉列表中选择 MicroPython (通用);
  4. 在「端口」下拉列表中选择开发板对应的串口(通常显示为 Board CDC @ COMx);
  5. 勾选「运行代码前先重启解释器」和「同步设备的实时时钟」;
  6. 点击「确定」完成配置。

如果端口列表中没有出现开发板,请尝试:

  • 重新插拔 USB 数据线;

  • 更换支持数据传输的 USB 数据线;

  • 关闭其他占用串口的软件(如串口助手、Arduino IDE 等);

  • 重新烧录 MicroPython 固件;

  • 安装树莓派 Pico USB 驱动。

4. ThingSpeak平台操作流程

ThingSpeak是一款轻量级物联网云平台,支持数据上传、存储与可视化,适合快速验证物联网数据上报功能,操作流程如下:

第一步:创建通道

  1. 访问ThingSpeak官方网站(https://thingspeak.com/),注册并登录账号;

  2. 点击顶部菜单栏「Channels」→「Create New Channel」,进入通道创建页面;

  3. 填写通道基本信息(如通道名称、描述),在「Field 1」填写"Temperature"(温度),「Field 2」填写"Humidity"(湿度),其他参数默认;

  4. 点击页面底部「Save Channel」,完成通道创建,记录页面显示的「Channel ID」(后续配置MQTT主题使用)。

第二步:获取MQTT连接参数

  1. 进入创建好的通道,点击顶部「API Keys」,查看并保存「Write API Key」(用于数据上报权限验证);

  2. 点击顶部「MQTT Settings」,获取MQTT连接核心参数,具体如下表(实际参数以个人平台显示为准):

|-------------|--------------------------|
| 参数名 | 参数值 |
| mqttHostUrl | mqtt3.thingspeak.com(固定) |
| port | 1883(固定) |
| clientId | Nx45MSAgCAkFNzQYDwwOOAc |
| username | Nx45MSAgCAkFNzQYDwwOOAc |
| passwd | WGG/tlO7RcEA89G8Ab+vIhmW |
| 发布主题 | channels/2362531/publish |

注意:发布主题中的"2362531"为个人Channel ID,需替换为自己创建通道的ID;passwd需妥善保存,用于MQTT连接身份验证。

5. 示例流程讲解

5.1 程序流程图

程序运行流程如下(代码自行编写时可参考此流程):

开发板上电 → 初始化SPI外设与GPIO(仅需配置DHT11传感器引脚,W5500与RP2040已内置连接) → W5500芯片初始化 → 尝试DHCP自动获取IP地址(失败则使用静态IP) → 初始化MQTT客户端 → 连接ThingSpeak MQTT服务器(失败则重启重连) → 初始化定时器(定时采集数据) → 主循环监听MQTT消息 → 定时器触发,采集DHT11温湿度数据 → 上报数据至ThingSpeak平台 → 重复循环。

5.2 测试准备

软件准备:

  1. 安装Thonny 4.0及以上版本,配置W55RP20开发环境;

  2. 下载W55RP20专属MicroPython固件;

  3. 下载umqttsimple.py库,导入W55RP20开发板;

  4. 注册ThingSpeak账号,创建通道并获取MQTT连接参数。

硬件准备:

  1. 准备W55RP20开发板,确认无硬件损坏;

  2. 准备Micro USB数据线、网线,确保路由器开启DHCP功能。

5.3 连接方式

  1. 开发板与电脑连接:使用Micro USB数据线连接W55RP20开发板与电脑,用于供电、代码烧录和串口调试;

  2. 开发板与网络连接:使用网线连接W55RP20的以太网接口与路由器的LAN口(确保电脑也连接同一路由器);

5.4 相关代码

mqtt_speak代码:

python 复制代码
# ==============================
# W55RP20 以太网初始化
# ==============================
import network
import time
from machine import Pin, WIZNET_PIO_SPI, Timer
from umqttsimple import MQTTClient, MQTTException

# 初始化以太网(稳定版)
def init_w55rp20():
    print("[ETH] 初始化 W55RP20...")
    spi = WIZNET_PIO_SPI(
        baudrate=20000000,  # 降速避免驱动报错
        sck=Pin(21),
        cs=Pin(20),
        mosi=Pin(23),
        miso=Pin(22)
    )
    nic = network.WIZNET6K(spi, Pin(20), Pin(25))
    nic.active(True)
    nic.ifconfig(("192.168.1.129", "255.255.255.0", "192.168.1.1", "8.8.8.8"))

    timeout = 0
    while not nic.isconnected() and timeout < 20:
        time.sleep(0.5)
        print("[ETH] 等待网线连接...")
        timeout += 1

    print("[ETH] 已连接")
    print("[ETH] IP:", nic.ifconfig())
    return nic

# ==============================
# 🔥🔥🔥 【100% 匹配你截图的真实账号】🔥🔥🔥
# ==============================
TS_CONFIG = {
    "host": "mqtt3.thingspeak.com",
    "port": 1883,
    "client_id": "DhMGLycADBorAAAxFCE9MBE",
    "user": "DhMGLycADBorAAAxFCE9MBE",
    "password": "lL+FaB1bnWFR9KlYIm7cTSnU",  # 你截图里的真实密码
    "topic": "channels/3360951/publish",
}

# 固定温湿度(你想改就改这里)
TEMP = 26
HUMI = 52
UPLOAD_INTERVAL = 15

# 全局变量(只创建一次客户端)
client = None
timer_cnt = 0

# 安全连接(修复内存泄漏)
def mqtt_connect():
    global client
    try:
        # 每次连接前清理旧连接
        client = MQTTClient(
            client_id=TS_CONFIG["client_id"],
            server=TS_CONFIG["host"],
            port=TS_CONFIG["port"],
            user=TS_CONFIG["user"],
            password=TS_CONFIG["password"],
            keepalive=30
        )
        client.connect(clean_session=True)
        print("[MQTT] 连接成功!")
        return True
    except MQTTException as e:
        print("[MQTT] 认证失败(账号错误):", e)
        return False
    except Exception as e:
        print("[MQTT] 网络错误:", e)
        return False

# 上传数据
def upload():
    payload = f"field1={TEMP}&field2={HUMI}"
    try:
        client.publish(TS_CONFIG["topic"], payload)
        print("[上传成功]", payload)
    except:
        print("[上传失败] 重新连接...")
        mqtt_connect()

# 定时器回调
def tick(t):
    global timer_cnt
    timer_cnt += 1
    if timer_cnt >= UPLOAD_INTERVAL:
        timer_cnt = 0
        upload()

# ==============================
# 主程序
# ==============================
def main():
    print("=== W55RP20 + ThingSpeak 温湿度上传 ===")
    init_w55rp20()
    time.sleep(1)

    # 只连接一次!避免内存爆炸
    while not mqtt_connect():
        print("等待 5 秒后重试...")
        time.sleep(5)

    # 启动定时上传
    tim = Timer(period=1000, mode=Timer.PERIODIC, callback=tick)

    while True:
        time.sleep(1)

if __name__ == "__main__":
    main()
python 复制代码
#umqttsimple.py file
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
        return op

    # 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()
python 复制代码
wiznet_init.py
import network
import time
try:
    from machine import Pin, WIZNET_PIO_SPI
except ImportError:
    WIZNET_PIO_SPI = None
    Pin = None

_DEFAULTS = {
    # Auto-construct boards (no explicit PIO SPI)
    "w5100s-evb-pico":  {},
    "w5500-evb-pico":   {},
    "w6100-evb-pico":   {},
    "w5100s-evb-pico2": {},
    "w5500-evb-pico2":  {},
    "w6100-evb-pico2":  {},

    # W55RP20 --- single SPI (PIO SPI)
    "w55rp20-evb-pico": {"baudrate": 31250000, "sck": 21, "cs": 20, "mosi": 23, "miso": 22, "reset": 25},

    # W6300 --- QSPI QUAD(io0..io3)
    "w6300-evb-pico":  {"baudrate": 31250000, "sck": 17, "cs": 16, "io0": 18, "io1": 19, "io2": 20, "io3": 21, "reset": 22},
    "w6300-evb-pico2": {"baudrate": 31250000, "sck": 17, "cs": 16, "io0": 18, "io1": 19, "io2": 20, "io3": 21, "reset": 22},
}
_AUTO = {
    "w5100s-evb-pico", "w5500-evb-pico", "w6100-evb-pico",
    "w5100s-evb-pico2","w5500-evb-pico2","w6100-evb-pico2",
}
_SINGLE = {"w55rp20-evb-pico"}   # PIO single-SPI
_QSPI   = {"w6300-evb-pico", "w6300-evb-pico2"}

def _pin(x): return x if isinstance(x, Pin) else Pin(x)

def wiznet(board, *, dhcp=True, spi=None, cs=None, reset=None, **kw):
    board = board.strip().lower()
    if board not in _DEFAULTS:
        raise ValueError("Unsupported board: {}".format(board))
    cfg = _DEFAULTS[board].copy()
    cfg.update(kw)
    
    # Manual override path: if spi is provided, use it directly
    if spi is not None:
        if cs is None or reset is None:
            raise ValueError("When passing custom spi, also pass cs and reset")
        nic = network.WIZNET6K(spi, cs, reset)
    else:
        if board in _AUTO:
            nic = network.WIZNET6K()

        elif board in _SINGLE:
            if WIZNET_PIO_SPI is None or Pin is None:
                raise RuntimeError("WIZNET_PIO_SPI/Pin not available on this port")
            required = ["sck", "cs", "mosi", "miso", "reset"]
            missing = [k for k in required if k not in cfg]
            if missing:
                raise ValueError("Missing pins for W55RP20 single-SPI: " + ", ".join(missing))
            spi = WIZNET_PIO_SPI(
                baudrate=cfg.get("baudrate", 31250000),
                sck=_pin(cfg["sck"]), cs=_pin(cfg["cs"]),
                mosi=_pin(cfg["mosi"]), miso=_pin(cfg["miso"]),
            )
            nic = network.WIZNET6K(spi, _pin(cfg["cs"]), _pin(cfg["reset"]))

        elif board in _QSPI:
            if WIZNET_PIO_SPI is None or Pin is None:
                raise RuntimeError("WIZNET_PIO_SPI/Pin not available on this port")
            for k in ["sck","cs","io0","io1","io2","io3"]:
                if k not in cfg: raise ValueError("Missing pin '{}' for W6300 QSPI".format(k))
            spi = WIZNET_PIO_SPI(
                baudrate=cfg.get("baudrate", 31250000),
                sck=_pin(cfg["sck"]), cs=_pin(cfg["cs"]),
                io0=_pin(cfg["io0"]), io1=_pin(cfg["io1"]),
                io2=_pin(cfg["io2"]), io3=_pin(cfg["io3"]),
            )
            nic = network.WIZNET6K(spi, _pin(cfg["cs"]), _pin(cfg.get("reset", cfg["cs"])))

        else:
            raise ValueError("Unexpected board mapping")

    # Bring up (if supported)
    try: nic.active(True)
    except AttributeError: pass

    if dhcp:
        try: nic.ifconfig("dhcp")
        except Exception: pass
    else:
        ip = cfg.get("ip"); sn = cfg.get("sn"); gw = cfg.get("gw"); dns = cfg.get("dns", gw or "8.8.8.8")
        if not (ip and sn and gw): raise ValueError("Static mode requires ip/sn/gw")
        nic.ifconfig((ip, sn, gw, dns))
    while not nic.isconnected():
        print("Waiting for the network to connect...")
        time.sleep(1)

    print("MAC Address:", ":".join("%02x" % b for b in nic.config("mac")))
    print("IP Address:", nic.ifconfig())
    return nic

5.5 烧录验证

烧录与验证步骤如下,确保每一步操作正确,避免出现连接或上报失败问题:

第一步:烧录固件

  1. 按住W55RP20开发板上的BOOTSEL按键不放,使用Micro USB数据线连接开发板与电脑;

  2. 待电脑识别出名为「RPI-RP2」的U盘后,松开BOOTSEL按键;

  3. 将下载好的W55RP20专属MicroPython固件(UF2格式)拖拽到U盘中,开发板会自动重启,固件烧录完成。

第二步:导入库文件

  1. 打开Thonny软件,配置解释器为「MicroPython (Raspberry Pi Pico)」,选择开发板对应的串口;

  2. 点击Thonny菜单栏「文件」→「新建」,粘贴umqttsimple.py库代码,保存为「umqttsimple.py」并上传到开发板根目录。wiznet_init也要保存到根目录下。

第三步:烧录自定义代码

  1. 新建文件,编写自定义代码(参考4.1程序流程图,配置MQTT参数、网络初始化等逻辑);

  2. 点击Thonny工具栏「运行」按钮(或按F5键),将代码烧录到开发板并运行。

第四步:验证结果

  1. 串口验证:在Thonny的Shell窗口中,查看是否打印网络配置信息、MQTT连接成功信息,以及定时温湿度上报消息;

  2. 平台验证:打开ThingSpeak个人通道页面,查看「Field 1」(温度)和「Field 2」(湿度)是否实时更新,若有数据显示,说明整个系统运行正常。

注意:因为MicroPython的print函数启用了stdout缓冲,有时不会第一时间打印内容,可耐心等待1-2秒,或在代码中添加time.sleep(0.5)缓解该问题。

6. 常见问题一站式排查指南

6.1 烧录相关问题

|-------------------|------------------------------------------------------------------------------------------------------|
| 问题现象 | 排查步骤 |
| 电脑无法识别「RPI-RP2」U盘 | 1. 确认按住BOOTSEL按键后再插入USB数据线; 2. 更换支持数据传输的USB数据线(避免使用纯充电线); 3. 更换电脑USB接口(优先使用USB 2.0接口); 4. 尝试使用另一台电脑。 |
| 固件拖拽后开发板无反应 | 1. 确认下载的是W55RP20专属固件,而非通用RP2040固件; 2. 重新烧录固件,确保拖拽过程中数据线未松动; 3. 检查USB供电是否稳定,可更换USB接口或使用外接电源。 |

6.2 网络连接问题

|-------------------|-------------------------------------------------------------------------------------------|
| 问题现象 | 排查步骤 |
| 网络初始化失败,一直打印寄存器信息 | 1. 确认网线插紧,网口指示灯是否闪烁; 2. 检查路由器是否开启DHCP功能,或尝试使用静态IP配置; 3. 更换网线或路由器LAN口; 4. 重新烧录W55RP20专属固件。 |
| IP地址显示为0.0.0.0 | 1. 执行上述网络连接排查步骤; 2. 确认W55RP20固件正确,重新烧录 ;3. 检查路由器DHCP地址池是否充足; 4. 重启路由器后重新尝试。 |

6.3 MQTT对接问题

|--------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| 问题现象 | 排查步骤 |
| 无法连接MQTT服务器,提示连接失败 | 1. 确认MQTT参数(url、port、clientid、username、passwd)填写正确; 2. 检查开发板与电脑是否连接同一路由器,网络通畅; 3. 确认ThingSpeak通道已创建,API Key未错误; 4. 尝试关闭电脑防火墙,重新运行程序。 |
| 能连接MQTT服务器,但无法上报数据 | 1. 检查发布主题是否正确(替换为个人Channel ID); 2. 确认消息格式符合ThingSpeak要求(field1=温度&field2=湿度); 3. 查看Thonny串口打印,确认传感器采集到有效数据;4. 检查umqttsimple库是否正确导入。 |

7. W55RP20核心优势

为了让你更直观地了解W55RP20的价值,对比目前主流的三种嵌入式以太网方案,突出其在MQTT物联网场景中的优势:

|---------|-------------------------------------------|-----------------------------|------------------------|
| 对比维度 | W55RP20方案 | 外接PHY芯片方案 | 外接串口转以太网模块方案 |
| BOM成本 | 低(一体化开发板,无需额外器件) | 中高(MCU + PHY芯片 + 外围器件) | 高(模块+开发板+转接电路) |
| PCB面积 | 极小(一体化设计,布线已集成) | 大(需预留芯片和布线空间) | 中(模块体积较大,需单独固定) |
| 开发难度 | 低(MicroPython成熟固件,API封装完善,无需手动配置SPI引脚) | 中高(需调试SPI接口、编写PHY驱动和软件协议栈) | 低(串口通信,无需调试协议栈) |
| 网络稳定性 | 极高(WIZnet硬件TCP/IP协议栈,抗干扰能力强,适合MQTT长期稳定上报) | 不定(依赖软件协议栈调试水平,易出现丢包、断连) | 一般(串口转以太网存在延迟,易受干扰) |
| CPU资源占用 | 0%(协议栈由W5500硬件处理,RP2040专注传感器采集和MQTT消息处理) | 50%以上(软件协议栈占用大量CPU资源,影响主业务) | 低(串口通信占用少量资源,但数据转发效率低) |
| 网络吞吐量 | 最高15Mbps(满足MQTT高频上报需求) | 视MCU能力而定,一般低于20Mbps | 约3-5Mbps(不适合高频数据上报) |
| MQTT适配性 | 强(支持8路硬件Socket,可同时实现数据上报与命令接收) | 一般(需手动移植MQTT库,适配难度高) | 弱(单路透传,无法同时处理多连接) |

8. 典型应用场景

W55RP20开发板,结合MicroPython的便捷开发和MQTT的轻量化通信,适合多种物联网场景,尤其适合快速原型开发和小型物联网项目:

  1. 环境监测终端:用于室内、大棚、机房等环境的温湿度、光照等数据采集,通过MQTT上报至云平台,实现远程监控;

  2. 智能传感节点:作为物联网边缘节点,采集各类传感器数据(如温湿度、气压、土壤湿度),通过ThingSpeak等平台实现数据可视化与分析;

  3. 远程控制终端:接收云平台MQTT命令,控制继电器、LED等外设,实现设备远程开关、参数调节;

  4. 工业数据采集:适配工业场景中的传感器,采集设备运行参数,通过MQTT协议上传至工业物联网平台,实现设备状态监测;

  5. 教学实验平台:适合嵌入式、物联网相关教学,快速实现网络连接、数据上报等功能,降低教学难度。

9. 系列预告与资源获取

9.1系列预告

下一篇教程:将讲解Modbus 工业协议通信,W55RP20 搭建 Modbus TCP Server等一系列的内容。

9.2 资源获取

本文完整代码:WIZnet 官方 Gitee 仓库

W55RP20 芯片手册:WIZnet 官方资料网址

如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我们持续更新的动力!如有任何问题,欢迎在评论区留言,我们会第一时间回复。

相关推荐
LCG元15 小时前
STM32实战:基于STM32F103的家用新风系统智能控制器(空气质量监测+PID调速)
stm32·单片机·嵌入式硬件
LCG元16 小时前
STM32实战:基于STM32F103的多通道工业数据采集与监控系统(Modbus RTU+上位机)
stm32·单片机·嵌入式硬件
资深流水灯工程师16 小时前
STM32 单片机 USB 通讯原理与 HAL 库实战详解
stm32·单片机·嵌入式硬件
资深流水灯工程师16 小时前
STM32 I2C 通讯原理与三种实现模式详解
stm32·单片机·嵌入式硬件
zlinear数据采集卡17 小时前
电源纹波杀手:LDO线性稳压电路的“降噪哲学”——基于ZLinear数据采集卡的深度解析
单片机·嵌入式硬件·fpga开发·硬件架构
资深流水灯工程师17 小时前
STM32 USART 通讯原理与三种模式详解
stm32·单片机·嵌入式硬件
资深流水灯工程师17 小时前
STM32 单片机 SPI 通讯原理详解
stm32·单片机·嵌入式硬件
EMTime17 小时前
玲珑GUI-工程设置
单片机·mcu·ui·用户界面
不做无法实现的梦~17 小时前
MAVLink 协议教程
linux·stm32·嵌入式硬件·算法