ESP8266 制作一个网页控制程序
手上的这块MCU为ESP8266,在arduino中进行开发需要安装对应的支持包,这里up选择nodemcu 1.0 ESP-12E版本。
一、核心硬件清单
首先确认需要准备的硬件,避免接线时遗漏:
ESP8266 开发板(如 NodeMCU、D1 mini,引脚定义需与代码一致)
SSD1306 128×32 OLED 显示屏(I2C 接口)
舵机(SG90 等常见型号,支持 500-2500μs 脉冲控制)
按键(1 个,用于触发舵机往复转动)
LED(1 个,需串联 220Ω 限流电阻,防止烧毁)
杜邦线(公对母 / 公对公若干)
电源(ESP8266 建议 5V/1A 供电,舵机若电流较大需单独 5V 供电)
代码:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Servo.h>
#include <WiFiManager.h>
#include <ESP8266WebServer.h>#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);Servo myservo; // create servo object to control a servoconst int buttonPin = D5; // the number of the pushbutton pin
int buttonState = 0; // variable for reading the pushbutton statusconst int togglePin = D0; // the number of the toggle pin
bool toggleState = false; // variable for storing the toggle stateconst int ledPin = D7; // LED 连接的引脚ESP8266WebServer server(80);void setup() {Serial.begin(115200);// Initialize the OLED displayif (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {Serial.println(F("SSD1306 allocation failed"));for (;;); // Don't proceed, loop forever}display.clearDisplay();display.setTextSize(1); // Normal 1:1 pixel scaledisplay.setTextColor(SSD1306_WHITE); // Draw white textdisplay.setCursor(0, 0); // Start at top-left cornerdisplay.println("Initializing...");display.display();// WiFi 配置WiFiManager wifiManager;if (!wifiManager.autoConnect("ESP8266_AP")) {Serial.println("Failed to connect and hit timeout");// 可以在这里添加更多错误处理逻辑delay(3000);// 重置并尝试重新连接ESP.reset();delay(5000);}Serial.println("Connected to WiFi");display.clearDisplay();display.setCursor(0, 0);display.println("Connected to WiFi");display.print("IP: ");display.println(WiFi.localIP());display.display();// Attach the servo on pin D6 to the servo objectmyservo.attach(D6, 500, 2500); // Adjust min and max pulse widths// Set up the button pin as an inputpinMode(buttonPin, INPUT_PULLUP); // Internal pull-up resistor// Set up the toggle pin as an outputpinMode(togglePin, OUTPUT);digitalWrite(togglePin, LOW); // Initial state is LOW// Set up the LED pin as an outputpinMode(ledPin, OUTPUT);// Initial position of the servomyservo.write(90);// 处理根路径请求server.on("/", []() {String page = "<!DOCTYPE html>";page += "<html lang=\"en\">";page += "<head>";page += "<meta charset=\"UTF-8\">";page += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";page += "<script src=\"https://cdn.tailwindcss.com\"></script>";page += "<title>Device Control</title>";page += "</head>";page += "<body class=\"bg-gray-100 font-sans flex justify-center items-center h-screen\">";page += "<div class=\"bg-white p-8 rounded shadow-md w-full max-w-md\">";page += "<h1 class=\"text-2xl font-bold mb-6 text-center\">Device Control</h1>";page += "<form action='/setServo' method='get' class=\"mb-4\">";page += "<label for=\"angle\" class=\"block text-sm font-medium text-gray-700\">Set Servo Angle:</label>";page += "<input type='number' name='angle' min='0' max='180' class=\"mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm\">";page += "<button type='submit' class=\"mt-4 w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\">Set</button>";page += "</form>";page += "<form action='/toggleLED' method='get' class=\"mb-4\">";page += "<button type='submit' class=\"w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500\">Toggle LED</button>";page += "</form>";page += "<form action='/toggleD0' method='get'>";page += "<button type='submit' class=\"w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-yellow-600 hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500\">Toggle D0</button>";page += "</form>";page += "</div>";page += "</body>";page += "</html>";server.send(200, "text/html", page);});// 处理设置舵机角度请求server.on("/setServo", []() {if (server.hasArg("angle")) {int angle = server.arg("angle").toInt();if (angle >= 0 && angle <= 180) {myservo.write(angle);updateDisplay(angle);server.send(200, "text/plain", "Servo angle set to " + String(angle));} else {server.send(400, "text/plain", "Invalid angle value");}} else {server.send(400, "text/plain", "Missing angle parameter");}});// 处理切换 LED 请求server.on("/toggleLED", []() {toggleState = !toggleState;digitalWrite(ledPin, toggleState);updateDisplay(myservo.read());server.send(200, "text/plain", "LED state toggled");});// 处理切换 D0 请求server.on("/toggleD0", []() {toggleState = !toggleState;digitalWrite(togglePin, toggleState);updateDisplay(myservo.read());server.send(200, "text/plain", "D0 state toggled");});// 启动服务器server.begin();Serial.println("Server started");
}void loop() {server.handleClient();// Read the state of the button valuebuttonState = digitalRead(buttonPin);// Check if the button is pressedif (buttonState == LOW) {// Rotate the servo from 0 to 180 degreesfor (int pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degreesmyservo.write(pos); // tell servo to go to position in variable 'pos'updateDisplay(pos);delay(15); // waits 15ms for the servo to reach the position}// Rotate the servo back from 180 to 0 degreesfor (int pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degreesmyservo.write(pos); // tell servo to go to position in variable 'pos'updateDisplay(pos);delay(15); // waits 15ms for the servo to reach the position}// Wait until the button is releasedwhile (digitalRead(buttonPin) == LOW) {delay(50); // Debounce delay}}// Toggle D0 pin every 5 secondsstatic unsigned long lastToggleTime = 0;if (millis() - lastToggleTime > 5000) {lastToggleTime = millis();toggleState = !toggleState;digitalWrite(togglePin, toggleState);updateDisplay(myservo.read());}
}void updateDisplay(int servoPosition) {display.clearDisplay();display.setTextSize(1); // Normal 1:1 pixel scaledisplay.setTextColor(SSD1306_WHITE); // Draw white text// Display Servo Positiondisplay.setCursor(0, 0);display.print("Servo: ");display.print(servoPosition);display.println(" deg");// Draw progress bar for servo positiondrawProgressBar(0, 15, SCREEN_WIDTH - 2, 7, servoPosition / 180.0);// Display D0 Statedisplay.setCursor(0, 24);display.print("D0: ");display.print(digitalRead(togglePin) ? "HIGH" : "LOW");// Draw sun or moon icon based on D0 stateif (digitalRead(togglePin)) {drawSunIcon(SCREEN_WIDTH - 20, 24);} else {drawMoonIcon(SCREEN_WIDTH - 20, 24);}display.display();
}void drawProgressBar(int x, int y, int width, int height, float percentage) {display.drawRect(x, y, width, height, SSD1306_WHITE);int fillWidth = width * percentage;display.fillRect(x + 1, y + 1, fillWidth - 2, height - 2, SSD1306_WHITE);
}void drawSunIcon(int x, int y) {// Sun icon: small circle with raysdisplay.fillCircle(x, y, 3, SSD1306_WHITE);display.drawLine(x, y - 5, x, y - 8, SSD1306_WHITE); // Top raydisplay.drawLine(x, y + 5, x, y + 8, SSD1306_WHITE); // Bottom raydisplay.drawLine(x - 5, y, x - 8, y, SSD1306_WHITE); // Left raydisplay.drawLine(x + 5, y, x + 8, y, SSD1306_WHITE); // Right raydisplay.drawLine(x - 4, y - 4, x - 7, y - 7, SSD1306_WHITE); // Top-left raydisplay.drawLine(x + 4, y - 4, x + 7, y - 7, SSD1306_WHITE); // Top-right raydisplay.drawLine(x - 4, y + 4, x - 7, y + 7, SSD1306_WHITE); // Bottom-left raydisplay.drawLine(x + 4, y + 4, x + 7, y + 7, SSD1306_WHITE); // Bottom-right ray
}void drawMoonIcon(int x, int y) {// Moon icon: half-circledisplay.drawCircle(x, y, 3, SSD1306_WHITE);display.drawLine(x - 3, y, x + 3, y, SSD1306_WHITE); // Horizontal line through centerdisplay.drawLine(x - 3, y - 1, x + 3, y - 1, SSD1306_WHITE); // Line above centerdisplay.drawLine(x - 3, y + 1, x + 3, y + 1, SSD1306_WHITE); // Line below centerdisplay.drawLine(x - 3, y - 2, x + 3, y - 2, SSD1306_WHITE); // Line above centerdisplay.drawLine(x - 3, y + 2, x + 3, y + 2, SSD1306_WHITE); // Line below centerdisplay.drawLine(x - 3, y - 3, x + 3, y - 3, SSD1306_WHITE); // Line above centerdisplay.drawLine(x - 3, y + 3, x + 3, y + 3, SSD1306_WHITE); // Line below center
}
联网后可进行网页控制
硬件连接:
三、关键模块接线图解(以 NodeMCU 为例)
1. OLED 显示屏(I2C 接口)
SSD1306 OLED 通常有 4 个引脚:VCC、GND、SDA、SCL,接线如下:
OLED VCC → ESP8266 3.3V(不可接 5V! OLED 多数为 3.3V 供电)
OLED GND → ESP8266 GND
OLED SDA → ESP8266 D2(板载 SDA 引脚,代码中
Wire
库默认使用此引脚)OLED SCL → ESP8266 D1(板载 SCL 引脚,代码中
Wire
库默认使用此引脚)
2. 舵机(SG90 为例)
舵机通常有 3 根线:电源(红)、地(棕)、信号(橙):
舵机 红线 → 5V 电源(ESP8266 3.3V 可能供电不足,建议单独 5V)
舵机 棕线 → ESP8266 GND(必须与 ESP 共地)
舵机 橙线 → ESP8266 D6(代码中
myservo.attach(D6)
)
3. 按键(上拉输入模式)
代码中按键使用 INPUT_PULLUP
(内部上拉电阻),无需外部上拉电阻,接线最简单:
按键一端 → ESP8266 D5
按键另一端 → ESP8266 GND
按下按键时,D5 引脚被拉到 GND(LOW),触发舵机转动逻辑
4. LED(带限流电阻)
LED 必须串联限流电阻(220Ω~1kΩ),防止电流过大烧毁 ESP8266 引脚:
LED 正极 → 220Ω 电阻 → ESP8266 D7
LED 负极 → ESP8266 GND
四、接线注意事项(避坑指南)
舵机供电问题ESP8266 的 5V 引脚电流有限(通常≤500mA),若舵机转动时出现抖动或不响应,需给舵机单独供 5V 电源(如充电宝、18650 电池组),但必须保证 舵机 GND 与 ESP8266 GND 共地,否则信号无法正常传输。
OLED 地址匹配代码中 OLED 初始化用
display.begin(SSD1306_SWITCHCAPVCC, 0x3C)
,若 OLED 显示异常(如白屏、不显示),可能是 I2C 地址不匹配(部分 OLED 为0x3D
),需修改地址后重试。D0 引脚的特殊性ESP8266 的 D0 引脚(GPIO16)在复位时会输出高电平,且不能用于外部中断,代码中仅作为普通输出控制设备,避免用它接需要中断触发的模块(如红外接收、人体感应)。
共地原则所有模块(OLED、舵机、按键、LED)的 GND 必须连接到 ESP8266 的 GND,否则会因电位差导致通信失败(如 OLED 乱码、舵机不转、按键无响应)。
电源稳定性若同时接多个模块(如舵机 + OLED+LED),建议用 5V/1A 以上的电源给 ESP8266 供电,避免因电压不足导致 ESP 频繁重启(表现为串口打印断断续续、WiFi 频繁断开)。
五、接线后测试步骤
硬件检查:接线完成后,先检查是否有短路(如 VCC 与 GND 误接),确认无误后再通电。
串口调试:ESP8266 接电脑,打开 Arduino IDE 串口监视器(波特率 115200),通电后查看:
若显示
SSD1306 allocation failed
:检查 OLED 接线或 I2C 地址。若显示
Connected to WiFi
+ IP 地址:WiFi 配网成功,可通过该 IP 访问 Web 控制页。
功能测试:
按下按键(D5):舵机应从 0°→180°→0° 往复转动,OLED 实时显示舵机角度。
访问 Web 页(ESP 的 IP 地址):点击
Toggle LED
应控制 D7 引脚的 LED 亮灭;输入舵机角度点击Set
应控制舵机转到对应角度。等待 5 秒:D0 引脚电平应自动翻转(可通过万用表测量 D0 与 GND 之间的电压,高电平约 3.3V,低电平约 0V)。