W6300发送邮件给他人
更新于 2026年10月9日
本篇总结
在 W6300 上实现 SMTP 协议,通过 126 邮箱服务器发送电子邮件到指定收件人。
硬件平台:本例程基于 W6300-EVB-M4 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板
本示例演示如何在 W6300 上实现 SMTP 协议,通过实战例程讲解如何在 W6300 上使用 SMTP 协议给他人发送电子邮件。该例程用到的其他网络协议(例如 DHCP、DNS),请参考相关章节。有关 W6300 的初始化过程,请参考 Network install 章节,这里不再赘述。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
SMTP 协议简介
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是一种用于电子邮件传输的通信协议。它是互联网标准协议之一,专门设计用于电子邮件的发送和路由。SMTP 定义了邮件如何从发件人发送到收件人的电子邮件服务器,并规范了服务器之间的邮件中继操作。
SMTP 协议特点:
- 面向文本:SMTP 使用纯文本命令和响应(如 HELO、MAIL FROM、RCPT TO 等)进行通信,邮件内容通常以 ASCII 码表示
- 请求-响应模式:基于请求-响应模型,客户端发送请求,服务器返回响应
- 基于 TCP:依赖 TCP 协议提供可靠数据传输,通过 TCP 建立连接并发送邮件
- 广泛兼容性:国际标准邮件传输协议,广泛应用于邮件系统,确保不同系统间高效互通
- 高效性与可靠性:协议简单易用,支持错误处理和重试机制,邮件无法发送时可暂存队列并重试
- 可扩展性:通过扩展命令和响应码,支持更多邮件传输特性和功能
- 安全性:本身不处理加密,但可结合 SSL/TLS(SMTPS)提供加密通道
- 异步传输:支持异步传输,邮件发送和接收可在不同时间进行,提升效率并支持批量处理
- 灵活性:可配置邮件路由、优先级、大小限制等,满足不同需求
SMTP 应用场景
① 物联网设备远程监控:收集传感器数据,发送定期报告或警报邮件;② 环境监测系统:将温湿度、CO2 浓度等监测数据发送到指定邮箱;③ 设备状态报告与日志记录:定时触发自动发送设备状态或运行日志;④ 远程故障报警与支持:结合传感器实现自动报警;⑤ 工业自动化和远程监控:发送设备状态、报警等信息到指定邮箱;⑥ 远程控制反馈:接收邮件解析命令并执行,通过 SMTP 反馈结果。
SMTP 发送邮件流程
- 使用 TCP 协议连接 SMTP 服务器
- 发送握手消息
- 发送用户认证消息
- 设置邮件发送地址
- 传输邮件内容
- 完成邮件发送
硬件连接与拓扑
请注意:
因为本示例需要访问互联网,请确保 W6300 的配置能够访问互联网。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口,路由器需连接互联网
- 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
│ W6300 │──网线──│ 路由器/网关 │──互联网──│ SMTP 服务器 │
│ (SMTP │ │ (DHCP/DNS) │ │ smtp.126.com │
│ Client) │ │ 192.168.1.1 │ │ TCP :25 │
└────┬──────┘ └─────────────────┘ └──────────────────┘
│ QSPI
┌────┴──────┐
│ MH2457 │──────────→ WIZ UartTool
│ MCU │
└───────────┘
工程结构
工程核心文件如下:
Project/
├── main.c ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
├── user_main.c ← 网络地址定义 + SMTP 服务器 + user_run() 主流程
├── do_smtp.c ← SMTP 协议实现(send_mail / do_smtp / base64encode / mailmessage)
├── do_dns.c ← DNS 域名解析
├── wiz_platform.c/h ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
└── wiz_interface.c/h ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器
步骤1:SMTP 发送内容初始化
文件:USER/do_smtp.c — 定义 SMTP 通信过程中用到的命令字符串、认证信息、收件人/发件人地址等。
char hello[50] = "HELO localhost"; // Identity command
char hello_reply[] = "250 OK"; // Id successfully responded
char AUTH[50] = "AUTH LOGIN"; // Authentication request
char AUTH_reply[] = "334 dXNlcm5hbWU6"; // The authentication request was successfully sent
char name_126[100] = "[email protected]"; // 126 Login email address
char base64name_126[200]; // 126 base64 encoding of the login mailbox name
char name_reply[] = "334 UGFzc3dvcmQ6"; // The login name was sent successfully
char password_126[50] = "ZPURADLGRUPQLVBK"; // 126 Email login password
char base64password_126[100]; // 126 base64 Password
char password_reply[] = "235 Authentication successful"; // Login successful response
char from[] = "[email protected]"; // Sender email
char from_reply[] = "250 Mail OK";
char to[] = "[email protected]"; // Recipient email address
char to_reply[] = "250 Mail OK";
char data_init[10] = "data"; // Request data transfer
char data_reply[] = "354"; // The request was successfully responded to HEAD
char Cc[] = ""; // Cc to email
char subject[] = "Hello!WIZnet!"; // subject
char content[] = "Hello!WIZnet!"; // text part
char mime_reply[] = "250 Mail OK queued as"; // The email was sent successfully
char mailfrom[50] = "MAIL FROM:<>";
char rcptto[50] = "rcpt to:<>";
char mime[200] = "From:\r\n";
char mime1[50] = "To:\r\n";
char mime2[50] = "Cc:\r\n";
char mime3[50] = "Subject:\r\n";
char mime4[50] = "MIME-Version:1.0\r\nContent-Type:text/plain\r\n\r\n";
char mime5[50] = "\r\n.\r\n";需要修改的关键信息:发送方邮箱(name_126 / from)、发送方授权码(password_126,注意 126 邮箱使用的是授权码而非登录密码)、收件人邮箱(to)、邮件主题与内容(subject / content)。
使用前的三点注意事项
- 修改邮箱账号及收发件人信息
- 邮箱必须开启 SMTP 功能(126 邮箱需在设置中开启 SMTP 服务并获取授权码)
- 确认邮箱所用的 SMTP 服务器地址和端口号(126 邮箱为 smtp.126.com:25)
步骤2:发送邮件内容初始化
文件:USER/do_smtp.c | 函数:mailmessage() — 对发送内容进行拼接处理,包括:Hello / AUTH 命令加 \r\n、用户名和密码 Base64 编码、插入发件人/收件人/抄送/主题到对应命令字符串、拼接 MIME 报文头。
void mailmessage(void)
{
strcat(hello, "\r\n");
strcat(AUTH, "\r\n");
base64encode(name_126, base64name_126);
base64encode(password_126, base64password_126);
strcat(base64name_126, "\r\n");
strcat(base64password_126, "\r\n");
str_insert(mailfrom, from, 11);
strcat(mailfrom, "\r\n");
str_insert(rcptto, to, 9);
strcat(rcptto, "\r\n");
strcat(data_init, "\r\n");
str_insert(mime, from, 5);
str_insert(mime1, to, 3);
str_insert(mime2, Cc, 3);
str_insert(mime3, subject, 8);
str_insert(mime5, content, 0);
strcat(mime, mime1);
strcat(mime, mime2);
strcat(mime, mime3);
strcat(mime, mime4);
strcat(mime, mime5);
}
Base64 编码:SMTP 认证过程中,用户名和密码需要进行 Base64 编码传输。base64encode() 函数实现了标准的 Base64 编码算法。
字符串插入:str_insert(s1, s2, pos) 把字符串 s2 插入到 s1 的 pos 位置处,例如把邮箱地址插入到 "MAIL FROM:<>" 的尖括号中间。
步骤3:DNS 解析 SMTP 服务器地址
文件:USER/user_main.c — 使用 do_dns() 将 SMTP 服务器域名 smtp.126.com 解析为 IP 地址。
uint8_t smtp_server_name[] = "smtp.126.com";
uint8_t smtp_server_ip[4] = {0};
mailmessage(); // Mail command information processing
if (do_dns(ethernet_buf, smtp_server_name, smtp_server_ip))
{
printf("DNS request failed.\r\n");
while (1)
{
}
}
注意:mailmessage() 必须在 DNS 解析之前调用,因为它只做字符串拼接,不依赖网络。do_dns() 成功后 smtp_server_ip 中即为 SMTP 服务器 IP。W6300 平台的 do_dns() 内部通过软件定时器链表注册 DNS_time_handler() 提供时基。
步骤4:SMTP 发送邮件操作
文件:USER/do_smtp.c | 函数:do_smtp() — 一个 TCP Client 状态机:SOCK_CLOSED 时打开 Socket,SOCK_INIT 时 connect() 连接 SMTP 服务器 25 端口,SOCK_ESTABLISHED 时接收服务器响应并调用 send_mail() 处理状态机。
void do_smtp(uint8_t sn, uint8_t *buf, uint8_t *smtp_server_ip)
{
int8_t socket_ret;
int32_t io_ret;
uint32_t len = 0U;
uint16_t anyport = 5000U;
uint16_t smtp_port = 25U;
memset(buf, 0, ETHERNET_MAX_BUF_SIZE);
switch (getSn_SR(sn))
{
case SOCK_INIT:
io_ret = connect(sn, smtp_server_ip, smtp_port, 4U);
(void)io_ret;
break;
case SOCK_ESTABLISHED:
if ((getSn_IR(sn) & Sn_IR_CON) != 0U)
{
setSn_IR(sn, Sn_IR_CON);
}
while (Mail_Send_OK == 0U)
{
len = getSn_RX_RSR(sn);
if (len > 0U)
{
if (len >= ETHERNET_MAX_BUF_SIZE)
{
len = ETHERNET_MAX_BUF_SIZE - 1U;
}
memset(buf, 0, ETHERNET_MAX_BUF_SIZE);
io_ret = recv(sn, buf, (uint16_t)len);
if (io_ret > 0)
{
buf[io_ret] = 0U;
send_mail(sn, buf, smtp_server_ip);
}
}
}
(void)disconnect(sn);
break;
case SOCK_CLOSE_WAIT:
len = getSn_RX_RSR(sn);
if (len > 0U)
{
while (Mail_Send_OK == 0U)
{
if (len >= ETHERNET_MAX_BUF_SIZE)
{
len = ETHERNET_MAX_BUF_SIZE - 1U;
}
io_ret = recv(sn, buf, (uint16_t)len);
if (io_ret > 0)
{
buf[io_ret] = 0U;
send_mail(sn, buf, smtp_server_ip);
}
len = getSn_RX_RSR(sn);
if ((len == 0U) && (Mail_Send_OK == 0U))
{
break;
}
}
}
(void)disconnect(sn);
break;
case SOCK_CLOSED:
socket_ret = socket(sn, Sn_MR_TCP4, anyport++, 0x00U);
(void)socket_ret;
break;
default:
break;
}
if (Mail_Send_OK != 0U)
{
while (1)
{
}
}
}
send_mail() 函数实现了完整的 SMTP 状态机,通过 SMTP_STATE 变量在 8 个状态间切换:
static void send_mail(uint8_t sn, uint8_t *buf, uint8_t *smtp_server_ip)
{
int32_t ret;
(void)smtp_server_ip;
switch (SMTP_STATE)
{
case waitfor220:
if (strstr((const char *)buf, "220") != NULL)
{
ret = send(sn, (uint8_t *)hello, (uint16_t)strlen(hello));
SMTP_STATE = waitforHELO250;
}
else
{
printf("Connected failed!\r\n");
}
break;
case waitforHELO250:
if ((strstr((const char *)buf, hello_reply) != NULL) &&
(strstr((const char *)buf, "Mail") == NULL))
{
ret = send(sn, (uint8_t *)AUTH, (uint16_t)strlen(AUTH));
SMTP_STATE = waitforAUTH334;
}
else
{
printf("smtp handshake failed!\r\n");
}
break;
case waitforAUTH334:
if (strstr((const char *)buf, AUTH_reply) != NULL)
{
ret = send(sn, (uint8_t *)base64name_126, (uint16_t)strlen(base64name_126));
SMTP_STATE = waitforuser334;
}
else
{
printf("AUTH authentication request failed!\r\n");
}
break;
case waitforuser334:
if (strstr((const char *)buf, name_reply) != NULL)
{
ret = send(sn, (uint8_t *)base64password_126, (uint16_t)strlen(base64password_126));
SMTP_STATE = waitforpassword235;
}
else
{
printf("username send failed!\r\n");
}
break;
case waitforpassword235:
if (strstr((const char *)buf, password_reply) != NULL)
{
ret = send(sn, (uint8_t *)mailfrom, (uint16_t)strlen(mailfrom));
SMTP_STATE = waitforsend250;
}
else
{
printf("password error!\r\n");
}
break;
case waitforsend250:
if ((strstr((const char *)buf, from_reply) != NULL) &&
(strstr((const char *)buf, "queued as") == NULL))
{
ret = send(sn, (uint8_t *)rcptto, (uint16_t)strlen(rcptto));
SMTP_STATE = waitforrcpt250;
}
else
{
printf("Send email failed to set up!\r\n");
}
break;
case waitforrcpt250:
if (strstr((const char *)buf, to_reply) != NULL)
{
ret = send(sn, (uint8_t *)data_init, (uint16_t)strlen(data_init));
SMTP_STATE = waitfordate354;
}
else
{
printf("Failed to set the receiving mailbox!\r\n");
}
break;
case waitfordate354:
if (strstr((const char *)buf, data_reply) != NULL)
{
ret = send(sn, (uint8_t *)mime, (uint16_t)strlen(mime));
SMTP_STATE = waitformime250;
}
else
{
printf("Failed to send content setup\r\n");
}
break;
case waitformime250:
if (strstr((const char *)buf, mime_reply) != NULL)
{
Mail_Send_OK = 1U;
printf("mail send OK\r\n");
}
break;
default:
break;
}
(void)ret;
}
SMTP 状态机流程:① 握手:发送 HELO 命令,等待 250 OK;② 用户认证:发送 AUTH LOGIN → 334 请求用户名 → 发送 Base64 用户名 → 334 请求密码 → 发送 Base64 密码 → 235 认证成功;③ 设置地址:MAIL FROM → 250 → RCPT TO → 250;④ 传输内容:DATA → 354 → 发送 MIME 报文 → 250 排队成功;⑤ 设置 Mail_Send_OK = 1 并打印 mail send OK。
运行验证
打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先进行 PHY 链路检测和 DHCP 获取网络地址,然后 DNS 解析 SMTP 服务器域名,最后连接 SMTP 服务器并发送邮件。预期串口输出如下:
MH2457 W6300 QSPI example start
W6300 SMTP example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
W6300 network configuration : DHCP
MAC : 00:08:DC:12:34:56
IP : 192.168.1.8
Subnet Mask : 255.255.255.0
Gateway : 192.168.1.1
DNS : 192.168.1.1
====================================================================================================
> Translated smtp.126.com to 111.124.203.46
mail send OK
串口打印 mail send OK 表示邮件发送成功。可在收件人邮箱中查收到的邮件,邮件主题为 Hello!WIZnet!。


