Arduino 玩转 ESP8266
入门教程

本教程整理自公众号「开源造物手记」9 篇 ESP8266 入门文章,涵盖环境搭建、GPIO、按键、模拟输入输出等核心内容。点击左侧目录可展开章节与小结,平滑跳转阅读。

第 1 篇

新手的最佳选择Arduino玩转ESP8266

物联网入门系列 · 第1篇

Arduino玩转ESP8266

为什么这是新手最佳选择?

告别复杂配置,拥抱简单高效的开发生态

ESP8266 Arduino框架 新手友好 实战项目

写给初学者的抉择时刻

如果你刚开始接触ESP8266这个强大的Wi-Fi芯片,可能已经听说有两种主要开发方式:

两条技术路线

路线A:乐鑫官方SDK(ESP-IDF)官方原生开发框架,功能最全,性能最优,但学习曲线陡峭,需要掌握FreeRTOS、硬件配置等专业知识。

路线B:Arduino框架(ESP-Arduino)基于Arduino生态的兼容层,易学易用,有海量现成库,特别适合快速原型开发和初学者。

作为一个新手,选择Arduino框架几乎是必然的选择。今天,我将详细告诉你为什么。

Arduino vs 乐鑫SDK:全方位对比

对比维度
Arduino框架
乐鑫官方SDK
上手难度★ 极低
类C语言,简单直观
★★★ 较高
需掌握FreeRTOS
开发速度★★★★ 很快
安装即用,有现成库
★★ 较慢
需配置编译环境
生态资源★★★★★ 丰富
全球Arduino社区支持
★★★ 足够
官方文档和驱动
性能表现★★★ 良好
满足大部分应用需求
★★★★★ 优异
极致优化,原生控制
项目规模★ 中小型
原型验证、创意项目
★★★★ 大型
商业产品、复杂系统

总结:Arduino框架就像一辆自动挡汽车——上手快、操作简单;乐鑫SDK更像一辆手动挡赛车——性能强,但需要专业驾驶技术。

Arduino框架的4大优势

社区生态强大

全球最大的创客社区,遇到任何问题都能找到解决方案。无数现成的库可以直接调用。

快速原型开发

简单的setup()和loop()结构,让你在几分钟内就能点亮第一个LED,看到即时成果。

平滑迁移路径

掌握Arduino后,可以轻松迁移到ESP-IDF。很多Arduino代码可以直接重用。

专注功能实现

不用过多关注底层细节,集中精力实现你想要的功能,提升学习成就感。

从代码看差异

让我们用一个简单的WiFi连接示例,看看Arduino框架有多简单:

// Arduino框架代码 - 连接WiFi
#include
<ESP8266WiFi.h>
const char* ssid ="你的WiFi名";
const char* password ="你的密码";
void setup() {
  Serial.begin(115200);
  delay(1000);
// 就是这么简单!
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("连接成功!");
  }
void loop() {
    // 主循环代码
}

同样的功能如果用乐鑫SDK实现,代码量要多出3-5倍,而且需要处理更多的配置细节,更要命的是可能你折腾半天连编程环境都还没搞定,更别说配置了。对于新手来说,简洁就是生产力!而Arduino就是这么的简洁。

所以,对于新手来说,对于科创项目来说Arduino无疑是最优的选择。

第 2 篇

从零开始搭建ESP8266开发环境

软件+硬件,一站式搞定!告别配置烦恼,30分钟搞定从零到开发

01 开发前的必要准备

在上一篇中,我们了解了为什么选择Arduino框架。今天,我们将正式开始动手!搭建一个完整的ESP8266开发环境需要软件+硬件两方面准备。

本篇目标

通过本文,你将完成:

  1. 1. ✅ 准备好所有必需的硬件设备
  2. 2. ✅ 安装并配置Arduino IDE
  3. 3. ✅ 添加ESP8266开发板支持
  4. 4. ✅ 完成第一个程序上传测试

02 硬件清单:你需要准备什么?

ESP8266有多种开发板型号,对于新手强烈推荐D1 mini开发板,理由如下:

  • 小巧精致: 开发板主板尺寸仅约25mm×35mm,比NodeMCU小很多,方便你把作品做小,非常适合DIY项目
  • 安全可靠: 开发板只引出了用户可用的GPIO引脚,不会造成因连接错误而烧毁Flash的问题
  • 功能完整: 和NodeMCU开发板使用的是相同的ESP-12F主控模组,功能上没有任何区别,完全兼容
  • 性价比高: 价格通常比NodeMCU更便宜,淘宝上约10-15元即可入手
  • USB数据线多选: Micro-USB接口和type-C接口可选

购买建议

购买方式: 某宝直接搜索"D1 mini开发板"注意事项: 现在网上有ESP8266和ESP32主控的不同开发板,外形相似不易辨认。初学建议先入手ESP8266主控版本的D1 mini,性价比更高。推荐: 建议连带面包板和LED、电阻一起购买,方便后续的入门学习。淘宝上有很多"D1 mini入门套件",价格约30-50元,非常划算,也可自行组合购买更便宜。

回复杠精

有人可能会说:"现在ESP8266没用了,都转战ESP32了。"我觉得,新手入门时,简单是关键,便宜是核心以最简单的方式先让自己动起来,有成效、有收获、有成就感——这才是继续下去的最好动力。等你对物联网有了感觉,再升级到ESP32也不迟。再说,简单的开关控制,舵机旋转,NTP网络时钟之类的入门小项目,ESP8266足够。

03 软件安装:四步搞定Arduino IDE

步骤1:下载Arduino IDE

官方下载地址: https://www.arduino.cc/en/software

版本选择:

操作系统
推荐版本
说明
Windows
Windows Win 7 and newer
安装版(.exe文件)
macOS
macOS 64 bits
安装版(.dmg文件)
Linux
Linux 64 bits
安装版(.tar.xz文件)

安装前的重要提醒:

  1. 1. 操作系统用户名不能是中文如果Windows用户名是中文,可能导致Arduino IDE无法正常工作
  2. 2. 安装目录不能有中文建议安装到 D:\ArduinoC:\Arduino
  3. 3. 不要安装在C:\Program Files这个目录有权限限制,可能导致上传失败

步骤2:安装Arduino IDE

  1. 1. 双击下载的.exe安装文件
  2. 2. 按照安装向导提示进行安装
  3. 3. 重要: 选择安装目录时,确保路径没有中文,且不要安装在C:\Program Files
  4. 4. 安装完成后,桌面会出现Arduino IDE图标

步骤3:配置中文界面(可选)

Arduino IDE支持中文界面,配置方法:

1. 点击 "文件" → "首选项" (File → Preferences)
2. 找到 "语言" (Language) 选项
3. 选择 "中文(简体)" (Chinese (Simplified))
4. 点击 "确定",重启软件

重启后,界面就会变成中文,对新手更加友好。

04 USB驱动安装

D1 mini开发板通过USB连接到电脑时,需要正确的驱动程序才能被识别。大多数D1 mini使用的USB是CH340芯片,需要安装CH340驱动。

步骤1:确认开发板使用的USB芯片

D1 mini开发板通常使用以下两种USB转串口芯片:

芯片型号
驱动名称
如何识别
CH340
CH340驱动
开发板上芯片印有"CH340"字样
CP2102
CP210x驱动
开发板上芯片印有"CP2102"字样

步骤2:下载并安装驱动

CH340驱动下载:

CP210x驱动下载(直接下载):

安装完成后,需要重启电脑,驱动才能生效。

05 配置ESP8266开发板支持

Arduino IDE默认不支持ESP8266,需要手动添加开发板支持。提供两种方法,推荐方法一:

方法一:离线安装(推荐)

适合网络环境不好或想要快速安装的用户。

步骤1:下载离线安装包

下载地址:链接: https://pan.baidu.com/s/1wuG06TgRrT-zL_Vc5RTpWA?pwd=kyzw 提取码: kyzw链接中包含:Arduino IDE + ESP8266离线包 + ESP32离线包

步骤2:运行安装程序

双击运行ESP8266的.exe安装程序,按照提示完成安装。

安装完成后重启Arduino IDE。

步骤3:验证安装

