零知开源(零知IDE)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从“配置环境”转移到“创意实现”,极大降低了技术门槛。零知IDE编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知开源(零知IDE)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com
项目概述
本项目基于零知增强板(主控STM32F407VET6)结合W5500以太网模块,实现了一套完整的UDP通信温湿度监控系统。系统通过DHT11传感器实时采集环境温湿度数据,通过W5500以太网模块建立UDP通信链路,将数据发送至PC上位机。同时,上位机可通过UDP协议发送控制指令,远程控制开发板上的LED灯开关状态
项目难点及解决方案
问题描述:多网卡路由冲突导致路由表混乱,UDP包丢失
解决方案:网段隔离与静态配置,将网络拓扑从混合网段改为独立网段;代码中禁用DHCP功能,网关和DNS强制指向PC的以太网IP;同时网段检测逻辑
一、系统硬件部分
1.1 元件清单
| 硬件名称 | 数量 | 备注 |
|---|---|---|
| 零知增强板(STM32F407VET6) | 1 | 主控核心板 |
| W5500 以太网模块 | 1 | 带 SPI 接口的以太网模块 |
| DHT11 温湿度传感器 | 1 | 数字型温湿度传感器 |
| LED 发光二极管 | 1 | 用于远程控制演示 |
| 10K 上拉电阻 | 1 | DHT11 数据脚需接 |
| 杜邦线 | 若干 | 连接各模块 |
| 网线 | 1 | PC 与 W5500 直连 |
| PC(带以太网口) | 1 | 运行 Python 上位机 |
1.2 接线方案表
请务必严格按照代码中的定义进行连接,否则会导致初始化失败。
| 零知增强板引脚 | 外接设备 | 设备引脚 | 功能说明 |
|---|---|---|---|
| 7 | DHT11 | DATA | 温湿度数据传输 |
| 8 | LED | 正极 | LED 控制引脚(低电平熄灭) |
| GND | DHT11/LED/W5500 | GND | 公共接地 |
| 5V | W5500/DHT11 | 5V / + | 供电(W5500 需 5V) |
| 3.3V | 可选 | DHT11 (+) | DHT11 也可接 3.3V |
| A5 (SCLK) | W5500 | SCLK | SPI 时钟线 |
| A6 (MISO) | W5500 | MISO | SPI 主机输入 / 从机输出 |
| A7 (MOSI) | W5500 | MOSI | SPI 主机输出 / 从机输入 |
| A4 (SCS) | W5500 | CS | W5500 片选引脚 |
1.3 接线示意图

W5500 的 SPI 接线必须严格对应零知增强板 的 SPI 引脚(SCK/MISO/MOSI/CS)
1.4 实物连接图
二、安装与使用部分
2.1 开源平台-输入"W5500的UDP通信"并搜索-代码下载自动打开

2.2 连接-验证-上传
2.3 调试-串口监视器