常见问题
Q: 邮件发送失败怎么办
排查步骤:① 确认开发板能访问互联网;② 确认 DNS 解析成功(串口打印 > Translated smtp.126.com to ...);③ 确认邮箱已开启 SMTP 服务并获取授权码;④ 确认发件人、收件人邮箱地址填写正确;⑤ 检查 25 端口是否被防火墙拦截;⑥ 查看串口打印的错误信息定位状态机失败的位置。
Q: 为什么使用 126 邮箱的授权码而不是登录密码
126 邮箱(以及大部分公共邮箱)出于安全考虑,在第三方客户端登录时要求使用专门的"授权码"而不是登录密码。授权码在邮箱设置中开启 SMTP 服务后生成,作为第三方客户端的登录密码使用。
Q: 为什么用户名和密码需要 Base64 编码
SMTP 认证过程中(AUTH LOGIN),用户名和密码按照 Base64 编码传输。这是 SMTP 协议规范的要求,Base64 编码后的字符都是可打印 ASCII 字符,便于在文本协议中传输。base64encode() 函数实现了标准 Base64 编码。
Q: 如何发送带附件的邮件
本例程只发送纯文本邮件,Content-Type 为 text/plain。要发送附件需修改 MIME 报文头为 multipart/mixed,并按照 MIME 规范对附件进行 Base64 编码后拼接到邮件体中,工作量和内存占用都会显著增大。
总结
本文讲解了如何在 W6300 芯片上实现 SMTP 协议:
- SMTP 协议:基于 TCP 25 端口的简单邮件传输协议,采用请求-响应模式,报文纯文本
- 发送流程:TCP 连接 → HELO 握手 → AUTH LOGIN 认证 → MAIL FROM / RCPT TO 设置地址 → DATA 传输内容 → 完成
- 关键 API:
mailmessage()内容拼接、base64encode()认证编码、do_smtp()TCP 状态机、send_mail()SMTP 状态机 - 状态机模式:通过
SMTP_STATE变量在 8 个状态间切换,每收到一次服务器响应就推进到下一个状态 - W6300 平台特有:socket() 使用 Sn_MR_TCP4,connect() 需传地址长度,DNS 时基通过软件定时器链表提供
- 应用价值:设备远程监控、环境监测、故障报警、日志记录等场景中通过邮件通知管理人员