点击"工具" → "开发板",应该能看到"ESP8266 Boards"选项。

方法二:在线安装

适合网络环境好的用户。

步骤1:添加开发板管理器地址

点击"文件" → "首选项",在"附加开发板管理器网址"中添加:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

步骤2:安装ESP8266库

点击"工具" → "开发板" → "开发板管理器",搜索"esp8266",找到并安装。版本选择: 安装最新版即可

常见问题解决

问题1: 安装失败,显示网络错误解决: 使用离线安装包,或检查网络代理设置问题2: 安装后找不到ESP8266选项解决: 完全关闭Arduino IDE再重新打开

06 开发板与端口设置

安装完成后,需要正确设置开发板和端口,这是最关键的一步

设置项
正确值
说明
开发板
WeMos D1 R2 & mini 或 LOLIN(WEMOS) D1 R2 & mini
D1 mini对应的Arduino IDE开发板选项
Upload Speed
115200
上传速度,保持默认
CPU Frequency
80 MHz
CPU频率,保持默认
Flash Size
4M (3M SPIFFS)
Flash大小,D1 mini标准配置
端口
COM3 或 /dev/ttyUSB0
连接开发板后自动出现

端口找不到怎么办?

第一步:确认开发板使用的芯片

  1. 1. 查看D1 mini开发板上的USB转串口芯片
  2. 2. 如果芯片印有"CH340",需要安装CH340驱动
  3. 3. 如果芯片印有"CP2102",需要安装CP210x驱动

第二步:检查驱动是否安装正确

  1. 1. 打开设备管理器(Win+X → 设备管理器)
  2. 2. 查看"端口(COM和LPT)",确认出现的设备与开发板芯片一致
  3. 3. 如果芯片是CH340,应该显示"USB-SERIAL CH340"
  4. 4. 如果芯片是CP2102,应该显示"Silicon Labs CP210x..."
  5. 5. 如果设备名称不对或没有设备,说明安装的驱动和硬件不一致,需要安装正确的驱动

第三步:安装或重新安装正确的驱动

  • • CH340驱动:访问WCH官网下载
  • • CP210x驱动:访问Silicon Labs官网下载
  • • 安装完成后重启电脑

07 第一个测试:Blink程序

让我们用经典的"Blink"程序来验证一切是否正常。

步骤1:打开示例程序

点击"文件" → "示例" → "01.Basics" → "Blink"

步骤2:查看代码

你会看到如下代码:

void setup() {
  // 初始化LED引脚为输出模式
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // 熄灭LED
  delay(1000);                       // 等待1秒
  digitalWrite(LED_BUILTIN, LOW);    // 点亮LED
  delay(1000);                       // 等待1秒
}

步骤3:上传程序

  1. 1. 用USB线连接D1 mini开发板到电脑,注意COM端口号。
  2. 2. 点击右上角的"→"按钮(上传)
  3. 3. 等待编译和上传完成

上传成功标志: 底部显示"上传成功",并且开发板上的蓝色LED开始闪烁(约1秒一次)。提示: D1 mini的板载LED连接在GPIO2(D4)引脚,和NodeMCU相同,所以Blink程序可以直接使用。

步骤4:修改并测试

尝试修改延迟时间,比如把delay(1000)改为delay(200),重新上传看看效果!

恭喜!如果你看到LED在闪烁,说明你已经成功搭建了ESP8266开发环境!这是物联网开发的第一步,也是最关键的一步。

📥 资源下载汇总

为了方便大家,这里汇总了本文提到的所有资源:

第 3 篇

Arduino玩转ESP8266  入门系列-认识面包板

ESP8266入门系列难度:零基础预计阅读时间:8分钟动手实践时间:20分钟

前言:一块切面包的木板,改变了电子世界

今天从一个有趣的故事开始关于"面包板"这个名字是怎么来的故事发生在很久以前(大约1920~1930年代),那时候还没有集成电路,电子电路的主角是真空管,体积大、发热厉害,但这就是那个时代的"芯片"。业余无线电爱好者想自己做一台发射机,问题来了:零件往哪儿装?有人灵机一动,从厨房里拿了一块切面包用的木板(真的就是面包板!),在木板上钉了几根钉子,把真空管插座、线圈、电容这些零件钉在钉子上,再用导线绕接起来……一块电路板,就这样诞生了。

还有个传说:据说有位工程师,半夜突然想到一个真空管电路的点子,急需一块木板做原型。在家里翻了一圈,唯一能找到的合适木板——就是他老婆放在面包盒上的切菜板。他拿过来用了,电路做成了,"breadboard"这个名字也就传开了。

后来(1971年),Ronald J. Portugal 发明了现代免焊面包板,获得专利。再后来,专利过期,面包板大规模普及,成为每个电子爱好者的必备工具。名字留了下来,形状也留了下来——现代面包板上面那一排排小孔,排列起来就像切好片的面包……

准备工作:今天需要以下材料

在正式学习面包板之前,先把"武器"准备好。以下物品建议和上一篇中提到的D1mini开发板一次性买齐

物品
数量
说明
面包板
1~2块
推荐400孔标准款
面包板连接线
1捆
公对公(两头都是针)60根左右
LED
10-50个
各种颜色各几个,练习用
电阻包
1包
220Ω、510Ω、1kΩ、5.1K、10kΩ 各一些
轻触按钮
10个
4脚按钮、大小都可以
公对母杜邦线
1排
连接开发板和面包板用
母对母杜邦线
1排
延长导线使用

建议:直接在网上搜"Arduino入门"或"面包板入门元件包",通常30元左右就能买到包含以上所有东西 !往后将是实操性质的了。

一、面包板是什么?

外观认识

面包板是一块白色的、带有很多小孔的塑料板。大概是这样:

关于编号方向:不同型号的面包板,上面的编号大同小异,但是连接逻辑是相通的。

二、面包板内部结构——这里是关键

只有搞懂了面包板的内部结构,我们才能更好的使用它

2.1 最重要的两条规则

规则一:同1列的5个孔,内部是连通的面包板组件区(中间部分)分为上下两块,由中间的凹槽隔开,每块有5行:

关键:1列的5个孔是互相连通的,但列与列之间是断开的

规则二:电源轨(上下两条长条)整条连通面包板最上方和最下方,各有两条长条孔位(通常标红色"+"和蓝色"-"),这就像两条"电源线",方便给多个元件同时供电,但上电源轨和下电源轨是独立的!。

2.2 用一张准确的图彻底搞懂

对照实物

拿出你的面包板,正面朝上,你会看到:

  • • 板子上方有字母 a b c d e 印在塑料外壳上
  • • 中间有一条 凹槽
  • • 凹槽下方有字母 f g h i j
  • • 板子左侧(或右侧)有数字 1 2 3 4 5... 表示列号
  • • 最上方和最下方有红色/蓝色的线条,标示 + 和 - 电源轨

2.3 总结

区域
连通规则
组件区上半
同一列的5个孔互相连通
组件区下半
同一列的5个孔互相连通
上半区 vs 下半区
凹槽隔开,完全断开
电源轨
整条长孔全部连通
上电源轨 vs 下电源轨
断开

三、手把手教你用面包板搭接第一个电路

3.1 实验:用面包板点亮一个LED

目标:把LED和电阻插在面包板上,通过ESP8266供电,让LED亮起来。

步骤一:认识LED的极性

快速判断极性长脚 = 正极(阳极)短脚 = 负极(阴极)

透明塑封LED还可以从内部结构来区分,金属面小的为正,金属面大的为负;外形上看通常是外边有切口的为负,另一边就为正。LED灯在工作时,电流只能从正极流进,从负极流出,反之则不会有电流通过,这就是二极管的单向导电性。

步骤二:在面包板上插入LED和电阻

①LED的长脚和短脚插入不同的列,取一个阻值在220Ω-1K之间的任意电阻,电阻一端接入LED的短脚所在的列,另一端插入其他的一个空列。

②将D1mini开发板跨过面包板中间的凹槽,上下居中插入。正确插入后会在D1mini的上下两个各还有一行孔位。对应Dmini开发板上的标注,用2根面包板连接线(两头针)连接电源轨红线(+)到3V3,电源轨蓝线(-)到GND。

