本文为 W55RP20-EVB-MKR 模块 MicroPython 教程专项篇,基于官方最新固件编写,代码均经过实际验证,可直接烧录运行。 版权声明:本文为 WIZnet 官方原创技术文章,转载请注明出处。
前言
上一篇教程中,我们已经完成了 MQTT 协议与阿里云平台对接 的开发,实现了设备上云、物模型数据定时上报与云端监控。而在国内物联网开发场景中,中移 OneNET 平台凭借接入简单、轻量易用、免费额度充足的优势,成为嵌入式设备快速上云的高性价比选择。
本文将带你快速上手 W55RP20-EVB-Pico 模块的 MicroPython 开发,重点实现 MQTT 协议与 OneNET 平台对接 功能,学完本文,你将掌握:
- OneNET 平台 MQTT 接入核心原理与物模型规范
- OneNET 产品、设备创建与权限配置全流程
- MQTT 连接参数(ProductID、DeviceName、Token)生成方法
- 设备上报 + 云端指令下发双向通信实现
- 标准 JSON 格式数据封装与应答机制
- MQTT 断线自动重连与心跳保活方案
- OneNET 平台联调测试与日志查看方法
- MQTT 连接异常与故障一站式排查
- WIZnet 硬件协议栈在轻量化上云场景的核心优势
系列教程学习路径
本专栏共 16 篇,循序渐进覆盖 W55RP20-EVB-MKR 模块 MicroPython 开发全流程:
- 第 1 篇:静态 IP 配置与网络基础
- 第 2 篇:DHCP 自动联网与网络诊断
- 第 3 篇:TCP Client 客户端通信
- 第 4 篇:TCP Server 服务端通信
- 第 5 篇:UDP 单播数据通信
- 第 6 篇:UDP 组播/广播数据通信
- 第 7 篇:DNS 域名解析
- 第 8 篇:NTP 从网络获取时间
- 第 9 篇:HTTP Client 客户端请求
- 第 10 篇:HTTP Server 服务端搭建
- 第 11 篇:HTTP 协议与 OneNET 平台数据上云
- 第 12 篇:MQTT 协议基础通信验证
- 第 13 篇:MQTT 协议与阿里云平台对接
- 第 14 篇:MQTT 协议与 OneNET 平台对接(本文)
- 第 15 篇:MQTT 协议与 ThingSpeak 平台对接
- 第 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. MQTT + OneNET 核心原理](#4. MQTT + OneNET 核心原理)
[4.1 MQTT 协议简介](#4.1 MQTT 协议简介)
[4.2 OneNET 平台接入流程](#4.2 OneNET 平台接入流程)
[4.3 核心优势](#4.3 核心优势)
[5. OneNET 平台配置流程](#5. OneNET 平台配置流程)
[6. 核心代码解析](#6. 核心代码解析)
[6.1 完整可运行代码](#6.1 完整可运行代码)
[6.2 代码关键步骤说明](#6.2 代码关键步骤说明)
[7. 运行结果与测试验证](#7. 运行结果与测试验证)
[7.1 串口输出结果](#7.1 串口输出结果)
[7.2 OneNET 平台验证](#7.2 OneNET 平台验证)
[8. 常见问题一站式排查指南](#8. 常见问题一站式排查指南)
[8.1 网络连接问题排查](#8.1 网络连接问题排查)
[8.2 MQTT 连接失败排查](#8.2 MQTT 连接失败排查)
[8.3 数据上报 / 平台指令无反应排查](#8.3 数据上报 / 平台指令无反应排查)
[9. W55RP20核心优势](#9. W55RP20核心优势)
[10. 典型应用场景](#10. 典型应用场景)
[11. 系列预告与资源获取](#11. 系列预告与资源获取)
[11.1 系列预告](#11.1 系列预告)
[11.2 资源获取](#11.2 资源获取)
1. 准备工作
1.1 软件准备
所需软件均为免费版本,按要求下载安装即可,无需额外付费。
表格
| 软件名称 | 版本要求 | 下载地址 | 说明 |
|---|---|---|---|
| Thonny | 4.0 及以上 | Thonny 官方下载 | 轻量级 MicroPython IDE,支持代码编辑、烧录与串口调试,新手友好 |
| W55RP20-EVB-MKR 模块 MicroPython驱动库 | 最新稳定版 | WIZnet 官方固件/驱动库下载 | 专为 W55RP20-EVB-MKR 模块编写,已集成 WIZnet 硬件驱动、TCP/IP协议栈及MQTT客户端库 |
| 串口调试助手(如SecureCRT) | 任意版本 | 官方下载或第三方工具 | 用于查看串口输出的运行日志、调试信息,定位连接和数据上传问题 |
| OneNET物联网平台 | 在线版 | OneNET物联网平台官网 | 创建产品、注册设备,获取MQTT连接参数,查看上传的温湿度数据 |
1.2 硬件准备

- W55RP20-EVB-MKR × 1
- Micro USB 数据线(必须支持数据传输,不能使用纯充电线)× 1
- 标准网线 × 1
- 开启 DHCP 功能的路由器 / 交换机 × 1(用于获取网络参数,实现外网访问)
- OneNET 平台账号(免费注册开通)
2. 烧录 W55RP20-EVB-MKR 模块专属 MicroPython 固件
W55RP20-EVB-Pico 模块 完全兼容树莓派 Pico 的 UF2 固件烧录方式,操作简单无需额外烧录器,新手可快速上手:
- 按住 RP2040 开发板上的 BOOTSEL 按键不放;
- 使用 Micro USB 数据线连接开发板与电脑;
- 待电脑识别出名为 RPI-RP2 的 U 盘后,松开 BOOTSEL 按键;
- 将下载好的 W5500_RP2040_firmware.uf2 固件文件拖拽到 U 盘中;
- 开发板会自动重启,固件烧录完成。
3. 硬件连接与开发环境配置
3.1 硬件连接
W55RP20-EVB-Pico 模块连接分为两步,分别实现供电 / 调试和以太网连接,操作简单,无需复杂接线:
3.1.1 基础连接(供电 + 调试)
使用 Micro USB 数据线连接 RP2040 开发板与电脑,用于开发板电、代码烧录和串口调试。
3.1.2 以太网连接
使用网线连接 W55RP20-EVB-MKR 模块的以太网接口与路由器的 LAN 口,确保设备可正常访问外网。
3.1.3 模块与开发板接线
本开发板为一体化集成设计,无需额外 SPI、外设接线,上电联网即可直接使用。

3.2 Thonny 开发环境配置
打开 Thonny 软件,按以下步骤配置开发环境,确保代码能正常烧录和运行:
- 点击顶部菜单栏「运行」→「配置解释器」;
- 切换到「解释器」选项卡;
- 在「解释器」下拉列表中选择 MicroPython (通用);
- 在「端口」下拉列表中选择开发板对应的串口(通常显示为 Board CDC @ COMx);
- 勾选「运行代码前先重启解释器」和「同步设备的实时时钟」;
- 点击「确定」完成配置。
4. MQTT + OneNET 核心原理
4.1 MQTT 协议简介
MQTT(Message Queuing Telemetry Transport)是基于发布 / 订阅模式的轻量级物联网通信协议,基于 TCP 协议传输,具备报文短小、带宽占用低、功耗小、稳定性强的特点,是全球主流的物联网设备上云标准协议。
4.2 OneNET 平台接入流程
- OneNET 平台创建产品 → 定义物模型(温度 temp、湿度 humi);

- 创建设备 → 获取 PRODUCT_ID、DEVICE_NAME、TOKEN

- 配置 MQTT 接入地址、端口与订阅主题;

需要将主题的{device-name}替换为设备名(或
设备ID)
密码生成工具:https://open.iot.10086.cn/doc/iot_platform/images/tools/token.exe
| 参数名 | 参数值 |
|---|---|
| mqttHostUrl | mqtts.heclouds.com(固定不变) |
| port | 1883(固定不变) |
| clientId | W5100S_W5500(设备 ID) |
| username | 75w4NMRceb(产品 ID) |
| passwd | version=2018-10-31&res=products%2F75w4NMRceb%2Fdevices%2FW5100S_W5500&et=1791400694&method=md5&sign=FTnZrF14Pqy%2F3CXggctheg%3D%3D(工具计算) |
| 上报温湿度主题 | $sys/75w4NMRceb/W5100S_W5500/thing/property/post(发布权限) |
| 上报回复主题 | $sys/75w4NMRceb/W5100S_W5500/thing/property/post/reply(订阅权限) |
| 设置 LED 状态 | $sys/75w4NMRceb/W5100S_W5500/thing/property/set(订阅权限) |
| 设置状态回复 | $sys/75w4NMRceb/W5100S_W5500/thing/property/set_reply(发布权限) |
4.设备通过 W55RP20 联网 → 建立 MQTT 连接;
python
MPY: soft reboot
==================================================
W55RP20 OneNET 温湿度上报
==================================================
MAC Address: 02:90:86:88:4d:56
IP Address: ('192.168.1.123', '255.255.255.0', '192.168.1.1', '202.96.134.33')
IP: ('192.168.1.123', '255.255.255.0', '192.168.1.1', '202.96.134.33')
正在连接 OneNET...
✅ 连接成功
- 设备定时上报属性数据 → 平台实时展示

4.3 核心优势
- 接入流程极简,无需复杂签名加密;
- 支持上报 + 指令下发双向通信;
- 免费额度充足,适合学习与小型项目;
- 硬件协议栈独立处理网络,MCU 零占用。
5. OneNET 平台配置流程
- 登录中移物联网 OneNET 平台,进入物联网核心套件;
- 创建产品,选择 MQTT 协议接入;
- 定义物模型:添加
temp(温度)、humi(湿度) 属性; - 创建设备,复制 PRODUCT_ID、DEVICE_NAME、TOKEN;
- 记录系统主题:属性上报、指令下发、指令应答;
- 开启设备权限,完成平台配置。
6. 核心代码解析
W55RP20-EVB-MKR 模块的 MicroPython 库已完整封装底层网络与 MQTT 功能,对接 OneNET 仅需简单配置即可运行。
6.1 完整可运行代码
python
from wiznet_init import wiznet
from umqttsimple import MQTTClient
from machine import Pin
import time
import json
import random
# ================= 配置区域 =================
BOARD = "W55RP20-EVB-Pico"
# OneNET MQTT 配置
ONENET_HOST = "mqtts.heclouds.com"
ONENET_PORT = 1883
PRODUCT_ID = "ZGB6zBD0nS"
DEVICE_NAME = "led"
TOKEN = "version=2018-10-31&res=products%2FZGB6zBD0nS%2Fdevices%2Fled&et=1780105717&method=md5&sign=wj69s4%2FXpLuMus4JVb65ww%3D%3D"
# 系统主题
TOPIC_PROP_POST = f"$sys/{PRODUCT_ID}/{DEVICE_NAME}/thing/property/post"
TOPIC_PROP_SET = f"$sys/{PRODUCT_ID}/{DEVICE_NAME}/thing/property/set"
TOPIC_PROP_SET_REPLY = f"$sys/{PRODUCT_ID}/{DEVICE_NAME}/thing/property/set_reply"
# 上报间隔(秒)
REPORT_INTERVAL = 5
# 模拟温湿度数据
TEMP_VALUE = 25.5
HUMI_VALUE = 48.8
client = None
# ================= 数据上报 =================
def report_data():
message_id = str(random.randint(100000, 999999))
# OneNET 标准物模型格式
payload = {
"id": message_id,
"version": "1.0",
"params": {
"temp": {
"value": TEMP_VALUE
},
"humi": {
"value": HUMI_VALUE
}
}
}
message = json.dumps(payload)
print(f"\n📤 上报数据:")
print(f" {message}")
try:
result = client.publish(TOPIC_PROP_POST, message, qos=1)
print(f" ✅ 已发送")
return True
except Exception as e:
print(f" ❌ 失败: {e}")
return False
# ================= MQTT 消息回调 =================
def sub_cb(topic, message):
try:
topic_str = topic.decode('utf-8')
msg_str = message.decode('utf-8')
print(f"\n📥 收到指令:")
print(f" 主题: {topic_str}")
print(f" 内容: {msg_str}")
# 解析JSON并自动应答
data = json.loads(msg_str)
if 'params' in data:
msg_id = data.get('id', '')
reply = {
"id": msg_id,
"code": 200,
"msg": "success"
}
client.publish(TOPIC_PROP_SET_REPLY, json.dumps(reply), qos=1)
print(" ✅ 已应答平台")
except Exception as e:
print(f" ❌ 处理错误: {e}")
# ================= MQTT 连接 =================
def mqtt_connect():
global client
client = MQTTClient(
client_id=DEVICE_NAME,
server=ONENET_HOST,
port=ONENET_PORT,
user=PRODUCT_ID,
password=TOKEN,
keepalive=30,
ssl=False
)
client.set_callback(sub_cb)
print("正在连接 OneNET...")
client.connect()
print("✅ 连接成功")
# 订阅指令主题
client.subscribe(TOPIC_PROP_SET)
print(f"已订阅: {TOPIC_PROP_SET}")
# 自动重连
def reconnect():
while True:
try:
mqtt_connect()
break
except Exception as e:
print(f"重连中... {e}")
time.sleep(2)
# ================= 主程序 =================
def main():
print("=" * 50)
print("W55RP20 OneNET 温湿度上报 + 指令控制")
print("=" * 50)
# 初始化网络
nic = wiznet(BOARD, dhcp=True)
print(f"IP: {nic.ifconfig()}")
# 连接MQTT
mqtt_connect()
last_report = time.time()
count = 0
while True:
try:
# 处理消息
client.check_msg()
# 定时上报
if time.time() - last_report > REPORT_INTERVAL:
count += 1
print(f"\n{'='*20} 第{count}次上报 {'='*20}")
report_data()
last_report = time.time()
time.sleep(0.5)
except Exception as e:
print(f"断开连接: {e}")
reconnect()
last_report = time.time()
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
6.2 代码关键步骤说明
- 网络初始化:基于 wiznet_init 快速联网,DHCP 自动获取 IP;
- MQTT 连接:使用 OneNET 标准参数,简单直接无需加密计算;
- 定时上报:5 秒自动上传温湿度,平台可直接解析;
- 指令回调:订阅平台下发主题,收到指令自动应答;
- 异常重连:网络断开后自动循环重连,保证长期稳定运行;
- QoS=1:确保消息可靠传输,不丢包。
- 下面这个代码原版红箭头位置注释掉了,要取消注释,上面代码已经注释掉了,git可能没注释记得注释掉不然有问题

7. 运行结果与测试验证
7.1 串口输出结果
==================================================
W55RP20 OneNET 温湿度上报 + 指令控制
==================================================
IP: ('192.168.1.119', '255.255.255.0', '192.168.1.1', '114.114.114.114')
正在连接 OneNET...
✅ 连接成功
已订阅: $sys/ZGB6zBD0nS/led/thing/property/set
==================== 第1次上报 ====================
📤 上报数据:
{"id":"123456","version":"1.0","params":{"temp":{"value":25.5},"humi":{"value":48.8}}}
✅ 已发送
📥 收到指令:
主题: $sys/ZGB6zBD0nS/led/thing/property/set
内容: {"id":"666666","params":{"led":1}}
✅ 已应答平台

上面这二张图是我自己配置时候的具体参数,用户可以自己设置,关键注意生成产品后一定要发布才能使用。
7.2 OneNET 平台验证
- 进入 OneNET 控制台 → 设备 → 物模型数据,可看到温湿度实时刷新

- 平台下发指令,设备串口可实时接收并自动应答;
- 日志服务可查看完整上报、下发报文,验证通信正常。

8. 常见问题一站式排查指南
8.1 网络连接问题排查
| 故障现象 | 排查步骤 | 解决要点 |
|---|---|---|
| 无法联网 | 1. 检查物理网线连接2. 核查路由器上网状态3. 检查路由器 DHCP 功能 | 1. 网线两端插紧,观察网口指示灯正常常亮 / 规律闪烁2. 路由器正常供电,手机连同一 WiFi 可正常上网3. 路由器后台开启 DHCP,异常时重启路由和设备 |
| 获取不到 IP | 1. 检查 DHCP 地址池容量2. 切换静态 IP 方式测试 | 1. 地址池已满则释放闲置 IP 或扩大地址池范围2. 静态 IP 与路由器同网段,掩码255.255.255.0,网关和路由网关一致 |
8.2 MQTT 连接失败排查
| 故障原因 | 问题说明 | 处理方案 |
|---|---|---|
| 接入地址错误 | 地址拼写错误、填写非官方地址,导致连接超时 / 被拒绝 | 固定使用官方地址:mqtts.heclouds.com,无多余空格、无拼写错误 |
| 身份参数错误 | PRODUCT_ID、DEVICE_NAME、TOKEN 不匹配、大小写混淆 | 1. 三大参数与平台产品设备完全一致、区分大小写2. TOKEN 通过平台工具正规生成,校验 res、et、method 等参数 |
| 端口配置错误 | 误用加密端口 8883 或其他端口,路由封禁端口流量 | 1. 固定使用非加密端口 18832. 关闭路由器防火墙测试,确保 1883 端口通行 |
8.3 数据上报 / 平台指令无反应排查
| 故障类型 | 故障描述 | 规范要求与解决方法 |
|---|---|---|
| 主题错误 | 自定义主题、字段缺失、前缀错误、权限不足 | 1. 严格遵循格式:$sys/[PRODUCT_ID]/[DEVICE_NAME]/thing/property/xxx2. 不可省略$sys前缀,产品 / 设备 ID 填写无误3. 上报主题开发布权限、指令主题开订阅权限 |
| JSON 格式错误 | 层级缺失、标点语法错误、不符合物模型结构 | 1. 标准结构:{"params":{"属性名":{"value":属性值}}}2. 杜绝缺逗号、引号不匹配、括号不闭合3. 串口打印 JSON 原文,逐行校验格式 |
| 物模型未匹配 | 未创建物模型、属性标识符大小写 / 名称不一致 | 1. 平台提前创建对应温度、湿度等物模型属性2. 平台属性标识符与代码上报名称完全一致(区分大小写)3. 数据类型与代码定义保持统一 |
9. 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库,适配难度高) | 弱(单路透传,无法同时处理多连接) |
10. 典型应用场景
W55RP20开发板,结合MicroPython的便捷开发和MQTT的轻量化通信,适合多种物联网场景,尤其适合快速原型开发和小型物联网项目:
-
环境监测终端:用于室内、大棚、机房等环境的温湿度、光照等数据采集,通过MQTT上报至云平台,实现远程监控;
-
智能传感节点:作为物联网边缘节点,采集各类传感器数据(如温湿度、气压、土壤湿度),通过ThingSpeak等平台实现数据可视化与分析;
-
远程控制终端:接收云平台MQTT命令,控制继电器、LED等外设,实现设备远程开关、参数调节;
-
工业数据采集:适配工业场景中的传感器,采集设备运行参数,通过MQTT协议上传至工业物联网平台,实现设备状态监测;
-
教学实验平台:适合嵌入式、物联网相关教学,快速实现网络连接、数据上报等功能,降低教学难度。
11. 系列预告与资源获取
11.1 系列预告
下一篇教程:MQTT 协议与 ThingSpeak 平台对接将讲解 ThingSpeak 开源云平台接入、MQTT 数据上报与可视化图表展示,实现物联网数据云端存储与实时监控。
11.2 资源获取
-
本文完整代码:WIZnet Pico MicroPython 示例工程
-
W55RP20 芯片手册:WIZnet 官方资料页面
下一篇讲解:W55RP20-EVB-MKR MicroPython 实战(15):MQTT协议与ThingSpeak平台对接
如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我们持续更新的动力!如有任何问题,欢迎在评论区留言,我们会第一时间回复。
