前言
ESP8266EX是乐鑫公司研发的一款芯片,具备完善的Wi-Fi网络功能,既能独立运作,也能作为从设备配合其他主控MCU使用。通过SPI/SDIO接口或UART接口,它可作为Wi-Fi适配器,轻松集成到基于各种微控制器的设计方案中。
一般来说,搭载了ESP8266芯片的开发板(如下图),就能称为ESP8266NodeMCU。
本文主要使用Arduino IDE来开发ESP8266NodeMCU,零基础也能看懂(前提是有点C语言基础)。
配置环境
首先我们需要安装好Arduino IDE,安装包可以在Arduino官网获取,Arduino IDE是完全免费的。
选择下载Windows版本,进入下载界面后一直选择仅下载就行。
下载地址:Arduino IDE下载
下载完成后直接安装Arduino IDE(注意设置安装地址,别装到C盘去了)。
安装完就是这样了,接下来我们还需要安装ESP8266的支持包。
可以选择直接在Arduino IDE中安装ESP32的支持包,不过很慢而且需要科学的网络环境。
这里我推荐去下载ESP8266的离线支持包,下载地址:ESP8266离线安装包
下载完离线安装包后双击打开等待读条完毕就可以了。
Arduino IDE搞定后我们还需要安装USB转串口的驱动,这个根据你的开发板上的转串口芯片来,一般常用的是CH340系列。CH340和CP210系列驱动下载地址:USB驱动 CH340&CP210
安装完USB驱动后使用数据线将开发板与电脑相连,如果安装成功且开发板没有问题就可以读出芯片了。
开发板和电脑连接没问题的话就可以再打开Arduino IDE了。
打开设备管理器查看我们的开发板使用的端口,我这里使用的COM5,。,
选择完端口和开发板后点确认,至此开发环境我们已经搭建完成,下面开始攻略ESP8266吧。
GPIO函数
要使用一款单片机首先需要需要熟悉GPIO的基本操作,先看芯片引脚。
ESP8266可使用的GPIO口并不多,I/O口参考安信可的ESP-12F的规格书。
初始化GPIO
void pinMode(uint8_t pin, uint8_t mode);
GPIO口初始化函数,参数pin是引脚号,mode是配置引脚模式,引脚模式有以下几种:
OUTPUT
:设为输出模式。
INPUT
:设为输入模式。
INPUT_PULLUP
:设为输入模式,并启用内部上拉电阻。
写GPIO状态
void digitalWrite(uint8_t pin, uint8_t val);
GPIO数字输出,参数pin是引脚号,val是电平状态(输1或0就行)。
读GPIO状态
int digitalRead(uint8_t pin);
读取GPIO电平状态,参数pin是引脚号,返回1或者0。
延时函数
delay(unsigned long ms);
delayMicroseconds(unsigned int us);
毫秒延时和微秒延时。
点亮LED
OK,知道了引脚初始化和引脚操作函数那么接下来就是点灯了。
void setup() {
pinMode(12,OUTPUT);
digitalWrite(12,1);
}
由于我的开发板板载的LED灯链接的是GPIO12所以我这里配置了12引脚,烧录成功就可以看到LED被点亮,下面让它闪烁起来。
void setup() {
pinMode(12,OUTPUT);
}
void loop() {
digitalWrite(12,1);
delay(500);
digitalWrite(12,0);
delay(500);
}
加入delay函数即可实现闪烁功能,当然这样写就太小白了而且可读性不佳。
void setup() {
pinMode(12,OUTPUT);
}
void loop() {
digitalWrite(12,!digitalRead(12));
delay(500);
}
可以使用digitalRead()函数直接读取IO口点电压并取反,这下又短又方便了。因为前面给IO12配置的是输出模式,所以digitalRead()其实是不能直接读取引脚外部电平的,这里实际只是读取了程序输出电平而已,想要去读外部电平还是得初始化引脚为输入模式。
PWM输出
要使用PWM功能需要先配置引脚模式为输出模式。
PWM输出
analogWrite(pin,value);
在配置完引脚模式后使用这个函数就可以直接输出PWM默认输出1khz,参数pin为引脚号,value是设置占空比,默认范围一般是0-255。
PWM频率
void analogWriteFreq(uint32_t freq);
设置PWM频率,frep为要设置的频率。ESP8266的所有的PWM引脚都是共享同一个时钟源,所以此函数设置后是对所有PWM引脚作用的。
PWM分辨率
void analogWriteResolution(int res);
设置PWM分辨率,res为要设置的分辨率,通常为8-16位之间。例如设置为10,那么analogWrite()函数可以设置的参数范围就是0-1023了,如果说要输出一个占空比为%50的PWM信号那么就输512就行了。
输出PWM信号
了解完前面的一系列函数,马上来做下输出PWM信号的实验。
void setup() {
pinMode(12,OUTPUT);
analogWrite(12, 128);
}
将这段函数烧进芯片就可以输出一个1khz占空比50%的PWM信号,下面我们来设置下PWM信号的频率和占空比。
void setup() {
pinMode(12,OUTPUT);
analogWriteFreq(10000);
analogWriteRange(1023);
analogWrite(12, 512);
}
这样就可以输出一个10khz占空比50%的PWM信号。
PWM呼吸灯
要想变强还得点灯,下面写进阶点灯之呼吸灯。
int i=0;
uint8_t flag=0;
void setup() {
pinMode(12,OUTPUT);
analogWriteFreq(1000);
analogWriteRange(255);
}
void loop() {
if(i<255&& flag==0) i++;
else flag=1;
if(i>0 && flag==1) i--;
else flag=0;
analogWrite(12,i);
delay(1);
}
简单写几个if else就可以搞定呼吸灯了,好了感觉到实力变强了,马上进入下个环节。
串口通信
串口通信的实验需要将我们的开发板通过数据线和电脑端连接起来,Arduino IDE中自带了一个串口调试端可以直接使用来查看串口数据(个人感觉不太好用)。
初始化和配置
Serial.begin(speed);
初始化串口,ESP8266NodeMCU串口速率支持110~4608000 bps,默认115200bps,这个函数是指定波特率,然后数据位是默认8位,停止位1位,无校验位。这里直接配置Serial使用的是ESP8266的UART0,配置Serial1则使用UART1。
发送数据
size_t Serial.write(uint8_t data);
size_t Serial.write(const uint8_t *buffer, size_t size);
写一个字节到串口,参数data要写入的数据(uint8_t类型),buffer指向要写入的数据缓冲区的指针,size要写入的字节数,size_t 返回写入的字节数。这个函数可以用于发送数组,它不会自动在数据末尾加换行符,需要手动添加。
Serial.print(data);
Serial.println(data);
Serial.printf(data);
这三函数都是打印数据到串口
print是单纯输出数据
println是在输出数据并自动在末尾添加换行符
printf就跟c语言中的printf一样,可以格式化输出
发送数据实验
了解完了前面的函数我们就可以开始串口输出数据实验了,先试试发送数组。
uint8_t data[] = {0xff,0xee,0xaa,0x00};
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.write(data,sizeof(data));
delay(500);
}
把程序烧进芯片,然后打开串口调试助手查看发送的数据,这里我使用的是野火的串口调试助手下载地址我会放在相关链接里。
可以看到data数组的确发送出去了,OK,马上打印下字符串。
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.printf("holle world!\n");
delay(500);
}
nice!字符串也是成功输出。
接收数据
int Serial.available();
检查串口缓冲区中是否有可读取的数据,返回缓冲区中可读取的字节数。
int Serial.read();
从串口缓冲区读取一个字节的数据,返回读取的字节,没有则返回-1。
String Serial.readString(char terminator);
从串口缓冲区读取字符串,直到指定的终止字符或缓冲区为空,参数terminator指定终止字符。返回读取的字符串。
String Serial.readStringUntil(char terminator);
功能和参数跟readString()差不多,返回读取到的字符串,不包括终止符。
串口回声实验
了解完前面的函数我们就可以进行接收数据并将接收的数据发回去的实验了,也就是回声实验。
void setup() {
Serial.begin(115200);
}
void loop() {
//检查是否有数据可读
if(Serial.available()>0){
//读取数据直到终止字符
String receivedString = Serial.readStringUntil('\n');
//输出接收的字符
Serial.print("Received: ");
Serial.println(receivedString);
}
}
定时函数
ESP8266在运行过程中,通常是一条线式的依次执行任务,而在我们实际开发项目时,可能需要同时执行多个任务。比如要定时输出一段数据并且还要实时检测按键是否被按下,这种情况下我们就需要分时执行各个任务,在单片机极快的运行速度下分时运行的各个任务可以做到“同时进行”。
为解决以上问题,我们需要使用Ticker库。接下来介绍Ticker库的函数与使用。
初始化Ticker对象
#include <Ticker.h>
Ticker tim0;
要使用Ticker库首先要包含Ticker.h文件,再创建Ticker类。注意:不能命名为time。
启动定时函数
ticker.attach(interval, callback);
这个函数可以启动定时执行函数,并按设定时间间隔调用回调函数。参数interval是设置的间隔时长,单位为秒,callback是调用的函数。这个函数其实还有第三个参数,就是向定时调用的callback函数所传递的参数。
注意:attach函数所能传递的参数最多只有一个。另外该参数仅能是以下类型中的一种:char, short, int, float, void*, char*。
ticker.attach_ms(interval, callback);
这个函数还有个ms版本的,除了interval参数单位为毫秒以外用法一样。
停止定时函数
ticker.detach();
使用此函数即可停止定时执行函数。
定时执行函数
#include <Ticker.h>
Ticker tim0;
int i;
int flag;
int cnt;
void setup() {
Serial.begin(115200);
pinMode(12,OUTPUT);
Serial.print("begin\n");
tim0.attach(1, callback, 5);
}
void loop() {
if(i<255&& flag==0) i++;
else flag=1;
if(i>0 && flag==1) i--;
else flag=0;
analogWrite(12,i);
delay(5);
}
void callback(int max){
cnt++;
Serial.printf("hello world!\n");
if(cnt==max){
tim0.detach();
Serial.printf("stop\n");
}
}
这段函数实现了在运行呼吸灯的同时定时发送“hello world”,然后在执行到设定次数时关闭定时执行函数。
外部中断
外部中断是一种非常有用的功能,它允许微控制器响应来自外部源的异步事件。在做交互类或者不定时采集数据的项目中会很有用。
配置外部中断
attachInterrupt(digitalPinToInterrupt(pin), callback, mode);
这个函数用于设置一个中断,当指定的引脚发生变化时,会调用回调函数。
digitalPinToInterrupt(pin):设置中断引脚。
callback:当中断发生时调用的函数。这个函数不能有参数,也不能返回任何值。
mode:设置中断触发方式,触发方式有以下几种:
LOW
:低电平触发。
CHANGE
:上升下降延都触发。
RISING
:上升延触发。
FALLING
:下降延触发。
关闭外部中断
detachInterrupt(uint8_t pin);
关闭指定引脚外部中断触发,参数pin为需要关闭的引脚号。
外部中断实验
int cnt;
ICACHE_RAM_ATTR void callback(){
Serial.printf("hello world!\n");
if(++cnt>=3){
Serial.printf("stop\n");
detachInterrupt(2);
}
}
void setup() {
Serial.begin(115200);
pinMode(2,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), callback, RISING);
}
void loop() {
}
我们把IO2设为输入模式,并启用内部上拉电阻,再配置为外部中断。每次触发会使用串口发送“hello world”,触发三次后关闭中断。
特别注意:外部中断回调函数的代码必须要放在iram区(指令RAM)中,而不是默认的flash区。所以中断函数要加上前缀ICACHE_RAM_ATTR
申明函数在iram区中。
关于 ICACHE_RAM_ATTR
:iram的大小是有限的(ESP8266通常是32KB),通常,只有那些确实需要快速执行且频繁调用的函数才需要使用 ICACHE_RAM_ATTR
。
读写存储器
ESP8266 没有内置的 EEPROM,但是可以使用其内部的 Flash 来模拟 EEPROM 的功能。我们可以使用EEPROM
库来操作EEPROM,可以掉电不丢失地帮我们存储一些数据。
初始化EEPROM
#include <EEPROM.h>
void setup(){
EEPROM.begin(512);
}
首先包含EEPROM.h文件,再初始化EEPROM并设置空间大小。ESP8266的EEPROM空间可达4096字节,即EEPROM可操作的地址为0到4095。每个地址空间可以存储一个字节的数据,范围是0到255。
写入数据
EEPROM.write(address, value);
将一个字节写入 EEPROM。参数address是写入数据的地址(0到EEPROM最大值-1),value要写入的字节(0到255)。
EEPROM.put(address, value);
将一个值写入EEPROM。这个函数的用法跟上个函数是一样的,不过这个函数写入的数据可以是多个字节,例如写入一个float类型的值。
提交写入
EEPROM.commit();
在执行完EEPROM.write函数后,还需要使用此函数才能真正将数据写入EEPROM。
读取数据
EEPROM.read(address);
从EEPROM中读取一个字节。参数address是读取数据的地址,返回读取的字节。
EEPROM.get(address, value);
从 EEPROM 读取一个值。第一个参数跟上个函数一样,value是存储读取到的值的变量(注意要和写入数据的类型一样)。这个函数和EEPROM.put相对,可以用于读取多字节数据。
关闭EEPROM
EEPROM.end();
关闭 EEPROM 库,释放资源。
读写数据实验
#include <EEPROM.h>
uint8_t data[]={11,45,14};
void setup() {
Serial.begin(115200);
EEPROM.begin(10);
for(int i=0;i<sizeof(data);i++){
EEPROM.write(i, data[i]);
}
EEPROM.commit();
}
我们先写入一段数据进EEPROM。
#include <EEPROM.h>
uint8_t data[]={11,45,14};
void setup() {
Serial.begin(115200);
EEPROM.begin(10);
delay(1000);
for(int i=0;i<sizeof(data);i++){
Serial.printf("%d",EEPROM.read(i));
}
}
将这段函数烧入单片机后再把单片机断电,测试其掉电数据是否保存。
呜哇!好臭的数据,不过EEPROM读写实验没问题。
总结
做完以上的各个实验后,我们已经能够使用ESP8266NodeMCU的基本功能了,然而接下来才刚刚开始。
ESP8266真正强大的是它的WIFI功能,下一节我们将攻略ESP8266的WIFI功能,并使用ESP8266连接物联网平台。
下一节
快速攻略ESP8266NodeMCU(基于Arduino IDE)(下)
好看爱看(๑•̀ㅁ•́ฅ)