插入位置对照表: LED长脚(正极) → 插入单独一列

LED短脚(负极) → 插入单独一列

电阻的一端 → 插入LED短脚所在列

电阻的另一端 → 插入单独一列

LED是半导体元件,需要区分极性

电阻是无极性元件,无须区分引脚,可随便接入,只需注意选择合适的阻值。

步骤三:连接电源

用连接线连接电源轨红线(+)和LED长脚所在列,再用1根线连接电源轨蓝线(-)和电阻空着的那个脚所在列。检查连接没有问题,通过USB线给D1 mini开发板供电,如果电路连接正确的话,此时会看到LED灯将亮起,实验成功!

此处的D1 mini开发板只是提供电源使用,用3-5V直流电源可以直接代替。

四、动手实战

搭建一个按键控制LED灯的电路示例

要求:按下按键,LED点亮,释放按键,LED熄灭。评论区发出你的实验结果。

购买建议:入门时候,没必要搞上几大白的套装。接下来我们用到的硬件基本都能在50元内搞定,切记一步到位。

面包板是电子制作的"草稿纸",不用焊接、随意修改,是每个电子爱好者的必备工具。有问题欢迎留言,下期见~

第 4 篇

Arduino玩转ESP8266 入门系列-GPIO数字输出

从点亮第一个LED开始

ESP8266入门系列难度:入门预计阅读时间:8分钟动手实践时间:30分钟

前言

在前面的文章中,我们已经完成了ESP8266开发环境的搭建,简单认识了D1 mini开发板,了解了面包板等简单的硬件知识。现在,终于要开始进入硬件编程了——让代码控制真实的物理世界

这一篇,我们就做一件最基础、也最重要的事:让ESP8266输出信号去点亮一个LED灯。

别小看这件事,它是所有硬件+软件项目的基石。智能家居里的灯光控制、门磁传感器、温湿度检测……本质上都是GPIO的输入输出操作。

一、GPIO是什么?——ESP8266的"手脚"

1.1 什么是GPIO?

GPIO(General Purpose Input/Output,通用输入输出)是微控制器与外部世界交互的桥梁。

打个比方:ESP8266芯片就像一个被困在开发板里的大脑,它很聪明,但无法直接触碰外界。GPIO引脚就是它伸向外界的"手"和"感官":

  • 输出模式(Output):像"手"一样,向物理世界发出指令——点亮LED、驱动蜂鸣器、控制继电器……
  • 输入模式(Input):像"感官"一样,感知物理世界状态——读取按钮、检测传感器、接收信号……

每一个GPIO引脚都可以在程序中灵活配置为输入或输出模式,这也是它叫"通用"的原因。

1.2 D1 mini的GPIO引脚一览

D1 mini是个人比较喜欢的ESP8266开发板之一,体积小巧、价格便宜、引脚够用。但它的引脚标识方式有点"绕",需要特别注意。D1 mini的引脚标号和GPIO编号是两套不同的体系

引脚标识(板上丝印)
GPIO编号
常用功能
注意事项
D0
GPIO16
数字IO
不支持PWM,唤醒专用
D1
GPIO5
数字IO
✅ 稳定,推荐使用
D2
GPIO4
数字IO
✅ 稳定,推荐使用
D3
GPIO0
数字IO
启动时有特殊作用,慎用
D4
GPIO2
数字IO
✅ 板载LED,调试神器
D5
GPIO14
数字IO
✅ 稳定
D6
GPIO12
数字IO
✅ 稳定
D7
GPIO13
数字IO
✅ 稳定
D8
GPIO15
数字IO
启动时有特殊作用,慎用
A0
ADC0
模拟输入
读取0~3.3V电压
RX
GPIO3
串口接收
下载/调试用
TX
GPIO1
串口发送
下载/调试用

重要提示:在Arduino程序中,你可以使用两种编号方式:①使用 D1D2 这样的引脚标识(推荐,直观)②使用 54 这样的GPIO编号。

例如 digitalWrite(D1, HIGH)digitalWrite(5, HIGH) 是完全等价的。

1.3 板载LED——零门槛的起点

D1 mini的D4引脚(GPIO2)上连接了一个蓝色LED,这就是传说中的"板载LED"。这意味着:你不需要接任何电路,就能完成第一个硬件实验——只需一块D1 mini和一根USB线!说起连接硬件电路,上一篇的按键电路作业完成了没?

板载LED的连接方式:

ESP8266 GPIO2 (D4)
        │
       电阻(约220Ω,板载已集成)
        │
       LED(蓝色)
        │
       GND

思考:这颗板载LED是"低电平点亮"还是"高电平点亮"?

答案是:高电平点亮。当你向GPIO2输出HIGH(3.3V)时,LED亮起;输出LOW(0V)时,LED熄灭。有些其他主控的开发板板载LED是低电平点亮,D1 mini是高电平点亮,编程时请注意区分。

二、数字输出——点亮LED

2.1 数字输出的本质

数字输出只有两种状态:

状态
电平
电压(ESP8266)
含义
HIGH
高电平
3.3V
"开"
LOW
低电平
0V
"关"

这就像墙壁开关,只有"开"和"关"两种状态,没有中间值。

2.2 第一个程序:让板载LED闪烁

把以下代码复制到Arduino IDE,上传到D1 mini:

// 第一个LED程序:让板载LED闪烁
// 板载LED连接在 D4(GPIO2)

#define LED_PIN D4   // 使用引脚标识,直观易懂

void setup() {
    // 初始化串口,方便调试
    Serial.begin(115200);
    Serial.println("ESP8266 GPIO实验开始!");
    
    // 关键步骤:设置引脚为输出模式
    pinMode(LED_PIN, OUTPUT);
    // 初始状态:熄灭
    digitalWrite(LED_PIN, LOW);
}

void loop() {
    Serial.println("LED → 亮");
    digitalWrite(LED_PIN, HIGH);  // 输出高电平,点亮LED
    delay(1000);                  // 延时1000ms = 1秒
    
    Serial.println("LED → 灭");
    digitalWrite(LED_PIN, LOW);   // 输出低电平,熄灭LED
    delay(1000);
}

小知识:以上代码也可在Arduino IDE中示例程序中找到,文件->示例->01.Basics->Blink.

2.3 代码逐行解析

#define LED_PIN D4

用宏定义把引脚号"命名"为LED_PIN,后续代码中直接用LED_PIN就可直接代替D4,代码可读性更好,修改引脚时也只需改这一处。

pinMode(LED_PIN, OUTPUT);

这是最关键的一行。GPIO引脚默认是输入模式,要输出信号必须先调用pinMode()设置为OUTPUT。忘记这步是最常见的新手错误。

digitalWrite(LED_PIN, HIGH);

向引脚输出高电平(3.3V)。

delay(1000);

让程序暂停1000毫秒。

注意:delay()会阻塞程序运行,在这段时间内ESP8266什么也做不了,这叫阻塞式编程。"非阻塞"编程前面文章也讲到,后面还会继续讲。

2.4 上传与观察

  1. 1. 用USB线连接D1 mini到电脑
  2. 2. 在Arduino IDE中选择正确的开发板和端口
  3. 3. 点击"上传"按钮,“→”图标
  4. 4. 上传完成后,观察D1 mini板上的蓝色LED——它应该每秒闪烁一次
  5. 5. 打开"工具 → 串口监视器",设置波特率为115200,可以看到打印的调试信息。

成功标志:蓝色LED规律闪烁,串口监视器每秒输出"LED → 亮"和"LED → 灭"。小测试:此时,你可以尝试修改程序中第17行或者是21行delay中的值,比如将1000改为200或者是500,再次上传程序观察LED闪烁的变化。

三、控制外部LED

板载LED虽然方便,但真正的项目里,你需要控制外部的LED、继电器、电机等设备。

3.1 为什么要串联电阻?

LED是一种二极管,电流过大就会烧毁。ESP8266的GPIO引脚最大输出电流约12mA,虽然不足以直接烧毁LED,但为了保护GPIO引脚和LED,必须串联一个限流电阻。实际使用中,选用220Ω~1KΩ的电阻即可,不需要精确计算。

