目录

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_STATUSWifiS3 库内置如下几种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的能力。它的交互过层如下:

  1. 浏览器(客户端)连接到Arduino的IP地址
  2. 浏览器发送HTTP请求
  3. Arduino处理请求并返回网页内容
  4. 浏览器接收内容并断开连接
  5. Arduino在串口打印“客户端断开连接”

https://i-blog.csdnimg.cn/img_convert/68bbf80da815bbc7f572cb5a9ccc9bb3.jpeg

通过声明 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

串口打印

https://i-blog.csdnimg.cn/img_convert/a4f6355244bcbea04c667c3790ffba29.jpeg

打开浏览器,访问

https://i-blog.csdnimg.cn/img_convert/b8719b6a2dd3d7c4ca4267906b47b616.jpeg