惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
AI
AI
Security Latest
Security Latest
GbyAI
GbyAI
P
Proofpoint News Feed
Y
Y Combinator Blog
A
Arctic Wolf
G
Google Developers Blog
U
Unit 42
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
C
Check Point Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
WordPress大学
WordPress大学
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
Project Zero
Project Zero
博客园_首页

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
ardunio R4 WiFi连接实战 - 少数派
2025-03-13 · via 少数派

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();
        }
    }
}

参考完整示例,访问少数派文章为Claude桌面端集成tavily搜索 - 少数派

/*
  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在串口打印“客户端断开连接”

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

串口打印

打开浏览器,访问http://192.168.0.5