3.2 外部LED连接电路

ESP8266 D1 (GPIO5)
        │
       220Ω电阻
        │
       LED(长脚/正极)
        │
       LED(短脚/负极)
        │
       GND

LED极性判断

  • • 长脚 = 正极(阳极),接电阻一侧
  • • 短脚 = 负极(阴极),接GND

3.3 外部LED闪烁代码

// 外部LED闪烁实验
// 连接:D1 → 220Ω电阻 → LED正极,LED负极 → GND

#define LED_PIN D1   // 使用D1引脚(GPIO5)

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    Serial.println("外部LED实验开始");
}

void loop() {
    digitalWrite(LED_PIN, HIGH);
    delay(500);
    digitalWrite(LED_PIN, LOW);
    delay(500);
}

上传后,外接的LED应该每0.5秒闪烁一次。

第 5 篇

Arduino玩转ESP8266入门系列-GPIO数字输入

从读取按键开始

ESP8266入门系列难度:入门预计阅读时间:5分钟动手实践时间:20分钟

前言

在上期文章中,我们学会了如何让ESP8266输出信号,点亮LED灯。现在我们接着学习另一个重要技能——让ESP8266读取外界信号

这一篇,我们要做两件重要的事:

  1. 1. 让ESP8266读取按键状态——实现最简单的人机交互
  2. 2. 了解按键抖动问题——一个硬件+软件项目都会遇到的经典问题

别小看这两件事,它们是所有交互式硬件项目的基础。智能门铃、密码锁、游戏手柄……本质上都是GPIO的输入操作。

一、数字输入——读取按键状态

1.1 按键的工作原理

按键本质上是一个"通断开关":未按下:电路断开按下:电路导通

1.2 认识按键:4引脚轻触开关

在开始接线之前,我们先来认识一下这个小小的电子元件。

常用的轻触按键开关有4个引脚(也有部分封装只有2个引脚)。4个引脚的按键在使用时只需要用到2个引脚即可,因为它的内部结构是4引脚两两相连的。

内部连接规则当按键没有被按下时:引脚1与4相通引脚2与3相通当按键被按下时:中间的金属片将4个引脚全部连在一起

仔细观察会发现一个规律:1号和2号引脚在按键的同一侧,但它们之间没有直接相连;1号和4号引脚在按键的不同侧,但按键内部是连通的。

记住口诀:同侧不相连,相连不同侧。

如何选择引脚?

在实际使用中,你只需要连接任选同侧的两个引脚即可,比如选 1和2,或者 4和3,效果完全一样。

1.3 上拉电阻与下拉电阻

这里有一个关键概念:GPIO引脚在输入模式下,如果没有明确接高电平或低电平,它的状态是"浮空"的,会受外界电磁干扰随机变化。最简单的方式就是通过一个电阻,把GPIO接到一个确定的电压上,比如3.3V或者是0V(GND)。电阻接到3.3V就叫上拉电阻,在这种情况下,按键没按下时,GPIO是确定的3.3V,按键按下时,是0V(GND);电阻接到0V(GND)就叫下拉电阻,在这种情况下,按键没按下时,GPIO是确定的0V(GND),按键按下时,是3.3V。

好消息:ESP8266内置了上拉电阻!只需在pinMode()时使用INPUT_PULLUP模式,无需外接电阻。当然,你想尝试自己外接电阻来实现上拉,或者下拉也是完全可以的。

1.4 按键电路连接

采用上拉电阻方式(最常用):

       3.3V(内部上拉)
           │
GPIO4 ────┤(内部上拉电阻,已启用)
(D2)       │
           │
           按键
           │
          GND

外接下拉电阻连接方式(实物)

  • • 按键的一个引脚 → D2(GPIO4)
  • • 上面同一个按键引脚外接一个10K电阻,电阻另一个引脚接GND
  • • 按键的另一个引脚 → 3.3V

这种接法下:按键按下 = HIGH,按键释放 = LOW。但是更常用的方式还是采用上拉电阻连接方式。

1.5 读取按键状态的代码

// 读取按键状态实验(内部上拉)
// 连接:按键一端 → D2,另一端 → GND 

#define BUTTON_PIN D2
#define LED_PIN D4

void setup(){
    Serial.begin(115200);
    
    // 设置按键引脚为输入模式,启用内部上拉电阻
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    
    // 设置LED引脚为输出模式
    pinMode(LED_PIN, OUTPUT);
    
    Serial.println("按键读取实验开始");
    Serial.println("按下按键观察串口输出");
}

void loop(){
    // 读取按键状态
    int buttonState = digitalRead(BUTTON_PIN);
    
    // 上拉模式:按下=LOW,释放=HIGH
    if (buttonState == LOW) {
        Serial.println("按键被按下!");
        digitalWrite(LED_PIN, HIGH);  // 按下时点灯
    } else {
        Serial.println("按键已释放");
        digitalWrite(LED_PIN, LOW);   // 释放时灭灯
    }
    
    delay(100);  // 稍微延时,减少串口输出频率
}

1.6 代码解析

pinMode(BUTTON_PIN, INPUT_PULLUP);

设置为输入模式,并启用ESP8266内部的上拉电阻。这样,按键释放时GPIO被上拉到高电平(HIGH),按下时接地变为低电平(LOW)。

int buttonState = digitalRead(BUTTON_PIN);

读取引脚的当前电平状态,返回HIGHLOW

注意:D1 mini去读取引脚上的状态,会读取到一个信息(比如我们读书一样,会获得知识),这个信息需要存储(比如我们的记忆知识一样),所以在这里 digitalRead()得到的结果需要用一个变量来存储。

二、按键好像不太灵

把上面的代码上传运行,然后反复按按键,你可能会发现一些奇怪的现象:

  1. 1. 按一下,串口输出好几条"按键被按下"
  2. 2. 有时候按了没反应,要多按几次
  3. 3. 释放按键后,串口一直输出

这不是你的代码写错了,也不是按键坏了,这是一个所有硬件+软件初学时都会遇到的经典问题:按键抖动。

什么是按键抖动?

机械按键内部是两个金属触点。当你按下按键时,这两个触点并不是立刻和中间的金属片稳定接触的——它们会快速弹跳几次,才最终稳定接触。从ESP8266的视角看,一次"按下"操作,实际上产生了几十次快速的电平跳变

这段抖动时间大约持续10~20毫秒。在这期间,digitalRead()会读到一系列不稳定的高低电平变化,导致程序"以为"按键被按了很多次。既然我们找到了问题所在,那如何解决呢?具体如何操作,怎么修改程序,我们下一期揭晓答案。

第 6 篇

Arduino玩转ESP8266 入门系列-按键消抖

让你的按键听话

系列:入门系列难度:进阶预计阅读时间:10分钟动手实践时间:40分钟

前言

通过前面的学习,我们已经搞定了GPIO的数字输入和输出功能。但上一篇中我们已经发现了问题:按键时灵时不灵。这不是你的错,也不是按键坏了——这是机械按键的"天生缺陷":按键抖动今天我们就彻底解决这个问题,并介绍三种消抖方法,从简单到专业,总有一种适合你。

一、按键抖动

1.1 机械按键的内部结构

机械按键内部有两个金属触点和一个金属弹片。当你按下按键时,这两个触点和金属片之间并不是"啪"地一下立刻稳定接触的,它由于机械弹性和惯性,它们会快速弹跳几次,才最终稳定接触。就像把一个球扔到地上,它会弹几下才停下来。

抖动持续时间:大约在10~20毫秒之间,具体取决于按键的质量。

1.2 抖动带来的问题

ESP8266运行速度飞快,loop()每秒可以执行几万次。在这10~20ms的抖动期间,digitalRead()会读到很多次不稳定的高低电平变化。后果是什么?

// 假设你想用按键"每按一次,LED状态翻转一次"
if (buttonState == LOW) {
    ledState = !ledState;  // 翻转LED状态
}

一次按下操作,由于抖动,这段代码的if判断可能触发了几十次ledState被翻转了几十次……最终LED的状态是随机的,完全不可预期。

二、简单延时消抖

2.1 原理

