
想让ESP32真正变成一个"能被手机访问"的设备,Web Server是绕不开的第一课。这篇把Random Nerd Tutorials上Rui Santos那篇经典教程重新理顺了一遍,代码直接取自GitHub仓库RuiSantosdotme/ESP32-Course里的WiFi_Web_Server_Outputs.ino,逐字符核对过,照着做就能跑通。
这个项目的功能很直白:ESP32连上家里的WiFi后,在局域网里起一个80端口的网页服务器,页面上有两路GPIO的开关按钮,浏览器点一下,板子上的LED就亮或灭。整个逻辑只用到WiFi.h这一个库,ESP32核心包自带,不需要装任何第三方依赖,对新手非常友好。
准备材料与接线
材料清单很短:一块ESP32开发板(教程用的是ESP32 DEVKIT DOIT 36-pin)、两颗5mm LED、两颗330欧电阻、一块面包板和几根杜邦线。

接线只有两个引脚要记:LED1接GPIO 26,LED2接GPIO 27,各自串一颗330欧电阻再回GND。画原理图的时候注意,不同批次的DEVKIT引脚丝印顺序略有差异,接之前对照自己板子的pinout确认,别照着别的板型硬怼。
这里有个常见的坑:GPIO 26和27本身没问题,但如果想换引脚,要避开GPIO 6到11(接的是内部Flash,动了容易变砖),也别用仅输入的GPIO 34到39。
代码逐段拆解

先看头部和全局变量部分:
// Load Wi-Fi library
#include "WiFi.h"
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
// Auxiliar variables to store the current output state
String output26State = "off";
String output27State = "off";
// Assign output variables to GPIO pins
const int output26 = 26;
const int output27 = 27;ssid和password改成你自己路由器的名字和密码,这是整套代码里唯一必须动的地方。server(80)就是在80端口起服务,浏览器默认访问的端口就是它,所以后面直接输IP就能打开页面。
setup()负责初始化引脚和连WiFi:
void setup() {
Serial.begin(115200);
// Initialize the output variables as outputs
pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
// Set outputs to LOW
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}连WiFi就是标准的WiFi.begin()加while轮询,连上后把localIP打到串口,这个IP就是之后浏览器要访问的地址。
loop()里核心的开关判断逻辑是这一段,浏览器每次点按钮,请求头里都会带一个GET路径,代码靠字符串匹配来切换GPIO状态:
// turns the GPIOs on and off
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output26State = "on";
digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("GPIO 26 off");
output26State = "off";
digitalWrite(output26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
Serial.println("GPIO 27 on");
output27State = "on";
digitalWrite(output27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
Serial.println("GPIO 27 off");
output27State = "off";
digitalWrite(output27, LOW);
}整个方案没有用任何HTTP服务器库,就是手动读请求头、匹配字符串、再手写一段HTML回给浏览器,页面里两个按钮的样式内联在CSS里,ON键绿色、OFF键灰色。这种"裸写"的写法的好处是能看清楚HTTP交互的全过程,对理解Web服务的工作原理帮助很大。
另外loop()里有一个两秒的超时保护(timeoutTime = 2000),客户端超过两秒没发完请求就断开连接,防止浏览器连接挂着不释放把服务卡死。改代码的时候别把这个删了。
烧录与拿到IP地址

前提是Arduino IDE里已经装好ESP32的板支持包,如果还没装,先去Boards Manager里把esp32包装上。然后按三步走:Tools菜单里把Board选成ESP32 DEVKIT DOIT,Port选板子对应的COM口,点Upload等它跑完。
上传成功后打开串口监视器,波特率必须设成115200,这是代码里Serial.begin定的,设错了只会看到乱码。然后按一下板子上的EN复位键,监视器里就会打出连接过程和IP地址,教程里的示例是192.168.1.135,你家局域网分到的地址会不一样。
如果一直打印点连不上WiFi,先确认路由器是2.4GHz频段,ESP32不支持5GHz,这个坑每年都能拦住一批人。
浏览器控制LED

在同一局域网的设备浏览器里输入刚才那个IP,就能看到控制页面:两行GPIO状态加两个按钮。点GPIO 26下面的ON,请求路径是/26/on,板子上的LED立刻点亮,页面状态刷新为on;点OFF灭掉,GPIO 27同理。
与此同时串口监视器会实时打出HTTP请求头原文,你能亲眼看到GET /26/on HTTP/1.1这一行是怎么被代码捕获并处理的。想验证服务器逻辑,这是最好的观察窗口。
小结与下一步
这个项目麻雀虽小五脏俱全:WiFi连接、TCP服务、HTTP解析、GPIO控制一条链全走了一遍。跑通之后顺手就能往上叠东西:把LED换成继电器控制插座,或者按教程作者的新版指南加上DHT传感器读温湿度,把数据渲染到网页上,就是一个最简的家居监控面板。
复现层面再提醒一句:代码务必从GitHub仓库原样拷贝,不要手抄,引脚号和引号差一个字符就是一堆编译报错。跑不通的时候,串口监视器的报错信息是第一手线索,别急着改代码,先读懂它在抱怨什么。











这个项目我去年照着原版跑过一遍,说两个新手容易栽的点:一是串口监视器波特率必须115200,不然打印的全是乱码还以为板子坏了;二是WiFi.begin之前记得先pinMode,我当时把初始化顺序写反了,LED上电就乱闪。文章里两秒超时那个细节讲得好,很多人不知道client挂着不放会把服务卡死。
照着做通了,手机浏览器点ON灯真的亮,很有成就感
GET /26/on匹配请求头这个思路挺适合教学的,虽然实际项目我会换ESPAsyncWebServer,但裸写一遍才能看懂HTTP到底怎么交互的
2.4GHz那个坑是真的,我第一次就是连着5G频段死活连不上,换了手机热点才反应过来