ardunio-R4-WiFi连接实战
ardunio R4 WiFi连接实战
ardunio WiFi连接模板
ardunio R4 WiFi 开发板有着不错的性能和板载内存,本机自带 WiFi 连接模块,可以完成简单的网络服务。对于这个小东西我情有独钟,也总希望能够用它来做些什么,所以先从 WiFi 连接开始学起,未来考虑一步一步为它接入大模型服务,做出一个小的桌面显示小玩具。
WiFiClient
只能访问简单的http链接
ardunio R4 WiFi开发板依赖
WiFiS3.h
库进行网络连接。同时,最好新建一个保存密钥的头文件保存WiFi名称和密码。
使用
WiFiClient
声明
client
对象,用于表示一个TCP/IP客户端(类似于网络套接字)。并声明WiFi连接状态
status
,初始化为
WL_IDLE_STATUS
。
WifiS3
库内置如下几种WiFi连接状态:
WL_NO_SHIELD
** / **WL_NO_MODULE
:没有检测到WiFi模块。WL_IDLE_STATUS
:WiFi模块处于空闲状态。WL_NO_SSID_AVAIL
:没有可用的SSID。WL_SCAN_COMPLETED
:WiFi扫描完成。WL_CONNECTED
:成功连接到WiFi网络。WL_CONNECT_FAILED
:连接失败。WL_CONNECTION_LOST
:连接丢失。WL_DISCONNECTED
:断开连接。WL_AP_LISTENING
:WiFi模块作为接入点监听中。WL_AP_CONNECTED
:设备已连接到WiFi模块作为接入点。WL_AP_FAILED
:接入点模式启动失败。
WiFiClient client;
int status = WL_IDLE_STATUS;
之后在
setup
中配置网络连接初始化:声明波特率 → WiFi连接 → 尝试连接直到成功 → 打印IP。
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Connecting to WiFi");
WiFi.begin(ssid, pass); //WifiS3库已经内置WiFi全局变量,可以直接使用
while(WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println("Connected to WiFi");
Serial.println("IP Address: ");
Serial.println(WiFi.localIP());
}
WiFiSSLClient
可以访问https安全链接
WiFiClient
只能访问基础的http连接,想要允许 Arduino 设备通过 WiFi 网络与支持 SSL/TLS 加密的服务器建立安全连接,从而安全地发送和接收数据需要使用
WiFiSSLClient
类。它的使用方法和
WiFiClient
相似,只需改为声明
WiFiSSLClient client;
。
为了向服务器发送请求,我们使用如下方式添加请求内容:
if (client.connect(server, 443)) {
Serial.println("connected to server");
client.println("GET / HTTP/1.1");
client.println("Host: www.google.com");
client.println("Connection: close");
client.println();
}
client.connect(server, 443)
这行代码尝试通过TCP/IP协议连接到目标服务器的443端口(HTTPS默认端口)。如果连接成功,说明Arduino已经与网页服务器建立了通信通道。
client.println()
是用来向服务器发送HTTP请求,
GET / HTTP/1.1
表示请求根路径(
/
)的内容,使用HTTP 1.1协议;**
Host: www.google.com
指定目标主机名;
Connection: close
**告诉服务器在响应完成后关闭连接。
请求完成,我们可以使用如下方式查看服务器响应内容:
void read_response() {
uint32_t received_data_num = 0;
while (client.available()) {
/* actual data reception */
char c = client.read();
/* print data to serial port */
Serial.print(c);
/* wrap data to 80 columns*/
received_data_num++;
if(received_data_num % 80 == 0) {
Serial.println();
}
}
}
参考完整示例,访问少数派文章
/*
TLS WiFi Web client - 访问少数派文章
Board CA Root certificate bundle is embedded inside WiFi firmware:
https://github.com/arduino/uno-r4-wifi-usb-bridge/blob/main/certificates/cacrt_all.pem
*/
#include "WiFiS3.h"
#include "WiFiSSLClient.h"
#include "IPAddress.h"
#include "secret.h"
///please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;
// 修改为少数派网站
char server[] = "sspai.com"; // 少数派网站域名
// Initialize the SSL client
WiFiSSLClient client;
/* -------------------------------------------------------------------------- */
void setup() {
/* -------------------------------------------------------------------------- */
//Initialize serial and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network.
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
printWifiStatus();
Serial.println("\nStarting connection to sspai.com...");
// if you get a connection, report back via serial:
if (client.connect(server, 443)) {
Serial.println("connected to server");
// Make a HTTP request for the specific article:
client.println("GET /post/97248 HTTP/1.1");
client.println("Host: sspai.com");
client.println("User-Agent: Mozilla/5.0 (compatible; Arduino/1.0)"); // 添加更友好的User-Agent
client.println("Accept: text/html"); // 接受HTML内容
client.println("Connection: close");
client.println();
} else {
Serial.println("Connection to server failed");
}
}
/* just wrap the received data up to 80 columns in the serial print*/
/* -------------------------------------------------------------------------- */
void read_response() {
/* -------------------------------------------------------------------------- */
uint32_t received_data_num = 0;
while (client.available()) {
/* actual data reception */
char c = client.read();
/* print data to serial port */
Serial.print(c);
/* wrap data to 80 columns*/
received_data_num++;
if(received_data_num % 80 == 0) {
Serial.println();
}
}
}
/* -------------------------------------------------------------------------- */
void loop() {
/* -------------------------------------------------------------------------- */
read_response();
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting from server.");
client.stop();
// do nothing forevermore:
while (true);
}
}
/* -------------------------------------------------------------------------- */
void printWifiStatus() {
/* -------------------------------------------------------------------------- */
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
串口打印示例:
14:11:22.536 -> SSID: ZTE_2AED09
14:11:22.536 -> IP Address: 192.168.0.5
14:11:22.578 -> signal strength (RSSI):-51 dBm
14:11:22.578 ->
14:11:22.578 -> Starting connection to sspai.com...
14:11:23.804 -> connected to server
14:11:24.536 -> HTTP/1.1 200 OK
14:11:24.536 -> Date: Thu, 13 Mar 2025 06:11:22 GMT
14:11:24.536 -> Content-Type: text/html; c
14:11:24.536 -> harset=utf-8
14:11:24.536 -> Content-Length: 119824
14:11:24.536 -> Connection: close
14:11:24.536 -> Vary: Accept-Encoding
14:11:24.536 ->
14:11:24.536 -> Cache-Control: no-store
14:11:24.536 ->
14:11:24.536 -> <!DOCTYPE html>
14:11:24.536 -> <html lang="zh-CN" id="html">
14:11:24.536 -> <head>
14:11:24.536 ->
14:11:24.536 -> <meta charset="utf-8">
14:11:24.536 -> <meta name="viewport" content="width=device-width, us
14:11:24.536 -> er-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewpor
14:11:24.568 -> t-fit=cover">
14:11:24.568 -> <meta name="theme-color" content="" />
14:11:24.568 -> <meta
14:11:24.568 -> http-equiv="C
14:11:24.568 -> ontent-Security-Policy"
14:11:24.568 -> content="frame-src https://sspai.com https://*.sspai
14:11:24.568 -> .com https://*.bilibili.com https://bilibili.com https://v.qq.com https://embed.
14:11:24.568 -> music.apple.com https://jinshuju.net https://share.newsroom.apple;"
14:11:24.607 -> >
14:11:24.607 -> <meta
14:11:24.607 -> name="fragment" content="!">
14:11:24.607 -> <link rel="shortcut icon" href="https://cdn-stati
14:11:24.607 -> c.sspai.com/favicon/sspai.ico" type="image/x-icon" />
14:11:24.607 -> <link rel="icon" href="h
...
14:11:48.951 -> var _hmt = _hmt || []
14:11:48.951 -> ;
14:11:48.951 -> (function () {
14:11:48.951 -> var hm = document.createElement("script");
14:11:48.951 -> hm.s
14:11:48.951 -> rc = "//hm.baidu.com/hm.js?92174dab8163cf598817a93d11d5c588";
14:11:48.984 -> var s = docu
14:11:48.984 -> ment.getElementsByTagName("script")[0];
14:11:48.984 -> s.parentNode.insertBefore(hm, s);
14:11:48.984 ->
14:11:48.984 -> })();
14:11:48.984 ->
14:11:48.984 -> </script>
14:11:48.984 -> <!-- Baidu Push -->
14:11:48.984 -> <script>
14:11:48.984 -> (function () {
14:11:48.984 ->
14:11:48.984 -> var bp = document.createElement('script');
14:11:48.984 -> var curProtocol = window.locat
14:11:48.984 -> ion.protocol.split(':')[0];
14:11:48.984 -> if (curProtocol === 'https') {
14:11:48.984 -> bp.src
14:11:48.984 -> = 'https://zz.bdstatic.com/linksubmit/push.js';
14:11:48.984 -> } else {
14:11:49.025 -> bp.src =
14:11:49.025 -> 'http://push.zhanzhang.baidu.com/push.js';
14:11:49.025 -> }
14:11:49.025 -> var s = document.getEle
14:11:49.025 -> mentsByTagName("script")[0];
14:11:49.058 -> s.parentNode.insertBefore(bp, s);
14:11:49.058 -> })();
14:11:49.058 ->
14:11:49.058 -> </script>
14:11:49.058 -> </body>
14:11:49.058 -> </html>
14:11:49.058 ->
14:11:49.058 -> disconnecting from server.
WiFiServer
把ardunio作为服务器
ardunio同样可以作为服务器使用,提供浏览器以http访问IP的能力。它的交互过层如下:
- 浏览器(客户端)连接到Arduino的IP地址
- 浏览器发送HTTP请求
- Arduino处理请求并返回网页内容
- 浏览器接收内容并断开连接
- Arduino在串口打印“客户端断开连接”
通过声明
WiFiServer server(80);
来定义服务器。之后,使用
server.begin();
启动服务器。主循环参考如下:
void loop() {
// 检查是否有新的客户端连接
WiFiClient client = server.available();
if (client) {
Serial.println("新客户连接");
String currentLine = ""; // 用于存储当前读取的HTTP请求行
// 当客户端保持连接时持续处理
while (client.connected()) {
// 检查是否有数据可读
if (client.available()) {
// 逐字符读取HTTP请求
char c = client.read();
Serial.write(c); // 将读取的字符输出到串口监视器
// 处理换行符
if (c == '\n') {
// 空行标志着HTTP请求头的结束
if (currentLine.length() == 0) {
sendHttpResponse(client); // 发送HTTP响应
break; // 退出处理循环
} else {
currentLine = ""; // 重置当前行,准备读取下一行
}
} else if (c != '\r') { // 忽略回车符
currentLine += c; // 将字符添加到当前行
}
}
}
delay(1); // 短暂延时确保数据传输完成
client.stop(); // 关闭客户端连接
Serial.println("客户端断开连接");
}
}
当浏览器向ardunio发送请求时,ardunio服务器会向客户端发送HTTP响应,通过自定义的
sendHttpResponse
函数封装了这个响应,它具体如下:
void sendHttpResponse(WiFiClient &client) {
// 发送HTTP头
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html; charset=UTF-8");
client.println("Connection: close");
client.println();
// 发送HTML内容
client.print(HTML_HEADER);
// 可以在这里处理动态内容替换
String content = HTML_CONTENT;
content.replace("%SERVER_TIME%", String(millis() / 1000));
client.print(content);
client.print(HTML_FOOTER);
}
client.println("HTTP/1.1 200 OK");
:这行代码发送HTTP状态行。 HTTP/1.1 表示使用的HTTP协议版本, 200 是状态码,代表请求已成功处理, OK 是状态码对应的文本描述。客户端收到这个状态行后,就知道请求已经成功处理。client.println("Content-Type: text/html; charset=UTF-8");
:这行代码发送 Content-Type 头部。它告诉客户端响应内容的类型是HTML,并且使用的字符编码是UTF - 8。这样客户端就能正确解析和显示响应内容。client.println("Connection: close");
:这行代码发送 Connection 头部。 close 表示在响应完成后,服务器会关闭与客户端的连接。这是一种常见的做法,特别是在处理简单的HTTP请求时。client.println();
:这行代码发送一个空行。在HTTP协议中,空行用于分隔头部和主体。发送空行后,后续发送的内容就是响应的主体部分了。
服务器完整示例如下
#include "WiFiS3.h"
#include "IPAddress.h"
#include "secret.h" // 确保创建此文件
#include "html_content.h"
// 在arduino_secrets.h中定义您的网络凭据
// #define SECRET_SSID "ZTE_2AED09"
// #define SECRET_PASS "您的密码"
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
// 初始化串口通信
Serial.begin(115200);
while (!Serial) {
; // 等待串口连接
}
Serial.println("Arduino Web Server启动");
// 检查WiFi模块
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("WiFi模块通信失败!");
while (true); // 不继续
}
// 检查固件版本
String fv = WiFi.firmwareVersion();
Serial.print("WiFi固件版本: ");
Serial.println(fv);
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("请升级固件");
}
// 连接到WiFi网络
while (status != WL_CONNECTED) {
Serial.print("尝试连接到SSID: ");
Serial.println(ssid);
// 连接到WPA/WPA2网络
status = WiFi.begin(ssid, pass);
// 等待10秒连接
delay(10000);
}
// 打印WiFi状态
printWifiStatus();
// 启动服务器
server.begin();
Serial.println("服务器已启动");
}
void loop() {
// 检查是否有新的客户端连接
WiFiClient client = server.available();
if (client) {
Serial.println("新客户连接");
String currentLine = ""; // 用于存储当前读取的HTTP请求行
// 当客户端保持连接时持续处理
while (client.connected()) {
// 检查是否有数据可读
if (client.available()) {
// 逐字符读取HTTP请求
char c = client.read();
Serial.write(c); // 将读取的字符输出到串口监视器
// 处理换行符
if (c == '\n') {
// 空行标志着HTTP请求头的结束
if (currentLine.length() == 0) {
sendHttpResponse(client); // 发送HTTP响应
break; // 退出处理循环
} else {
currentLine = ""; // 重置当前行,准备读取下一行
}
} else if (c != '\r') { // 忽略回车符
currentLine += c; // 将字符添加到当前行
}
}
}
delay(1); // 短暂延时确保数据传输完成
client.stop(); // 关闭客户端连接
Serial.println("客户端断开连接");
}
}
void sendHttpResponse(WiFiClient &client) {
// 发送HTTP头
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html; charset=UTF-8");
client.println("Connection: close");
client.println();
// 发送HTML内容
client.print(HTML_HEADER);
// 可以在这里处理动态内容替换
String content = HTML_CONTENT;
content.replace("%SERVER_TIME%", String(millis() / 1000));
client.print(content);
client.print(HTML_FOOTER);
}
void printWifiStatus() {
// 打印SSID
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// 打印IP地址
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// 打印信号强度
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
// 显示访问链接
Serial.println();
Serial.print("在浏览器中访问 http://");
Serial.println(ip);
}
http网页
#ifndef HTML_CONTENT_H
#define HTML_CONTENT_H
// HTML头部
const char HTML_HEADER[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Arduino Web服务器</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
text-align: center;
background-color: #f0f0f0;
}
h1 {
color: #0066cc;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="container">
)rawliteral";
// 主要内容
const char HTML_CONTENT[] PROGMEM = R"rawliteral(
<h1>Arduino Web服务器</h1>
<p>欢迎访问Arduino UNO R4 WiFi服务器!</p>
<p>服务器运行正常</p>
<p>当前时间: <span id="server-time">%SERVER_TIME%</span></p>
)rawliteral";
// 底部
const char HTML_FOOTER[] PROGMEM = R"rawliteral(
</div>
</body>
</html>
)rawliteral";
#endif
串口打印
打开浏览器,访问