思路很简单:检测到按键按下后,延时一小段时间(比如50ms),等抖动过去了再读取一次,如果还是按下的状态,才认为是"真的按下"就像有人敲门,你等一下仔细听收否还有第二次敲门声再开门。

2.2 代码实现

// 方法一:delay()延时消抖
// 最简单的消抖方法,适合初学者

#define BUTTON_PIN D2
#define LED_PIN D4

int ledState = LOW;

void setup() {
    Serial.begin(115200);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, ledState);
    Serial.println("delay()延时消抖");
}
void loop() {
    // 读取按键状态(上拉模式:按下=LOW)
    if (digitalRead(BUTTON_PIN) == LOW) {
        // 检测到按下,先延时50ms,等抖动过去
        delay(50);
        
        // 再读一次,如果还是LOW,说明是真的按下了
        if (digitalRead(BUTTON_PIN) == LOW) {
            ledState = !ledState;           // 翻转LED状态
             //开关灯徐根据LED电路的驱动电平确定(此处为高电平点亮)
            digitalWrite(LED_PIN, ledState);
            Serial.println(ledState ? "LED 开" : "LED 关");
         
        }
    }
}

2.3 优缺点分析

优点
缺点
代码简单,容易理解
delay()
会阻塞程序运行
消抖效果还可以
效果不好可以适当延长delay()来修复
适合简单项目
不适合需要同时做多件事的项目

注意:这个方法里有delay(50),在这期间ESP8266完全无法做其他事情,并且按键的可靠性会因不同用户而随机,可靠性不稳定。如果你的项目只需要控制一个按键,该方法基本满足需求;但如果需要同时处理WiFi、传感器等任务,这个方法就不合适了。

三、等待按键释放(边沿检测)

3.1 原理

这个方法的核心思想是:在按键被按下后,等待按键释放,释放后再处理按键信息,防止一次按下被重复识别。这个方法比方法一更"彻底",但同样是阻塞式的。

3.2 代码实现

// 方法二:等待按键释放
// 在每次确认按键有效后,等待按键释放再继续

#define BUTTON_PIN D2
#define LED_PIN D4

int ledState = LOW;
int lastButtonState = HIGH;  // 上一次按键状态(上拉:释放=HIGH)

void setup(){
    Serial.begin(115200);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, ledState);
    Serial.println("等待按键释放");
}

void loop(){

    int ButtonState = digitalRead(BUTTON_PIN);
    // 检测按键"按下事件"(从高变低,即下降沿)
    if (lastButtonState == HIGH && ButtonState == LOW) {
        delay(50);  // 延时消抖
        
        // 确认按键确实被按下了
        if (digitalRead(BUTTON_PIN) == LOW) {
            // 处理按键按下事件
            ledState = !ledState;
            //开关灯徐根据LED电路的驱动电平确定(此处为高电平点亮)
            digitalWrite(LED_PIN, ledState);
            Serial.print("按键按下,LED → ");
            Serial.println(ledState ? "开" : "关");
            
            // 等待按键释放
            while (digitalRead(BUTTON_PIN) == LOW) {
                delay(10);  // 稍微延时,减轻CPU负担
            }
            delay(50);  // 释放抖动消抖
            Serial.println("按键已释放,可以继续按了");
        }
    }
    //更新上一次按键的状态
    lastButtonState = ButtonState;
}

3.3 关键概念:边沿检测

上面的代码引入了一个重要概念:边沿检测

  • 下降沿:电平从高变低(HIGH→LOW),对应按键"刚按下"的瞬间
  • 上升沿:电平从低变高(LOW→HIGH),对应按键"刚释放"的瞬间

只检测"电平状态"是不够的(因为抖动期间电平会反复变化),检测"边沿"才能准确捕捉按键动作。

// 下降沿检测模板(记住这个模板!)
if (lastState == HIGH && currentState == LOW) {
    // 按键刚被按下(下降沿)
}

3.4 优缺点分析

优点
缺点
比方法一更可靠
仍然是阻塞式的
能准确检测"按下事件"
等待释放期间无法做其他事
适合简单项目
不适合复杂项目

四、状态机消抖

4.1 为什么需要状态机?

前面两种方法都有一个共同问题:使用了delay()while循环,会阻塞程序运行

在真实的物联网项目中,ESP8266往往需要同时做多件事:

  • • 监听按键输入
  • • 维持WiFi连接
  • • 处理网络数据
  • • 控制多个输出设备

如果按键处理代码阻塞了主循环,WiFi可能断开、数据可能丢失……这显然不可接受。所以我们需要一种"非阻塞"的消抖方法这就是状态机消抖。

4.2 状态机的核心思想

状态机是一种编程思想:把系统的行为分解为若干个"状态",根据当前状态和输入条件,决定下一个状态和动作

对于按键消抖,我们可以定义以下几个状态:

状态0:等待按下
       ↓ 检测到低电平,启动定时器
状态1:消抖中
       ↓ 50ms后再次检测,如果还是低电平
状态2:确认按下
       ↓ 触发一次"按下事件"
状态3:等待释放
       ↓ 检测到高电平
       → 回到状态0

关键在于:状态转换不依赖delay(),而是用millis()函数记录时间,在每次loop()循环中检查是否到达了状态转换条件

4.3 代码实现(完整版)

// 状态机消抖(非阻塞,推荐)
// 使用millis()进行时间管理,不阻塞主循环

#define BUTTON_PIN D2
#define LED_PIN D4

int ledState = LOW;

// 状态机相关变量
int buttonState = HIGH;        // 当前稳定的按键状态
int lastReading = HIGH;        // 上一次的读取值(用于检测变化)
int reading;                   // 当前原始读取值

unsigned long lastDebounceTime = 0;   // 上次读取值变化的时间
const unsigned long debounceDelay = 50;  // 消抖时间(ms)

// 边沿检测变量
bool buttonJustPressed = false;   // 按键刚被按下的标志
bool buttonJustReleased = false;  // 按键刚被释放的标志

void setup(){
    Serial.begin(115200);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, ledState);
    Serial.println("状态机消抖(非阻塞)");
}

void loop(){
    // ===== 按键消抖处理(非阻塞)=====
    reading = digitalRead(BUTTON_PIN);

    // 当本次读取值与上次读取值不同时,说明有变化(抖动或真实按压),重置消抖定时器
    if (reading != lastReading) {
        lastDebounceTime = millis();
    }

    // 如果距离上次读取值变化已经超过消抖时间
    // 说明抖动已经结束,当前 reading 是稳定状态,可以接受
    if ((millis() - lastDebounceTime) > debounceDelay) {
        // 稳定状态确实与之前的 buttonState 不同,说明发生了真实的状态变化
        if (reading != buttonState) {
            // 先保存旧状态,再更新(用于边沿检测)
            int oldState = buttonState;
            buttonState = reading;

            // 检测边沿
            if (oldState == HIGH && buttonState == LOW ) {
                // 下降沿:按键刚被按下
                buttonJustPressed = true;
            } else if (oldState == LOW && buttonState == HIGH) {
                // 上升沿:按键刚被释放
                buttonJustReleased = true;
            }
        }
    }

    lastReading = reading;  // 保存本次读取值,供下次循环比较

    // ===== 按键事件处理 =====
    //按键按下开始
    if (buttonJustPressed) {
        buttonJustPressed = false;  // 清除标志

        // 在这里处理"按键按下"事件
        ledState = !ledState;
        digitalWrite(LED_PIN, ledState);
        Serial.print("按键按下(状态机消抖),LED → ");
        Serial.println(ledState ? "开" : "关");
    }
    //按键释放
    if (buttonJustReleased) {
        buttonJustReleased = false;  // 清除标志
        Serial.println("按键释放");
    }

    // ===== 其他任务(模拟)=====
    // 在这里可以添加其他需要持续执行的任务
    // 比如:WiFi处理、传感器读取、网络通信等
    // 因为消抖是非阻塞的,这些任务不会被耽误

    // 示例:每500ms打印一次"系统运行中"
    static unsigned long lastPrintTime = 0;
    if (millis() - lastPrintTime > 500) {
        lastPrintTime = millis();
        Serial.println("系统运行中,同时监听按键...");
    }
}

4.4 核心技巧:millis()时间管理

