编译环境 Arduino 1.8.18
芯片ESP8266 模组ESP-12S
其他说明:无
0.项目结构
0.1 setup()
Arduino控制器通电或复位后,即会开始执行setup() 函数中的程序,该部分只会执行一次。
通常我们会在setup() 函数中完成Arduino的初始化设置,如配置I/O口状态,初始化串口等操作。
示例程序
// 给13号引脚连接的设备设置一个别名“led”
int led = 13;
// 在板子启动或者复位重启后, setup部分的程序只会运行一次
void setup()
{
// 将“led”引脚设置为输出状态
pinMode(led, OUTPUT);
}
// setup部分程序运行完后,loop部分的程序会不断重复运行
void loop()
{
digitalWrite(led, HIGH); // 点亮LED
delay(1000); // 等待一秒钟
digitalWrite(led, LOW); // 通过将引脚电平拉低,关闭LED
delay(1000); // 等待一秒钟
}
0.2 loop()
在setup() 函数中的程序执行完后,Arduino会接着执行loop() 函数中的程序。而loop()函数是一个死循环,其中的程序会不断的重复运行。
通常我们会在loop() 函数中完成程序的主要功能,如驱动各种模块,采集数据等。
示例程序
// 给13号引脚连接的设备设置一个别名“led”
int led = 13;
// 在板子启动或者复位重启后, setup部分的程序只会运行一次
void setup()
{
// 将“led”引脚设置为输出状态
pinMode(led, OUTPUT);
}
// setup部分程序运行完后,loop部分的程序会不断重复运行
void loop()
{
digitalWrite(led, HIGH); // 点亮LED
delay(1000); // 等待一秒钟
digitalWrite(led, LOW); // 通过将引脚电平拉低,关闭LED
delay(1000); // 等待一秒钟
}
0.3 Arduino程序main函数结构解析
在进行Arduino开发时,没有像传统C/C++程序使用入口函数main。实际上main函数存在于Arduino核心库中,且仍然是程序的入口。
在Arduino核心库中可见main.cpp文件,其内容如下:
#include <Arduino.h>
// Declared weak in Arduino.h to allow user redefinitions.
int atexit(void (* /*func*/ )()) { return 0; }
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }
void setupUSB() __attribute__((weak));
void setupUSB() { }
int main(void)
{
init();
initVariant();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;)
{
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}
通过以上程序可见,Arduino程序中编写的setup和loop函数,都在main函数中调用了。
loop的循环执行,是通过for循环实现的,且每次loop结束后,都会进行串口事件判断,也正是因为这种设计,串口事件不能实时响应。
1.数字IO输入输出
1.1 pinMode(pin, mode)
在使用输入或输出功能前,你需要先通过pinMode() 函数配置引脚的模式为输入模式或输出模式。
参数:
pin 指定配置的引脚编号
mode 指定的配置模式
1.2 digitalWrite(pin, value)
使用pinMode配置成输出模式后,你还需要使用digitalWrite() 让其输出高电平或者是低电平。
参数:
pin 指定输出的引脚编号;
value 指定输出的电平
1.3 digitalRead(pin)
在使用输入或输出功能前,你需要先通过pinMode() 函数配置引脚的模式为输入模式或输出模式。
参数:
pin 指定读取状态的引脚编号。
返回值:
返回值为获取到的信号状态,1为高电平,0为低电平。
1.4 示例程序 数字输出
/*
等待一秒钟,点亮LED,再等待一秒钟,熄灭LED,如此循环
*/
// ESP-12S板上 ESP8266芯片2号引脚连接了一个LED灯
// 设置一个别名“led”
int led = 2;
// 在板子启动或者复位重启后, setup部分的程序只会运行一次
void setup()
{
// 将“led”引脚设置为输出状态
pinMode(led, OUTPUT);
}
// setup部分程序运行完后,loop部分的程序会不断重复运行
void loop()
{
digitalWrite(led, HIGH); // 关闭LED
delay(1000); // 等待一秒钟
digitalWrite(led, LOW); // 通过将引脚电平拉低,打开LED
delay(1000); // 等待一秒钟
}
1.5 示例程序 数字输入输出
/*
通过13号引脚连接的按键,控制2号引脚连接的LED
*/
// 设置各引脚别名
const int buttonPin = 13; // 连接按键的引脚
const int ledPin = 2; // 连接LED的引脚
// ESP-12S板上 ESP8266芯片2号引脚连接了一个LED灯
// 设置一个别名“ledPin”
// 变量定义
int buttonState = 0; // 存储按键状态的变量
void setup() {
// 初始化LED引脚为输出状态
pinMode(ledPin, OUTPUT);
// 初始化按键引脚为输入状态
pinMode(buttonPin, INPUT);
}
void loop(){
// 读取按键状态并存储在变量中
buttonState = digitalRead(buttonPin);
// 检查按键是否被按下
// 如果按键按下,那buttonState应该为高电平
if (buttonState == HIGH) {
// 点亮LED
digitalWrite(ledPin, HIGH);
}
else {
// 熄灭LED
digitalWrite(ledPin, LOW);
}
}
2.串口通讯
使用串口与计算机通信,需要先使用Serial.begin() 初始化Arduino的串口通信功能
2.1 Serial.begin(speed, config)
初始化串口,可配置串口的各项参数
语法:
Serial.begin(speed)
Serial.begin(speed, config)
参数:
speed:波特率
config:数据位、校验位、停止位配置
2.2 Serial.print(val, format)
将数据输出到串口。
数据会以ASCII形式输出。如果要以字节形式输出数据,你需要使用write() 函数。
语法:
Serial.print(val)
Serial.print(val, format)
参数:
val: 需要输出的数据
format: 输出的进制形式
BIN(二进制)
DEC(十进制)
OCT(八进制)
HEX(十六进制)
或者指定输出的float数据带有小数的位数(默认输出两位),例如:
Serial.print(1.23456)输出为"1.23";
Serial.print(1.23456, 0) 输出为"1";
Serial.print(1.23456, 2) 输出为"1.23";
Serial.print(1.23456, 4) 输出为"1.2345"。
返回值:
输出的字节数
Serial.println(data)
从串行端口输出数据,跟随一个回车(ASCII 13, 或 'r')和一个换行符(ASCII 10, 或 'n')。这个函数所取得的值与 Serial.print()一样。
2.2 示例程序 串口通讯
int counter=0;//计数器
void setup() {
//初始化串口
Serial.begin(115200);
}
void loop() {
//每loop循环一次,计数器变量加1
counter = counter+1;
// 输出变量
Serial.print(counter);
// 输出字符
Serial.print(":");
// 输出字符串;
Serial.println("Hellow World");
delay(1000);
if(counter == 10)
ESP.restart(); //复位esp8266
}
3.软件复位
ESP.restart(); //复位esp8266
int counter=0;//计数器
void setup() {
//初始化串口
Serial.begin(115200);
}
void loop() {
//每loop循环一次,计数器变量加1
counter = counter+1;
// 输出变量
Serial.print(counter);
// 输出字符
Serial.print(":");
// 输出字符串;
Serial.println("Hellow World");
delay(1000);
if(counter == 10)
ESP.restart(); //复位esp8266
}
4.ADC模拟量采集
5.时间控制
5.1 delay( ms)
此函数为毫秒级延时。
参数为时长,类型unsigned long
5.2 delayMicroseconds( us )
此函数为微秒级延时。
参数为时长,类型unsigned int
5.3 示例程序
/*
Blink
等待一秒钟,点亮LED,再等待一秒钟,熄灭LED,如此循环
*/
// 在大多数Arduino控制板上 13号引脚都连接了一个标有“L”的LED灯
// 给13号引脚连接的设备设置一个别名“led”
int led = 13;
// 在板子启动或者复位重启后, setup部分的程序只会运行一次
void setup(){
// 将“led”引脚设置为输出状态
pinMode(led, OUTPUT);
}
// setup部分程序运行完后,loop部分的程序会不断重复运行
void loop()
{
digitalWrite(led, HIGH); // 点亮LED
delay(1000); // 等待一秒钟
digitalWrite(led, LOW); // 通过将引脚电平拉低,关闭LED
delay(1000); // 等待一秒钟
}
6.PWM
7.网络
#include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库
#include <ESP8266WebServer.h> // ESP8266WebServer库
ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是 'wifiMulti'
ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
void setup(void){
Serial.begin(115200); // 启动串口通讯
pinMode(2, OUTPUT); //设置内置LED引脚为输出模式以便控制LED
//通过addAp函数存储 WiFi名称 WiFi密码
wifiMulti.addAP("wfm", "12345678"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
wifiMulti.addAP("CMCC-yLaQ", "1123456"); // 这3个WiFi网络名称和密码
wifiMulti.addAP("taichi-maker3", "13572468");
// 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
// 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
Serial.println("正在连接Wifi,请稍等:");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) { // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
delay(1000); // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
Serial.print(i++); Serial.print(' '); // 将会连接信号最强的那一个WiFi信号。
} // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
// 此处while循环判断是否跳出循环的条件。
// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println('\n');
Serial.print("Connected to ");
Serial.println(WiFi.SSID()); // 通过串口监视器输出连接的WiFi名称
Serial.print("IP address:\t");
Serial.println(WiFi.localIP()); // 通过串口监视器输出ESP8266-NodeMCU的IP
esp8266_server.begin(); // 启动网站服务
esp8266_server.on("/", HTTP_GET, handleRoot); // 设置服务器根目录即'/'的函数'handleRoot'
esp8266_server.on("/LED", HTTP_POST, handleLED); // 设置处理LED控制请求的函数'handleLED'
esp8266_server.onNotFound(handleNotFound); // 设置处理404情况的函数'handleNotFound'
Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}
void loop(void){
esp8266_server.handleClient(); // 检查http服务器访问
}
/*设置服务器根目录即'/'的函数'handleRoot'
该函数的作用是每当有客户端访问NodeMCU服务器根目录时,
NodeMCU都会向访问设备发送 HTTP 状态 200 (Ok) 这是send函数的第一个参数。
同时NodeMCU还会向浏览器发送HTML代码,以下示例中send函数中第三个参数,
也就是双引号中的内容就是NodeMCU发送的HTML代码。该代码可在网页中产生LED控制按钮。
当用户按下按钮时,浏览器将会向NodeMCU的/LED页面发送HTTP请求,请求方式为POST。
NodeMCU接收到此请求后将会执行handleLED函数内容*/
void handleRoot() {
esp8266_server.send(200, "text/html", "<form action=\"/LED\" method=\"POST\"><input type=\"submit\" value=\"LED\"></form>");
}
//处理LED控制请求的函数'handleLED'
void handleLED() {
digitalWrite(2,!digitalRead(2));// 改变LED的点亮或者熄灭状态
esp8266_server.sendHeader("Location","/"); // 跳转回页面根目录
esp8266_server.send(303); // 发送Http相应代码303 跳转
}
// 设置处理404情况的函数'handleNotFound'
void handleNotFound(){
esp8266_server.send(404, "text/plain", "404: Not found"); // 发送 HTTP 状态 404 (未找到页面) 并向浏览器发送文字 "404: Not found"
}
8.IIC
DS3231 读取时间的例子
#include <Wire.h>
#define SDA 5 // Pin sda (I2C)
#define SCL 4 // Pin scl (I2C)
const unsigned char DS3231_ADDRESS = 0x68;
const unsigned char secondREG = 0x00;
const unsigned char minuteREG = 0x01;
const unsigned char hourREG = 0x02;
const unsigned char WTREG = 0x03; //weekday
const unsigned char dateREG = 0x04;
const unsigned char monthREG = 0x05;
const unsigned char yearREG = 0x06;
const unsigned char alarm_min1secREG = 0x07;
const unsigned char alarm_min1minREG = 0x08;
const unsigned char alarm_min1hrREG = 0x09;
const unsigned char alarm_min1dateREG = 0x0A;
const unsigned char alarm_min2minREG = 0x0B;
const unsigned char alarm_min2hrREG = 0x0C;
const unsigned char alarm_min2dateREG = 0x0D;
const unsigned char controlREG = 0x0E;
const unsigned char statusREG = 0x0F;
const unsigned char ageoffsetREG = 0x10;
const unsigned char tempMSBREG = 0x11;
const unsigned char tempLSBREG = 0x12;
const unsigned char _24_hour_format = 0;
const unsigned char _12_hour_format = 1;
const unsigned char AM = 0;
const unsigned char PM = 1;
void setup()
{
Serial.begin(115200); //设置串口波特率为115200
rtc_init(SDA, SCL);//RTC数组芯片初始化
}//setup
void loop()
{
Serial.println("Hello world!");
Serial.print(rtc_stunde()); //读时钟芯片秒
Serial.print(":");
Serial.print(rtc_minute()); //读时钟芯片秒
Serial.print(":");
Serial.print(rtc_sekunde()); //读时钟芯片秒
Serial.println("");
delay(1000);
}//loop
//**************************************************************************************************
void rtc_init(unsigned char sda, unsigned char scl)
{
Wire.begin(SDA, SCL);
// Wire初始化, 加入i2c总线
// 如果未指定,则以主机身份加入总线。
rtc_Write(controlREG, 0x00);
}
//**************************************************************************************************
// RTC I2C Code
//**************************************************************************************************
unsigned char rtc_Read(unsigned char regaddress)
{
Wire.beginTransmission(DS3231_ADDRESS);//发送一个I2C开始字符
Wire.write(regaddress);
Wire.endTransmission();//结束一个由beginTransmission()开始的并且由write()排列的从机的传输
Wire.requestFrom((unsigned char) DS3231_ADDRESS, (unsigned char) 1);//主设备请求从设备一个字节,这个字节可以被主设备用 read()或available()接受
return (Wire.read());
}
//**************************************************************************************************
void rtc_Write(unsigned char regaddress, unsigned char value)
{
Wire.beginTransmission(DS3231_ADDRESS);// 向地址为DS3231_ADDRESS的从机传送数据
Wire.write(regaddress);
Wire.write(value);
Wire.endTransmission();
}
//**************************************************************************************************
unsigned char rtc_sekunde() //读时钟芯片秒
{
return (bcd2dec(rtc_Read(secondREG)));
}
unsigned char rtc_minute() //读时钟芯片分
{
return (bcd2dec(rtc_Read(minuteREG)));
}
unsigned char rtc_stunde() //读时钟芯片小时
{
return (bcd2dec(rtc_Read(hourREG)));
}
unsigned char rtc_wochentag() //读时钟芯片星期
{
return (bcd2dec(rtc_Read(WTREG)));
}
unsigned char rtc_tag() //读时钟芯片日
{
return (bcd2dec(rtc_Read(dateREG)));
}
unsigned char rtc_monat() //读时钟芯片月
{
return (bcd2dec(rtc_Read(monthREG)));
}
unsigned char rtc_jahr() //读时钟芯片年
{
return (bcd2dec(rtc_Read(yearREG)));
}
unsigned char bcd2dec(unsigned char x) { //value 0...99 BCD TO 10进制
int z, e;
e = x & 0x0F;
z = x & 0xF0;
z = z >> 4;
z = z * 10;
return (z + e);
}
9.读取ID
uint64_t chipid;
void setup()
{
Serial.begin(115200);
}
void loop()
{
chipid=ESP.getChipId();//The chip ID is essentially its MAC address(length: 6 bytes).
Serial.printf("ESP8266 Chip ID = %04X",(uint16_t)(chipid>>32));//print High 2 bytes
Serial.printf("%08X\n",(uint32_t)chipid);//print Low 4bytes.
delay(3000);
}