ESP32学习笔记
Arduino环境
一、环境准备
-
第一部分:使用ArduinoIDE进行ESP32开发
-
Arduino IDE的安装
ESP32可以使用Arduino进行开发,本人在学习过程中所使用的是Arduino 1.8.19,由于Arduino的服务器并不在国内,访问官网需要大家科学上网,因此就要找各大论坛或是社区中提供的软件。
如果找不到,可以通过我的云盘分享安装:
链接:https://pan.baidu.com/s/1hpCOpD51Fmrc83gf2DkesA?pwd=q5eo
提取码:q5eo -
附加开发板设置(这一步也有可能要安全上网)
- 打开Arduino IDE之后,点击左上角文件,点击首选项
-
设置附加开发板管理器网址:
我用的是:https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
如果显示下载失败各位就只能去找国内可以使用的了。
-
选择ESP32 Dev Module作为开发板
- 开始你美妙的ESP32 开发工作
-
二、EPS32介绍
ESP32是由乐鑫公司推出的一款采用50nm工艺制成的具有最佳的功耗性能、射频性能、稳定性、通用性和可靠性的芯片。
ESP32内置内置两个低功耗 Xtensa® 32-bit LX6 MCU,拥有以下片上资源(部分):
- 448KB的ROM
- 520KB的片上SRAM
- 40MHz晶振
可实现功能:
- 2.4GHz Wi-Fi
- 蓝牙
- 超低功耗协处理器
- 多种外设驱动
实验所用ESP32开发环境
本实验所用的开发环境为单片机与嵌入式系统竞赛实训平台上的ESP32部分
-
引脚图及引脚说明:
- 引脚图:
-
引脚说明:
-
引脚注意事项(凭实力踩过的坑,没好好看官方文档的下场)
- ESP32的 0 口涉及到ESP32的复位 能不接尽量不接!
- GPIO 34-39仅做输入管脚,如果有输出需求,避开这几个
- 管脚 D0、D1、D2、D3、CMD 和 CLK 用于 ESP32 芯片与 SPI flash 间的内部通信,这些管脚最好不连,否则可能影响 SPI flash / SPI RAM 的工作。
- 管脚 GPIO16 和 GPIO17 仅适用于板载 ESP32-WROOM 系列和 ESP32-SOLO-1 的开发板,板载 ESP32-WROVER 系列开发板的管脚 GPIO16 和 GPIO17 保留内部使用。
三、单模块测试练习
1. 一灯大师——点亮LED
作为单片机开发人员来讲,点灯相当于编程学习的“Hello World”,所以第一步永远是点亮LED灯。
本实验所用的实训平台集成了各种传感器和执行器,只需要用杜邦线进行简单的连接就可以快速使用对应的传感器或是执行器。
- LED灯电路原理图:
-
引脚连接:
ESP32 LED 2 LED-IO -
实验代码:
实验效果:LED灯1S间隔亮灭
int LEDPin = 2; //设置LED灯的控制引脚为2 void setup() { pinMode(LEDPin, OUTPUT); //设置2号引脚为输出 } void loop() { digitalWrite(LEDPin, HIGH); delay(1000); digitalWrite(LEDPin, LOW); delay(1000); }
2. 流水灯
- 8个LED灯电路原理图:
-
引脚定义
ESP32 LED 21 SMG_245_A(LED 0) 19 SMG_245_B(LED 1) 18 SMG_245_C(LED 2) 5 SMG_245_D(LED 3) 17 SMG_245_E(LED 4) 16 SMG_245_F(LED 5) 4 SMG_245_G(LED 6) 2 SMG_245_DP(LED 7) -
实验代码:
实验效果:8个LED依次点亮,再依次熄灭
/** *这是低智写法,各位看官可以直接用一个数组放这些引脚 *这样也方便引脚模式设定和流水灯for循环输出 */ const int LED0 = 21; const int LED1 = 19; const int LED2 = 18; const int LED3 = 5; const int LED4 = 17; const int LED5 = 16; const int LED6 = 4; const int LED7 = 2; void setup() { pinMode(LED0, OUTPUT); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(LED7, OUTPUT); } void loop() { digitalWrite(LED0,LOW); digitalWrite(LED1,HIGH); delay(500); digitalWrite(LED1,LOW); digitalWrite(LED2,HIGH); delay(500); digitalWrite(LED2,LOW); digitalWrite(LED3,HIGH); delay(500); digitalWrite(LED3,LOW); digitalWrite(LED4,HIGH); delay(500); digitalWrite(LED4,LOW); digitalWrite(LED5,HIGH); delay(500); digitalWrite(LED5,LOW); digitalWrite(LED6,HIGH); delay(500); digitalWrite(LED6,LOW); digitalWrite(LED7,HIGH); delay(500); digitalWrite(LED7,LOW); digitalWrite(LED0,HIGH); delay(500); }
3. 蜂鸣器
- 蜂鸣器电路原理图:
-
引脚定义:
ESP32 蜂鸣器 2 BEEP-IO -
实验代码:
实现效果:蜂鸣器bebebe叫
const int BeepPin = 2; void setup() { // put your setup code here, to run once: pinMode(BeepPin, OUTPUT); } void loop() { // put your main code here, to run repeatedly: digitalWrite(BeepPin, HIGH); delay(100); digitalWrite(BeepPin, LOW); delay(100); }
4. 数码管显示
- 数码管电路原理图
-
引脚定义:
ESP32 数码管 21 SMG_245_A 19 SMG_245_B 18 SMG_245_C 5 SMG_245_D 17 SMG_245_E 16 SMG_245_F 4 SMG_245_G 2 SMG_245_DP 27 SMG_138_A 14 SMG_138_B 12 SMG_138_C -
实验代码:
实现效果:8个数码管依次显示数字0~8
//用于连接译码器,选择显示的数码管 int LSA = 27; int LSB = 14; int LSC = 12; //定义数码管8个显示控制引脚 A B C D E F G DP byte shumaguanPins[8] = {21, 19, 18, 5, 17, 16, 4, 2}; //显示1-10 高电平有效 unsigned char smgduan[10][8] = { {1, 1, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0}, //0 1 {1, 1, 0, 1, 1, 0, 1, 0}, {1, 1, 1, 1, 0, 0, 1, 0}, //2 3 {0, 1, 1, 0, 0, 1, 1, 0}, {1, 0, 1, 1, 0, 1, 1, 0}, //4 5 {1, 0, 1, 1, 1, 1, 1, 0}, {1, 1, 1, 0, 0, 0, 0, 0}, //6 7 {1, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 1, 1, 0, 1, 1, 1} //8 9 }; void setup() { pinMode(LSA, OUTPUT); pinMode(LSB, OUTPUT); pinMode(LSC, OUTPUT); for(int i = 0; i< 8; i++) { pinMode(shumaguanPins[i], OUTPUT); } } void loop() { unsigned char i, j; for(i = 0; i < 8; i++) { switch(i) { case(0): digitalWrite(LSA, 0); digitalWrite(LSB, 0); digitalWrite(LSC, 0); break; //显示第0位 case(1): digitalWrite(LSA, 1); digitalWrite(LSB, 0); digitalWrite(LSC, 0); break; //显示第1位 case(2): digitalWrite(LSA, 0); digitalWrite(LSB, 1); digitalWrite(LSC, 0); break; //显示第2位 case(3): digitalWrite(LSA, 1); digitalWrite(LSB, 1); digitalWrite(LSC, 0); break; //显示第3位 case(4): digitalWrite(LSA, 0); digitalWrite(LSB, 0); digitalWrite(LSC, 1); break; //显示第4位 case(5): digitalWrite(LSA, 1); digitalWrite(LSB, 0); digitalWrite(LSC, 1); break; //显示第5位 case(6): digitalWrite(LSA, 0); digitalWrite(LSB, 1); digitalWrite(LSC, 1); break; //显示第6位 case(7): digitalWrite(LSA, 1); digitalWrite(LSB, 1); digitalWrite(LSC, 1); break; break; //显示第7位 } for(j = 0; j< 8; j++) { digitalWrite(shumaguanPins[j], smgduan[i][j]); } delay(1000); //间隔一段时间扫描 for(j = 0; j< 8; j++) //消隐 { digitalWrite(shumaguanPins[j], 0); } } }
硬件连线图:
实现效果:
数码管显示效果
5. 红外对管
- 反射式红外对管电路原理图:
-
引脚定义:
ESP32 红外对管、LED 4 IRF-IO 16 LED-IO -
实验代码:
实验效果:遮住红外对管,LED点亮,反之LED熄灭
const int LED = 4; //LED灯引脚 const int INF = 16; //红外对射引脚 void setup() { // put your setup code here, to run once: pinMode(LED, OUTPUT); pinMode(INF, INPUT); } void loop() { // put your main code here, to run repeatedly: if(digitalRead(INF) == 0) { digitalWrite(LED, 0); } else { digitalWrite(LED, 1); } }
6. 继电器
- 继电器电路原理图:
-
引脚定义:
ESP32 继电器、红外对射 4 RL-IO 16 IRF-IO -
实验代码:
实验效果:遮住红外对管,继电器吸合,反之继电器断开
const int RELAY = 4; const int INF = 16; //红外对射引脚 void setup() { // put your setup code here, to run once: pinMode(RELAY, OUTPUT); pinMode(INF, INPUT); } void loop() { // put your main code here, to run repeatedly: if(digitalRead(INF) == 0) digitalWrite(RELAY, 1); //红外无遮挡时继电器断开 else digitalWrite(RELAY, 0); //红外遮挡时继电器吸合 }
7. 矩阵键盘
- 矩阵键盘电路原理图:
-
引脚定义:
ESP32 矩阵键盘 5 KC-0 17 KC-1 16 KC-2 4 KC-3 26 KR-0 27 KR-1 14 KR-2 12 KR-3 -
特殊说明:
在本次实验中就用到一个非常爽的东西,库!虽然之前使用51单片机的时候也有".h"文件进行支持,但也需要自己写或者从别人那找已经写好的配套头文件,但是使用Arduino 环境进行开发,可以直接从“管理库”中直接搜自己需要的库文件然后下载即可(也有可能要科学上网,不然就要自己找Zip的库文件解压到你Arduino工程文件目录下的Libraries文件夹中)。
通过库就不需要自己写按键扫描函数,只需要提供按键功能序列以及你所用的矩阵键盘的行列数以及对应行列所连接的引脚即可,极为方便,同时也大大减少了开发时间以及代码量。
在本实验中使用的是"Keypad.h"库文件,各位下不到可以从下面网盘链接自取:
链接:https://pan.baidu.com/s/1lfTZ2EHE_4-cLMO8G0xHrQ?pwd=bhrm
提取码:bhrm -
实验代码:
实验效果:串口输出对应按键信息
#include <Keypad.h> const byte ROWS = 4; //设定行数 const byte COLS = 4; //设置列数 char keys[ROWS][COLS] = { //定义按键对应键码输出 {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'0', '*', '#', 'D'} }; byte rowPins[ROWS] = {5, 17, 16, 4}; //定义行引脚有哪些 byte colPins[COLS] = {26, 27, 14, 12}; //定义列引脚有哪些 //构造函数,创建一个keypad对象 方便后续调用方法 //传入参数:makeKeymap(键码数组), 行引脚, 列引脚, 行数, 列数 Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); void setup() { // put your setup code here, to run once: Serial.begin(9600); } void loop() { // put your main code here, to run repeatedly: //定义一个变量用于接收按键信息 char key = keypad.getKey(); //显示按键信息 if(key) { Serial.print("按键:"); Serial.println(key); } }
8. SPI协议的LCD12864
-
LCD12864 SPI方式:
通过SPI总线的方式,可以以更少的接线实现LCD12864的显示效果。通过SPI总线的方式,只需要连接9根线就可以实现效果。
本次不要通过实训平台上引脚连接,直接使用杜邦线连接至LCD12864上对应引脚即可。
连接图如下:
-
引脚定义:
ESP32 LCD12864 18 E(CLK) 23 R/W 5 RS(CS) 22 RST -
实验代码:
实验效果:LCD12864上屏幕显示“你好中国”以及"Hello World"字样
#include <U8g2lib.h> U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18, /* data=*/ 23, /* CS=*/ 5, /* reset=*/ 22); // ESP32 int a = 18; void setup() { // put your setup code here, to run once: u8g2.begin(); u8g2.enableUTF8Print(); u8g2.setFont(u8g2_font_wqy12_t_gb2312); u8g2.setFontDirection(0);//设置字体方向 u8g2.clearBuffer(); //清除缓冲区 u8g2.setCursor(0, 10); u8g2.print("你好中国"); u8g2.setCursor(0, 20); u8g2.print("Hello World"); u8g2.setCursor(0, 30); u8g2.print(a); u8g2.sendBuffer(); } void loop() { // put your main code here, to run repeatedly: }
9. 直流电机
- 直流电机电路原理图:
-
引脚定义:
由于实训平台是通过一个ULN2003控制芯片同时驱动两个直流电机以及一个步进电机,因此电机接口为ULN2003输入接口。
其中ULN-O1~4为步进电机控制口,ULN-O5、6为两个直流电机控制口
ESP32 电机 12 ULN-O5(直流电机1) 14 ULN-O6(直流电机2) -
实验代码:
实验效果:两个直流电机以2S为间隔旋转和停止
int motor1 = 12; int motor2 = 14; void setup() { // put your setup code here, to run once: pinMode(motor1, OUTPUT); pinMode(motor2, OUTPUT); } void loop() { // put your main code here, to run repeatedly: digitalWrite(motor1, 1); digitalWrite(motor2, 1); delay(2000); digitalWrite(motor1, 0); digitalWrite(motor2, 0); delay(2000); }
10. 步进电机
-
步进电机电路原理图:与上述直流电机原理图共用
-
引脚定义:
ESP32 步进电机 26 ULN-O1 27 ULN-O2 14 ULN-O3 12 ULN-O4 -
实验代码:
在本实验中,同样使用到库文件,本次使用的是"Stepper.h"库文件,只需要传入步进电机旋转1圈所要的节拍数,以及四相控制引脚即可。
Stepper.h的库文件连接:
链接:https://pan.baidu.com/s/1lfTZ2EHE_4-cLMO8G0xHrQ?pwd=bhrm
提取码:bhrm实验效果:
步进电机反复正转一圈再反转一圈
#include <Stepper.h> const int stepsPerRevolution = 100; //参数分别是驱动板上的 ULN1 , ULN2 , ULN3 , ULN4 Stepper myStepper = Stepper(stepsPerRevolution, 26, 27, 14, 12); void setup() { //设置转速 myStepper.setSpeed(200); } void loop() { myStepper.step(2048); //正转1圈 delay(500); myStepper.step(-2048);//倒转1圈 delay(500); }
11. 串口通信实验
-
引脚定义:
ESP32 上位机 14 RXD 15 TXD -
实验代码:
实验效果:实现ESP32与上位机串口通信
//HardwareSerial.h 该库可以实现串口映射到其他引脚 #include <HardwareSerial.h> //ESP32一共拥有三个串口,其中串口0应该是用于程序下载,因此能操作的只有串口1和串口2 HardwareSerial MySerial_esp32(1); unsigned short i; char temp; void clear_uart_buffer(); //清除串口读取缓存区的缓存 void read_uart(); //读取串口数据 void setup() { // put your setup code here, to run once: Serial.begin(115200); //配置波特率11200, 默认SERIAL_8N1:8位数据位 无校验 1位停止位, RXD, TXD) MySerial_esp32.begin(115200, SERIAL_8N1, 14, 15); clear_uart_buffer(); } void loop() { // put your main code here, to run repeatedly: MySerial_esp32.println("uart test!"); read_uart(); delay(500); } void clear_uart_buffer(){ i = MySerial_esp32.available(); if(i != 0){ Serial.print("清空串口接收区缓存......"); Serial.println(MySerial_esp32.available()); while(i --) MySerial_esp32.read(); } else Serial.println("串口接收的缓存为空!"); } /* 关于串口读取还有一些别的函数,用的不多但是找的资料给大家罗列在这里 * * int peek(void); //返回接收缓存中第一个字节数据,但并不从中删除它; * void flush(void); //等待串口收发完毕; * 下面是两个发送的,我没怎么用过,我一般直接使用println来发送 * size_t write(uint8_t);写数据到TX FIFO,在发送FIFO中的数据会自动输出到TX端口上;该方法有很多重载,可以用来发送字符串、长整型、整形;如果TX FIFO已满,则该方法将阻塞; * size_t write(const uint8_t *buffer, size_t size);写数据到TX FIFO,如果发送FIFO已满,则该方法将阻塞; * size_t setRxBufferSize(size_t); //设置接收缓存大小(默认为256字节);ESP32默认有128字节的硬件RX FIFO,在RX FIFO收到数据后会移送到上面的接收缓存中;这个挺重要的但是我们直接使用默认的就好 */ void read_uart() { i = MySerial_esp32.available(); if(i != 0) { Serial.print("串口接收到的数据量为:"); Serial.println(MySerial_esp32.available()); while(i--) { temp = MySerial_esp32.read(); //读取一个数据并且将它从缓存区删除 Serial.print(temp); //读取串口接收回来的数据但是不做处理只给与打印 } Serial.println(); } else Serial.println("串口接收区没有数据!"); }
12. 温湿度读取
- DHT11电路原理图:
-
引脚定义:
ESP32 DHT11 5 DHT-IO -
实验代码:
在本实验中,同样使用到库文件,本次使用的是"DHTesp.h"库文件。
DHTesp.h的库文件连接:
链接:https://pan.baidu.com/s/1lfTZ2EHE_4-cLMO8G0xHrQ?pwd=bhrm
提取码:bhrm实验效果:通过串口不断输出环境温湿度数据
#include "DHTesp.h" const int DHTPIN = 5; DHTesp dhtSensor; void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println("DHT11 test!"); dhtSensor.setup(DHTPIN, DHTesp::DHT11); } void loop() { // put your main code here, to run repeatedly: TempAndHumidity data = dhtSensor.getTempAndHumidity();//接受来自传感器的温度湿度数据,存入data变量 Serial.println("Temp: " + String(data.temperature, 2) + "°C");//开始通过串口显示变量的温度信息 Serial.println("Humidity: " + String(data.humidity, 1) + "%");//显示湿度信息 Serial.println("---");//打印分隔符 delay(1000);//延迟1秒,1000毫秒 }
13. 光敏电阻
- 光敏电路原理图:
-
引脚定义:
ESP32 光敏电阻、高亮LED 4 AIN1(光敏电阻) 16 PLED-IO(高亮LED) -
实验代码:
实验效果:随着光敏阻值的大小变化而调整亮度
/************************************ * 模拟手机屏幕随环境光自动调节亮度 * 使用光敏电阻实现高亮LED亮度随环境光亮变换 * 所用引脚对应: * ESP32 --------------- 元器件 * 4 --------------------- 光敏电阻(AIN1) * 16--------------------- 高亮LED(PLED_IO) *************************************/ const int LIGHTPIN = 4; const int LEDPIN = 16; void setup() { // put your setup code here, to run once: Serial.begin(115200); pinMode(LIGHTPIN, INPUT); pinMode(LEDPIN, OUTPUT); } void loop() { // put your main code here, to run repeatedly: int light = analogRead(LIGHTPIN); Serial.print("光敏阻值:"); Serial.println(light); float ledLight = light / 15; Serial.print("高亮LED灯亮度值:"); Serial.println(ledLight); analogWrite(LEDPIN, ledLight); delay(50); }
14. PWM输出
-
引脚定义:
ESP32 高亮LED 16 PLED-IO -
实验代码:
实验效果:随着PWM的输出,实现高亮LED的呼吸灯效果
#include <Arduino.h> /**使用Arduino库中自带的PWM函数库—— esp32-hal-ledc.c 和 esp32-hal-ledc.h **/ /**相关函数**/ /*************************************************************************************************** //channel 0-15 resolution 1-16bits freq limits depend on resolution double ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits);//设置通道频率和分辨率 void ledcWrite(uint8_t channel, uint32_t duty);//设置通道占空比 double ledcWriteTone(uint8_t channel, double freq);//设置通道频率 double ledcWriteNote(uint8_t channel, note_t note, uint8_t octave);//输出需要的音阶 uint32_t ledcRead(uint8_t channel);//读取某个通道的占空比 double ledcReadFreq(uint8_t channel);//读取通道频率 void ledcAttachPin(uint8_t pin, uint8_t channel);//将通道与引脚绑定 void ledcDetachPin(uint8_t pin);//解绑引脚与通道的关系 ****************************************************************************************************/ //定义高亮Led引脚 const int ledPin = 16; //设置PWM对应参数 const int freq = 5000; //频率 const int ledChannel = 0; //通道 const int resolution = 8; //分辨率 void setup() { //PWM参数设定 ledcSetup(ledChannel, freq, resolution); //通道与引脚绑定 ledcAttachPin(ledPin, ledChannel); } void loop() { //通过占空比完成高亮LED 呼吸灯效果 for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle ++) { ledcWrite(ledChannel, dutyCycle); delay(15); } for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle --) { ledcWrite(ledChannel, dutyCycle); delay(15); } }
15. ADC电位器输入
- 电位器电路原理图:
-
引脚定义:
ESP32 元器件 4 电位器AIN2 15 直流电机ULN-O5 16 高亮LED PLED_IO -
实验代码:
实验效果:旋转电位器,实现直流电机转速快慢控制以及高亮LED的明暗控制
/** * 实现效果:电位器控制直流电机和高亮LED亮度显示 * 引脚说明: * ESP32 --------------- 元器件 * 4 --------------------- 电位器(AIN2) * 15--------------------- 直流电机(ULN_05) * 16--------------------- 高亮LED(PLED_IO) */ const int ADCIN = 4; const int MOTOR = 15; const int LED = 16; void setup() { // put your setup code here, to run once: pinMode(ADCIN, INPUT); pinMode(MOTOR, OUTPUT); pinMode(LED, OUTPUT); Serial.begin(115200); } void loop() { // put your main code here, to run repeatedly: int adc = analogRead(ADCIN); int velocity = (adc - 1170) / 12; int light = (adc - 1200) / 12; Serial.print("ADC:"); Serial.println(adc); delay(100); analogWrite(MOTOR, velocity); analogWrite(LED, light); }
16. 超声波测距
- 超声波电路原理图:
-
引脚定义:
ESP32 超声波 15 TRIG 2 ECHO -
实验代码:
实验效果:串口不断打印输出超声波测出的物体距离值
const int TrigPin = 15;//Trig const int EchoPin = 2;//Echo float distance; void setup() { Serial.begin(115200); pinMode(TrigPin, OUTPUT); // 要检测引脚上输入的脉冲宽度,需要先设置为输入状态 pinMode(EchoPin, INPUT); Serial.println("超声波传感器:"); } void loop() { // 产生一个10us的高脉冲去触发TrigPin digitalWrite(TrigPin, LOW); delayMicroseconds(2); digitalWrite(TrigPin, HIGH); delayMicroseconds(10); digitalWrite(TrigPin, LOW); // 检测脉冲宽度,并计算出距离 distance = pulseIn(EchoPin, HIGH) / 58.00; Serial.print(distance); Serial.print("cm"); Serial.println(); delay(1000); }