状态机消抖的核心是用millis()代替delay()

// 阻塞方式
delay(50);  // 程序停在这里50ms,什么也做不了

// 非阻塞方式
if (millis() - lastTime > 50) {
    // 50ms时间到了,执行相应代码
    // 在这50ms期间,loop()可以继续执行其他任务
}

millis()返回系统启动以来的毫秒数,是一个不断递增的数。通过记录"上次时间"并与"当前时间"比较,就能实现精确的时间控制,而不阻塞主程序的运行,只有在比较"上次时间"和"当前时间"时才会占用CPU算力资源。

4.5 优缺点分析

优点
缺点
完全非阻塞
代码相对复杂
可以同时处理多任务
需要理解状态机概念
消抖效果最好
需要管理更多变量
专业项目的标准做法
理解难度和实操难度大

五、三种方法对比总结

方法
难度
阻塞性
适用场景
delay()延时
入门
阻塞
超简单项目
等待释放
进阶
阻塞
简单项目,按键不多
状态机消抖
终极
非阻塞
正式项目,推荐用法

建议:初学阶段可以用方法一或方法二,简单直观;但一旦项目变复杂或者是按键处理效果不理想时,可以尝试切换到方法三。早点习惯状态机思维,对后续学习非常有帮助。

六、本期挑战:简易计数器

理论知识学完了,来做点实际的!

挑战题目:按键计数器

功能要求

  1. 1. 使用一个按键和一个LED
  2. 2. 每次按下按键,计数器加1
  3. 3. 通过LED闪烁次数显示当前计数值
    • • 例如:计数为3时,LED闪烁3次(亮200ms → 灭200ms,重复3次)
  4. 4. 计数到10后自动重置为0
  5. 5. 将计数值通过串口输出到电脑

硬件连接

  • • 按键 → D2(GPIO4),另一端 → GND
  • • LED → D4(GPIO2,板载LED)

参考框架(你需要补全代码)

#define BUTTON_PIN D2
#define LED_PIN D4

int count = 0;  // 计数器

// 状态机消抖变量(自行补充)

void setup() {
    Serial.begin(115200);
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    Serial.println("===== 按键计数器挑战 =====");
    Serial.println("每次按下按键,计数+1");
}

void loop() {
    // 1. 状态机消抖,检测按键按下(自行实现)
    
    // 2. 如果按键被按下:
    //    - count++
    //    - 串口输出当前count
    //    - 如果count >= 10,重置为0
    //    - 调用blinkLED()函数,让LED闪烁count次
    
    // 3. 其他任务...
}

// LED闪烁函数:闪烁times次
void blinkLED(int times) {
    // 自行实现:亮200ms → 灭200ms,重复times次
    // 注意:这里也要用非阻塞方式!
}

提示:尝试用非阻塞方式实现blinkLED()函数(提示:在loop()中用状态机控制闪烁)。

消抖是GPIO编程的基本功,掌握状态机思维,你的Arduino之路会顺畅很多!

第 7 篇

Arduino玩转ESP8266 入门系列-模拟输出

让LED"骗过"你的眼睛

系列:入门系列难度:入门预计阅读时间:8分钟动手实践时间:25分钟

前言

前两篇我们一直在和数字信号打交道——HIGH或LOW,开或关,非0即1。但现实世界不是非黑即白的。光照强度、温度高低、声音大小、电压强弱……这些都是连续变化的量,无法用简单的"开/关"来描述,我们的物理世界就是这么的精彩。

要让ESP8266"模拟"出连续变化的量(比如让LED呈现不同的亮度,再比如让ESP8266知道当前的光线亮度是多少),就需要用到ESP8266的模拟功能。本篇专门讲解模拟输出(PWM),模拟输入(ADC)将在后面介绍。

一、欺骗眼睛的艺术

1.1 什么是PWM?

PWM(Pulse Width Modulation,脉冲宽度调制)是一种用数字信号来"模拟"模拟信号的技术。原理很简单:快速切换开关状态(1/0),通过改变"开"的时间占比,来控制平均输出电压

  • 占空比(Duty Cycle):一个周期内"高电平"时间所占的比例
  • 频率:PWM信号每秒重复的次数(ESP8266的analogWriteFreq()可设置,默认约1kHz)

1.2 人眼的"视觉暂留"

PWM之所以能"骗过"我们,让我们看到LED的不同亮度,是因为人眼有视觉暂留效应。当LED快速闪烁时(超过50Hz),人眼会把它"平均"成一种亮度,而不是看到闪烁。这就是为什么PWM可以用数字输出来"模拟"模拟输出的效果。

1.3 analogWrite()函数详解

analogWrite(pin, value);
  • pin:支持PWM的GPIO引脚(推荐用D1、D2、D5、D6、D7、D8)
  • value:PWM占空比值,范围0~255(8位分辨率)
    • 0 = 完全熄灭(占空比0%)等同于digitalWrite(pin,LOW)
    • 128 = 约半亮(占空比50%)
    • 255 = 最亮(占空比100%)等同于digitalWrite(pin,HIGH)

1.4 外部LED电路

在前面的文章中我们用过外部LED这里不再过多介绍,但这里要用LED的规范接法才能达到更好的实验效果。

标准外部LED连接电路

ESP8266 D1 (GPIO5)
        │
       220Ω限流电阻
        │
       【LED长脚/正极(阳极)】
        │
       【LED短脚/负极(阴极)】
        │
       GND

标准电路图如下所示:

实物连接步骤
  1. 1. 取一个LED,识别长脚(正极)和短脚(负极)
  2. 2. 长脚接220Ω电阻的一端
  3. 3. 电阻另一端接D1(GPIO5)
  4. 4. LED短脚接GND
  5. 5. 可以接多个LED,每个LED串联一个220Ω电阻,分别接到不同GPIO

为什么必须串联电阻?LED的正向压降约1.8~3.0V(取决于颜色),如果直接接到3.3V,电流会过大,可能烧毁LED或ESP8266的GPIO引脚。220Ω电阻将电流限制在约10mA,安全又明亮,也更好的实现后续的实验。

1.5 实验一:呼吸灯效果

这是PWM最经典的应用——让LED像呼吸一样,慢慢变亮再慢慢变暗。

// 实验一:呼吸灯效果
// 连接:D1 → 220Ω电阻 → LED正极,LED负极 → GND

#define LED_PIN D1  // 使用D1引脚输出PWM

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    Serial.println("呼吸灯实验开始");
    Serial.println("观察LED缓慢变亮、变暗的效果");
}
void loop() {
    // 逐渐变亮:从0到255
    Serial.println("逐渐变亮...");
    for (int brightness = 0; brightness <= 255; brightness++) {
        analogWrite(LED_PIN, brightness);
        delay(10);  // 每级亮度停留10ms,总时长约2.5秒完成渐变
    }
  
    delay(500);  // 最亮状态停留500ms
    
    // 逐渐变暗:从255到0
    Serial.println("逐渐变暗...");
    for (int brightness = 255; brightness >= 0; brightness--) {
        analogWrite(LED_PIN, brightness);
        delay(10);
    }
    delay(500);  // 熄灭状态停留500ms
}

代码解析

for (int brightness = 0; brightness <= 255; brightness++)

for语句,它会从 0 开始,将 brightness 逐次加 1,直到超过 255 为止,循环执行其内部的代码。内部代码analogWrite()会自动把0-255映射为0%-100%的占空比,从而调节D1引脚上的电压从0V变化到3.3V。

delay(10);

每级亮度停留10ms,256级 × 10ms = 2560ms ≈ 2.5秒完成一次渐变,效果很柔和。

1.6 实验二:用按键控制LED亮度

接下来我们把前面的知识接入,加入按键调节。完成一个用按键调节亮度的实验。本实验采用简单的延时消抖 + 按键释放检测方式,代码更直观易懂。按键电路连接参照前面文章。

// 实验二:按键调节LED亮度(
// 每按一次按键,亮度增加一级,5级循环

#define BUTTON_PIN D2
#define LED_PIN D1

int brightnessLevels[] = {0, 64, 128, 192, 255};  // 5级亮度
int currentLevel = 0;