三、核心代码讲解
本项目的代码设计体现了模块化和健壮性的特点,以下将对核心的四个部分进行详细剖析
3.1网络初始化与配置
网络初始化是本项目的核心,采用PC直连静态IP模式,确保通信稳定
// ==================== 网络配置 - PC直连模式 ====================
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// 静态IP配置
IPAddress staticIP(192, 168, 10, 22); // W5500的IP
IPAddress gateway(192, 168, 10, 1); // 设为PC的以太网卡IP
IPAddress subnet(255, 255, 255, 0);
IPAddress dnsip(192, 168, 10, 1); // DNS指向PC
// PC的以太网卡IP
IPAddress pcIP(192, 168, 10, 17);
// 网络初始化函数
void initNetwork() {
// 静态IP配置
Ethernet.begin(mac, staticIP, dnsip, gateway, subnet);
// 验证网络配置
IPAddress ip = Ethernet.localIP();
if (ip == IPAddress(0, 0, 0, 0)) {
Serial.println(" 错误: 以太网初始化失败! ");
// 错误处理...
}
// 启动UDP服务
Udp.begin(localPort);
}
PC以太网卡不提供DHCP服务,必须使用静态IP;网关设置指向PC,点对点直连网络
3.2DHT11数据采集与处理
DHT11传感器数据采集需要精确的时序控制,并处理可能的读取失败情况
// DHT11初始化
DHT dht(DHTPIN, DHTTYPE);
// 读取DHT数据函数
void readDHTData() {
unsigned long currentTime = millis();
// 每10秒读取一次
if (currentTime - lastDHTReadTime >= 10000) {
lastDHTReadTime = currentTime;
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println(" DHT11读取失败!");
dhtValid = false;
} else {
humidity = h;
temperature = t;
dhtValid = true;
// 发送数据
sendDHTData();
}
}
}
// 发送DHT数据函数
void sendDHTData() {
if (dhtValid) {
messageCount++;
char tempStr[10];
char humiStr[10];
floatToString(tempStr, temperature, 2); // 2位小数精度
floatToString(humiStr, humidity, 2);
// 构建JSON格式数据
snprintf(sendBuffer, sizeof(sendBuffer),
"{"type":"dht","count":%lu,"temp":%s,"humi":%s,"time":%lu}",
messageCount, tempStr, humiStr, millis() / 1000);
sendUDP(sendBuffer);
}
}
DHT11设置为10秒的读取间隔,使用自定义floatToString()函数处理浮点数
3.3 UDP通信协议解析
实现简单的命令解析机制,支持多种控制指令
// 协议解析函数
void parseCommand(const char* cmd, IPAddress remoteIP, int remotePort) {
// LED_ON命令
if (strcmp(cmd, "LED_ON") == 0) {
digitalWrite(LED_CONTROL_PIN, HIGH);
ledControlState = true;
snprintf(sendBuffer, sizeof(sendBuffer),
"{"type":"response","cmd":"LED_ON","status":"success","led_state":true}");
Udp.beginPacket(remoteIP, remotePort);
Udp.write((uint8_t*)sendBuffer, strlen(sendBuffer));
Udp.endPacket();
}
// GET_DHT命令
else if (strcmp(cmd, "GET_DHT") == 0) {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
snprintf(sendBuffer, sizeof(sendBuffer),
"{"type":"response","cmd":"GET_DHT","status":"error","error":"read_failed"}");
} else {
char tempStr[10];
char humiStr[10];
floatToString(tempStr, t, 2);
floatToString(humiStr, h, 2);
snprintf(sendBuffer, sizeof(sendBuffer),
"{"type":"response","cmd":"GET_DHT","status":"success","temp":%s,"humi":%s}",
tempStr, humiStr);
}
Udp.beginPacket(remoteIP, remotePort);
Udp.write((uint8_t*)sendBuffer, strlen(sendBuffer));
Udp.endPacket();
}
// 其他命令处理...
}
支持的命令列表
| 指令 | 功能说明 | 返回信息 |
|---|---|---|
| LED_ON | 点亮LED | 返回成功状态和LED状态 |
| LED_OFF | 熄灭LED | 返回成功状态和LED状态 |
| GET_DHT | 读取实时温湿度 | 返回数据或错误信息 |
| STATUS | 获取设备完整状态信息 | 返回设备状态 |
每个命令都有明确的成功/失败状态返回,接收到命令后立即处理并返回结果
3.4系统状态维护与心跳机制
系统需要维护多个状态变量,并实现心跳机制确保连接正常
// 全局状态变量
unsigned long lastDHTReadTime = 0; // 上次DHT读取时间
unsigned long lastSendTime = 0; // 上次发送时间
unsigned long messageCount = 0; // 消息计数器
unsigned long lastHeartbeat = 0; // 上次心跳时间
bool dhtValid = false; // DHT数据有效性
bool ledControlState = false; // LED控制状态
bool networkInitialized = false; // 网络初始化状态
unsigned long packetsSent = 0; // 发送数据包计数
unsigned long packetsReceived = 0; // 接收数据包计数
// 心跳包发送函数
void sendHeartbeat() {
unsigned long currentTime = millis();
if (currentTime - lastHeartbeat >= 30000) { // 每30秒
lastHeartbeat = currentTime;
snprintf(sendBuffer, sizeof(sendBuffer),
"{"type":"heartbeat","uptime":%lu,"packets_sent":%lu,"packets_received":%lu}",
millis() / 1000, packetsSent, packetsReceived);
sendUDP(sendBuffer);
}
}
// 状态LED指示函数
void updateStatusLED() {
unsigned long currentTime = millis();
if (currentTime - lastBlinkTime >= 500) { // 每500ms闪烁一次
lastBlinkTime = currentTime;
ledBlinkState = !ledBlinkState;
digitalWrite(LED_BUILTIN, ledBlinkState ? LOW : HIGH);
}
}
统计发送和接收的数据包数量,用于监控通信质量;定期发送心跳包,让上位机知道设备在线状态
3.5 系统完整代码
/**************************************************************************************
* 文件: W5500_UDP_DHT11_Control.ino
* 作者:零知实验室(深圳市在芯间科技有限公司)
* -^^- 零知实验室,让电子制作变得更简单! -^^-
* 时间: 2026-02-09
* 网络拓扑:
* 路由器(192.168.3.1) ←WiFi→ PC(WiFi: 192.168.3.17, 以太网: 192.168.10.1)
* ↓ 直连网线
* W5500(192.168.10.22)
*
* 功能说明:
* W5500以太网模块UDP通信、DHT11温湿度传感器数据采集和上报、远程LED控制功能、简单协议解析和响应、修复JSON浮点数格式化问题(snprintf不支持%f)
************************************************************************************/
#include < SPI.h >
#include < Ethernet_STM.h >
#include < EthernetUdp.h >
#include "DHT.h"
// ==================== 硬件配置 ====================
#define DHTPIN 7
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
#define LED_CONTROL_PIN 8
// ==================== 网络配置 - PC直连模式 ====================
#if defined(WIZ550io_WITH_MACADDRESS)
// WIZ550io有内置MAC地址
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
#endif
// PC直连模式 - 必须使用静态IP!
// PC的以太网卡不提供DHCP服务,所以DHCP无法工作
#define USE_DHCP false
// ==================== 重要: 网段配置说明 ====================
// 使用 192.168.10.x 网段,与PC的WiFi网段(192.168.3.x)分开
// 避免IP冲突和路由混乱
// 静态IP配置
IPAddress staticIP(192, 168, 10, 22); // W5500的IP
IPAddress gateway(192, 168, 10, 1); // 设为PC的以太网卡IP
IPAddress subnet(255, 255, 255, 0);
IPAddress dnsip(192, 168, 10, 1); // DNS指向PC
// PC的以太网卡IP (连接W5500的那个网卡)
// 不是WiFi的IP (192.168.3.17)!
IPAddress pcIP(192, 168, 10, 17);
// UDP端口
unsigned int localPort = 8888;
unsigned int pcPort = 9003;
// ==================== 如果您的PC以太网卡IP是其他值 ====================
// 请相应修改上面的配置,例如:
//
// 如果PC以太网卡是 192.168.137.1 (启用了ICS):
// IPAddress staticIP(192, 168, 137, 22);
// IPAddress gateway(192, 168, 137, 1);
// IPAddress dnsip(192, 168, 137, 1);
// IPAddress pcIP(192, 168, 137, 1);
//
// 如果PC以太网卡是 192.168.3.215 (不推荐,会与WiFi冲突):
// IPAddress staticIP(192, 168, 3, 22);
// IPAddress gateway(192, 168, 3, 215); // 指向PC,不是路由器!
// IPAddress dnsip(192, 168, 3, 215);
// IPAddress pcIP(192, 168, 3, 215);
// ==================== 全局变量 ====================
EthernetUDP Udp;
char receiveBuffer[256];
char sendBuffer[512];
// 定时器
unsigned long lastDHTReadTime = 0;
unsigned long lastSendTime = 0;
unsigned long messageCount = 0;
unsigned long lastHeartbeat = 0;
// DHT数据
float temperature = 0.0;
float humidity = 0.0;
bool dhtValid = false;
// LED状态
bool ledControlState = false;
unsigned long lastBlinkTime = 0;
bool ledBlinkState = false;
// 网络状态
bool networkInitialized = false;
unsigned long lastSuccessTime = 0;
unsigned long packetsSent = 0;
unsigned long packetsReceived = 0;
// ==================== 函数声明 ====================
void floatToString(char* buffer, float value, int decimalPlaces);
// ==================== 浮点数转字符串 ====================
void floatToString(char* buffer, float value, int decimalPlaces) {
int intPart = (int)value;
int decPart = (int)((value - intPart) * pow(10, decimalPlaces));
if (decPart < 0) decPart = -decPart;
if (decimalPlaces == 1) {
sprintf(buffer, "%d.%01d", intPart, decPart);
} else if (decimalPlaces == 2) {
sprintf(buffer, "%d.%02d", intPart, decPart);
}
}
// ==================== 初始化 ====================
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("nn");
Serial.println("========================================");
Serial.println(" W5500 UDP + DHT11温湿度监控系统");
Serial.println(" 零知实验室");
Serial.println(" 版本: v3.1 (PC直连专用版)");
Serial.println("========================================n");
Serial.println(" 网络模式: PC直连(静态IP)");
Serial.println(" 请确保PC的以太网卡已配置静态IP!n");
initHardware();
initNetwork();
initDHT();
Serial.println("n========================================");
Serial.println("系统启动完成!");
Serial.println("========================================n");
printSystemInfo();
sendStartupMessage();
Serial.println("n开始工作...n");
Serial.println("----------------------------------------n");
}
// ==================== 硬件初始化 ====================
void initHardware() {
Serial.println("[1/3] 初始化硬件...");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(LED_CONTROL_PIN, OUTPUT);
digitalWrite(LED_CONTROL_PIN, LOW);
ledControlState = false;
// 启动提示 - LED快闪3次
for(int i = 0; i < 3; i++) {
digitalWrite(LED_BUILTIN, LOW);
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
}
Serial.println(" 硬件初始化完成!");
}
// ==================== 网络初始化 ====================
void initNetwork() {
Serial.println("n[2/3] 初始化W5500以太网模块...");
Serial.println(" 模式: 静态IP (PC直连)");
delay(500);
// 静态IP配置
#if defined(WIZ550io_WITH_MACADDRESS)
Ethernet.begin(staticIP, dnsip, gateway, subnet);
#else
Ethernet.begin(mac, staticIP, dnsip, gateway, subnet);
#endif
delay(1000);
// 验证网络配置
IPAddress ip = Ethernet.localIP();
if (ip == IPAddress(0, 0, 0, 0)) {
Serial.println("n 错误: 以太网初始化失败! ");
Serial.println("n请检查:");
Serial.println(" 1. W5500模块SPI接线是否正确");
Serial.println(" 2. 网线是否连接到PC的以太网口");
Serial.println(" 3. PC的以太网卡是否已配置静态IP");
Serial.println("n设备将进入错误指示模式");
while(1) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(100);
}
}
Serial.println("n W5500初始化成功!");
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.print(" 本机IP: ");
Serial.println(ip);
Serial.print(" 子网掩码: ");
Serial.println(Ethernet.subnetMask());
Serial.print(" 网关: ");
Serial.println(Ethernet.gatewayIP());
Serial.print(" DNS: ");
Serial.println(Ethernet.dnsServerIP());
Serial.println(" 模式: 静态IP (PC直连)");
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
// 显示PC配置提示
Serial.println("n
审核编辑 黄宇