void setup(){
    Serial.begin(115200);
    pinMode(BUTTON_PIN, INPUT_PULLUP);  // 按键接D2,使用内部上拉电阻
    pinMode(LED_PIN, OUTPUT);
    analogWrite(LED_PIN, brightnessLevels[currentLevel]);
    Serial.println("===== 按键调光实验 =====");
    Serial.println("每按一次按键,LED亮度变化一级");
    Serial.println("0 → 64 → 128 → 192 → 255 → 0 ...");
    Serial.println("=======================");
}

void loop(){
    // 检测按键按下(LOW表示按下,因为使用了INPUT_PULLUP)
    if (digitalRead(BUTTON_PIN) == LOW) {
        // 延时消抖:等待20ms,避开机械抖动的时段
        delay(20);
        
        // 再次确认按键仍然是按下的状态
        if (digitalRead(BUTTON_PIN) == LOW) {
            // 切换亮度级别
            if(currentLevel<4)
            currentLevel = currentLevel + 1;
            else currentLevel = 0;
            analogWrite(LED_PIN, brightnessLevels[currentLevel]);
            
            // 串口输出当前状态
            Serial.print("亮度级:");
            Serial.print(currentLevel);
            Serial.print(",PWM值:");
            Serial.println(brightnessLevels[currentLevel]);
            
            // 等待按键释放
            while (digitalRead(BUTTON_PIN) == LOW) {
                delay(10);  // 等待,直到按钮松开
            }
            
            // 按键释放后再延时消抖
            delay(20);
        }
    }
}

代码解析

if (digitalRead(BUTTON_PIN) == LOW)

使用INPUT_PULLUP模式时,按键未按下GPIO读到HIGH,按下后接地读到LOW。所以检测LOW判断按键按下。

delay(20);
if (digitalRead(BUTTON_PIN) == LOW)

这就是延时消抖的核心:第一次检测到按下后,等待20ms让机械抖动过去,再读一次,如果仍然是LOW,说明是真的按下了。

while (digitalRead(BUTTON_PIN) == LOW) {
    delay(10);
}

这就是按键释放检测:一直等待,直到按键松开(GPIO读回HIGH),这样一次按下只触发一次,不会连续切换多个亮度级别。

本期小结

知识点
关键函数/概念
记住这一点
PWM原理
脉冲宽度调制
快速切换开关,"骗过"人眼
模拟输出
analogWrite(pin, 0~255)
在GPIO引脚上实现,8位分辨率
呼吸灯
for
循环 + analogWrite()
渐变效果的核心

PWM是ESP8266重要的输出功能之一,掌握了它,你可以更优雅的控制我们的物理世界,不再是非0即1的暴力方式。

第 8 篇

Arduino玩转ESP8266入门系列-模拟输入

读取连续变化的物理世界

系列:入门系列难度:入门预计阅读时间:8分钟动手实践时间:30分钟

前言

前面内容我们学习了如何用PWM实现"模拟输出",让LED呈现出不同的亮度。正如我们所处的现实世界一样,大多信号都是连续变化的,比如:光照强度、温度、声音……这些都不是简单的开关量。要让ESP8266"感知"这些连续变化的物理量,就需要用到模拟输入(ADC)。今天我们将深入模拟输入的世界,并用生活中的实用案例把知识融会贯通。

一、ESP8266的ADC规格

ESP8266内置了一个10位ADC(模数转换器),只有一个通道,引出到了开发板的A0引脚。

参数
规格
分辨率
10位(0~1023,共1024个等级)
输入电压范围
0 ~ 3.3V
(D1 mini)
对应的analogRead()返回值
0(0V)~ 1023(3.3V)
精度
约3.3mV/级(3.3V ÷ 1024)

重要警告:A0引脚的最大输入电压是3.3V如果输入超过3.3V,会永久损坏ESP8266的ADC,甚至损坏整个芯片如果需要测量高于3.3V的电压,必须使用电阻分压电路以上信息,我们只需要记住模拟输入引脚在A0,其他信息了解即可。

1.1 analogRead()模拟输入函数

int value = analogRead(A0);
  • • 返回值:0 ~ 1023之间的整数
  • • 对应电压:0V ~ 3.3V
  • • 换算公式:电压(V) = analogRead(A0) × 3.3 / 1023

二、实验一:简易电压表

用A0引脚制作一个简易电压表,通过串口输出电压值。

// 实验一:简易电压表
// 连接:待测电压 → A0引脚(注意:不能超过3.3V!)

void setup(){
    Serial.begin(115200);
    Serial.println("===== 简易电压表 =====");
    Serial.println("将0~3.3V电压接到A0引脚");
    Serial.println("======================");
}

void loop(){
    int adcValue = analogRead(A0);           // 读取ADC值(0~1023)
    float voltage = adcValue * 3.3 / 1023.0; // 换算为电压
    
    Serial.print("ADC值:");
    Serial.print(adcValue);
    Serial.print("  →  电压:");
    Serial.print(voltage, 2);  // 保留2位小数
    Serial.println(" V"); 
    delay(500);
}

测试方法

  • • 用杜邦线把A0接到3.3V引脚 → 串口应显示约1023和3.30V
  • • 用杜邦线把A0接到GND → 串口应显示约0和0.00V
  • • 还可通过两个电阻串联接入3.3V和GND之间,测量两个电阻之间 → 串口应显示一个其他值(根据两个电阻值而定)

三、实验二:光敏电阻控制LED

光敏电阻是一种特殊的电阻,它的阻值会随光照强度变化——光照越强,阻值越低。利用这个特性,我们可以制作一个"天黑自动开灯"的装置。

电路连接

3.3V
  │
 【光敏电阻】
  │
  ├──── A0(模拟输入)
  │
 【10kΩ电阻(下拉电阻)】
  │
 GND

同时,LED连接在D1引脚(PWM输出):

D1 ── 220Ω ── LED ── GND

完整代码

// 实验二:光敏电阻控制LED
// 功能:环境光暗时自动点亮LED,亮时自动熄灭
// 同时LED亮度随光照强度连续变化(PWM调光)

#define LDR_PIN A0     // 光敏电阻连接到A0
#define LED_PIN D1     // LED连接到D1(PWM输出)

void setup(){
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    Serial.println("===== 光敏电阻控制LED =====");
    Serial.println("同时LED亮度随光照连续变化");
}

void loop(){
    int lightValue = analogRead(LDR_PIN);  // 读取光照值(0~1023)
    
    // 把光照值映射为LED亮度(光照越暗,LED越亮)
    // 注意:光敏电阻的特性可能相反,需要根据实际接线调整
    int ledBrightness = map(lightValue, 0, 1023, 255, 0);
    ledBrightness = constrain(ledBrightness, 0, 255);  // 限制在0~255
    
    analogWrite(LED_PIN, ledBrightness);  // PWM输出
    
    // 串口输出调试信息
    Serial.print("光照ADC值:");
    Serial.print(lightValue);
    Serial.print("  LED亮度(PWM):");
    Serial.println(ledBrightness);  
    delay(100);
}

3.1 map()函数详解

int ledBrightness = map(lightValue, 0, 1023, 255, 0);

map()是Arduino的神器函数,用来把一个数从一个范围"映射"到另一个范围:

map(值, 原范围最小值, 原范围最大值, 目标范围最小值, 目标范围最大值)

上面这行代码的意思是:把lightValue(范围0-1023)映射为`ledBrightness`(范围255 - 0)。注意这里是反向映射(光照越暗,LED越亮)。

3.2 光敏电阻电路说明

上面的电路是一个电阻分压电路

3.3V
  │
 【光敏电阻 R_ldr】
  │
  ├──── A0
  │
 【固定电阻 R_fixed = 10kΩ】
  │
 GND

A0的电压为:

V_A0 = 3.3V × R_fixed / (R_ldr + R_fixed)
  • • 光照强 → R_ldr小 → V_A0大 → analogRead()值大
  • • 光照弱 → R_ldr大 → V_A0小 → analogRead()值小

实验现象

  1. 1. 上电后,LED亮度会根据当前环境光自动调整
  2. 2. 用手遮盖光敏电阻(模拟变暗)→ LED变亮
  3. 3. 移开手(环境光变强)→ LED变暗
  4. 4. 串口监视器可以看到光照值和亮度值的实时变化

本期小结

知识点
关键函数/概念
记住这一点
模拟输入(ADC)
analogRead(A0)
只在A0引脚,10位分辨率(0~1023)
A0电压范围
0~3.3V
,超过会烧芯片!
电压换算
voltage = adc * 3.3 / 1023
记住这个公式
数值映射
map()
Arduino神器函数
光敏电阻
光照强→阻值低
配合分压电路使用

到这我们已经学完了Arduino开发ESP8266的四个主要的GPIO功能。尽管我们做的所有实验和真实世界的实际场景还在相差甚远,但是我们知道了Arduino的核心四大法,后续再加努力,相信在不远的某天,我们将会制作出属于我们自己的第一个创意作品。

第 9 篇

Arduino玩转ESP8266入门系列-GPIO总结

GPIO编程核心要点总结

系列:入门系列难度:总结预计阅读时间:12分钟动手实践时间:0分钟

前言

从入门系列的开始,我们花了很大的力气来系统学习了ESP8266 GPIO编程的方方面面:

  • • GPIO数字输入输出基础,点亮LED,读取按钮
  • • 按键消抖的三种方法,从简单到专业
  • • 模拟输入输出,PWM呼吸灯,光敏电阻应用

今天,是GPIO的收官篇

一、GPIO编程核心要点系统梳理

1.1 引脚模式设置

所有GPIO操作的第一步,都是用pinMode()设置引脚模式:

pinMode(pin, mode);
模式
含义
典型用途
OUTPUT
数字输出
控制LED、继电器、蜂鸣器
INPUT
数字输入(浮空)
需要外接上拉/下拉电阻
INPUT_PULLUP
数字输入(启用内部上拉)
按钮输入(最常用)
INPUT_PULLDOWN
数字输入(启用内部下拉)
ESP8266部分引脚支持

忘记pinMode()是最常见的错误!如果你发现引脚没反应,第一件事就是检查有没有写pinMode(pin, OUTPUT/INPUT)

1.2 数字输出

digitalWrite(pin, HIGH);  // 输出高电平(3.3V)
digitalWrite(pin, LOW);   // 输出低电平(0V)

要点总结

要点
说明
限流电阻
驱动LED必须串联220Ω电阻
最大电流
每个GPIO约15mA,所有GPIO总和不超过200mA
不能直接驱动大功率设备
继电器、电机需要三极管或MOSFET驱动
上电默认状态
GPIO默认高阻态,外接上拉电阻可确保确定电平

1.3 数字输入

int state = digitalRead(pin);  // 读取引脚电平,返回HIGH或LOW

要点总结

要点
说明
上拉电阻模式
INPUT_PULLUP
:按钮接GND,按下=LOW
下拉电阻模式
INPUT
+ 外接10kΩ电阻到GND:按钮接3.3V,按下=HIGH
按键消抖
三种方法:delay、等待释放、状态机
边沿检测
if (last==HIGH && now==LOW)
检测按下事件

1.4 模拟输出(PWM)

analogWrite(pin, value);  // value: 0~255

要点总结

要点
说明
分辨率
8位(0~255,共256级)
推荐引脚
D1、D2、D5、D6、D7、D8
PWM频率
默认约1kHz,可用analogWriteFreq(freq)修改
原理
快速切换HIGH/LOW,利用视觉暂留产生调光效果

1.5 模拟输入(ADC)

int value = analogRead(A0);  // 返回值:0~1023
float voltage = value * 3.3 / 1023.0;

要点总结

要点
说明
引脚
只有A0一个引脚
支持模拟输入
分辨率
10位(0~1023,共1024级)
电压范围
0~3.3V
,超压会烧芯片!
精度
约3.3mV/级
分压电路
测量高于3.3V的电压必须用电阻分压

1.6 一个重要的概念

这是初学者最容易混淆的地方:

ESP8266的模拟输出和模拟输入是在完全不同的引脚上实现的,它们是两种完全不同的硬件功能!

功能
引脚
原理
分辨率
模拟输出(PWM)
所有支持PWM的GPIO(D1~D8中大部分)
脉冲宽度调制
8位(0~255)
模拟输入(ADC)
只有A0一个引脚
模数转换
10位(0~1023)

为什么是这样?

  • 模拟输出(PWM):本质上还是数字输出,通过快速切换HIGH/LOW来"模拟"出中间电压。任何支持数字输出的GPIO都可以通过软件实现PWM,所以"模拟输出"引脚和"数字输出"引脚是同一组。
  • 模拟输入(ADC):需要真正的模数转换器硬件,ESP8266只有一个ADC通道,引出到了A0引脚。这是真正的硬件模拟输入,和PWM完全不是一回事。

记住这句话:模拟输出用PWM引脚,模拟输入用A0引脚,它们各走各的路。

1.7 D1 mini引脚速查表(完整版)

引脚标识
GPIO
数字输出
数字输入
PWM
模拟输入
注意事项
D0
16
不支持PWM,可用于唤醒
D1
5
✅ 推荐
D2
4
✅ 推荐
D3
0
启动特殊,慎用
D4
2
✅ 板载LED
D5
14
✅ 推荐
D6
12
✅ 推荐
D7
13
✅ 推荐
D8
15
启动特殊,慎用
A0
ADC
唯一模拟输入引脚

推荐引脚:D1、D2、D5、D6、D7(GPIO5、4、14、12、13)慎用引脚:D0(无PWM)、D3、D4、D8(启动有特殊作用)、A0(仅输入)

二、GPIO学习检查清单

学完本系列之后,你应该能够:

  • 解释什么是GPIO,以及输入/输出模式的区别
  • 正确使用pinMode()digitalWrite()digitalRead()
  • 解释为什么按钮需要消抖,并实现状态机消抖
  • 正确使用analogWrite()实现PWM调光
  • 正确使用analogRead(A0)读取模拟电压,并知道A0的电压范围
  • millis()实现非阻塞延时
  • 独立完成按钮+LED的综合控制项目
  • 理解为什么模拟输出和模拟输入是不同的硬件功能

如果你能打满所有勾,恭喜你,ESP8266的GPIO编程你已经掌握了

三、为什么我们要从GPIO开始学?

到这里,GPIO编程的内容基本讲完了。你可能会问:硬件编程都是这么麻烦的吗?答案是:不是!已经有很方便的编程方式,库函数编程。既然有了现成的的库,为什么我们还要从最底层的GPIO开始学?直接用库不好吗?答案是:学GPIO,不是为了让你每个项目都从写底层开始,而是为了让你能"看懂"代码。

3.1 库文件是什么?

库文件(Library),就是别人写好的、封装好的代码包,提供了特定功能的函数接口,让你不必从零开始写所有代码。比如:

  • • 你想控制OLED显示屏 → 用Adafruit_SSD1306
  • • 你想驱动舵机 → 用Servo
  • • 你想连接WiFi → 用ESP8266WiFi
  • • 你想解析JSON → 用ArduinoJson
  • • 你只是想使用按键 → 用OneButton.h

使用库的典型方式:

#include <Servo.h>      // 1. 引入库
Servo myServo;           // 2. 创建对象
void setup() {
    myServo.attach(D1);  // 3. 初始化
}

void loop() {
    myServo.write(90);   // 4. 调用库函数
}

简单几行代码就能实现舵机的转动,对吧?但问题是:当程序不工作时,你怎么办?我们从基础学起,至少在程序出现Bug时,我们有勇气去查找问题的所在。现在万物都能靠人工智能的时代,我们不懂点基础,那真的是什么都全凭AI。这真的靠谱吗?这真的可行吗?

写在最后

从点亮第一个LED,到完成多功能按钮台灯项目,你已经走过了ESP8266编程最基础、也最重要的一段路。GPIO编程看似简单,但它是所有项目的基石。传感器要读、执行器要控、通信模块要驱动……一切最终都要回到GPIO。把基础打扎实,后面的学习会顺畅很多。后续,我们也会采用库函数编程实现一个更为复杂的项目,也可以借助人工智能协助我们完成更为复杂的项目开发。