简介
本代码是基于ESP32开发板实现的一个计时器功能,具备倒计时、计时器时长选择、显示当前时间、有源蜂鸣器报警等功能。代码中使用了WiFi网络连接、NTP时间同步、EEPROM存储等功能。通过按钮控制计时器的开始、停止和计时器时长的选择。
运行原理概述
在ESP32开发板上,使用了三个按钮,分别为开始计时按钮(BUTTON1)、停止计时按钮(BUTTON2)和计时器时长选择按钮。首先连接WiFi网络,同步NTP时间,并初始化OLED屏幕。
然后通过setdefaulttime()函数设置计时器默认时长,并初始化按钮引脚。
在主循环中通过checkTimerButtons()函数检测按钮状态,若按钮被按下,根据按钮类型执行相应的操作。如果开始计时按钮被按下,则开始计时,并在OLED屏幕上显示计时器倒计时时间。
如果停止计时按钮被按下,则停止计时,并根据按钮状态执行相应的操作。如果计时器时长选择按钮被按下,则切换计时器时长,并在OLED屏幕上显示新的计时器时长。
特色功能
1.具备倒计时功能,可以按照设定的计时器时长进行倒计时,到时后有报警提示。
2.计时器时长可以选择,用户可以根据需求选择不同的计时器时长。
3.通过WiFi网络连接和NTP时间同步功能,保证了计时器的时间准确性。
4.使用EEPROM存储功能,保存计时器时长选择的状态,即使重新上电,也能保留上一次的时长选择状态。
重要函数的解释
- void setdefaulttime()函数:设置计时器默认时长,并从EEPROM中读取上一次的时长选择状态。
- void checkTimerButtons()函数:检测按钮状态,并根据按钮类型执行相应的操作。
- void updateTimeDisplay()函数:更新OLED屏幕上的时间和计时器倒计时时间。
- bool saveIntToEEPROM(int value, int address)函数:将整数值存储到EEPROM中。
- int readIntFromEEPROM(int address)函数:从EEPROM中读取整数值。
ESP32开发板引脚 | 硬件设备引脚 | 连接方式 |
GPIO21 | OLED_SDA | I2C数据线 |
GPIO22 | OLED_SCL | I2C时钟线 |
GPIO35 | BUTTON1 | 按钮输入 |
GPIO34 | BUTTON2 | 按钮输入 |
GPIO32 | BUZZER_PIN | 有源蜂鸣器引脚 |
3.3V | OLED_VCC | OLED显示屏电源 |
GND | OLED_GND | OLED显示屏电源和地 |
3.3V | BUTTON1 | 按钮电源 |
3.3V | BUTTON2 | 按钮电源 |
GND | BUTTON1 | 按钮电源和地 |
GND | BUTTON2 | 按钮电源和地 |
#include <WiFi.h>
#include <NTPClient.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include <EEPROM.h>
#define OLED_ADDR 0x3C // OLED屏幕地址
#define OLED_SDA 21 // ESP32开发板上的SDA引脚
#define OLED_SCL 22 // ESP32开发板上的SCL引脚
#define BUTTON1 35 // 开始计时按钮
#define BUTTON2 34 // 停止计时按钮
#define BUZZER_PIN 32 // 有源蜂鸣器连接的引脚
#define MAXMENU 5
#define ADDR_1 1
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.ntsc.ac.cn"); // NTP客户端
Adafruit_SSD1306 display(128, 64, &Wire, -1); // OLED屏幕
int menuvalue = 1;
// 计时器相关变量
unsigned long startTime = 0; // 计时器开始时间
unsigned long stopTime = 0; // 计时器停止时间
bool timerRunning = false; // 是否正在计时
unsigned long duration = 0; // 计时器持续时间
unsigned long remainingTime = 0; // 剩余时间
// 时间显示相关变量
String lastFormattedTime = ""; // 上次更新的时间
unsigned long lastUpdateTime = 0; // 上次更新时间的时间戳
// 默认倒计时时间
int defaulttime = 5*60*1000;
// 设置默认倒计时时间
void setdefaulttime();
bool saveIntToEEPROM(int value, int address) {
EEPROM.begin(16);
EEPROM.put(address, value);
bool success = EEPROM.commit();
EEPROM.end();
display.setCursor(0, 50);
display.setTextSize(1);
if(success)
{
display.println("Save Success");
}
else
{
display.println("Failed");
}
Serial.print("save menuvalue:");
Serial.println(value);
return success;
}
int readIntFromEEPROM(int address) {
if (EEPROM.begin(16)) {
int value;
if (EEPROM.get(address, value)) {
EEPROM.end();
return value;
} else {
Serial.println("Failed to read from EEPROM");
}
} else {
Serial.println("EEPROM initialization failed");
}
EEPROM.end();
return 0;
}
unsigned long timerDuration = 5 * 60 * 1000; // 初始计时器长度为5分钟
//frequency参数是需要发出的声音频率,单位为Hz;duration参数是发出声音的持续时间,单位为毫秒。
void buzz(int frequency, long duration)
{
int period = 1000000 / frequency; // 计算周期
int pulse = period / 2; // 计算脉冲时间
for (long i = 0; i < duration * 1000L; i += period)
{
digitalWrite(BUZZER_PIN, HIGH); // 发送高电平
delayMicroseconds(pulse); // 持续脉冲时间的一半
digitalWrite(BUZZER_PIN, LOW); // 发送低电平
delayMicroseconds(pulse); // 持续脉冲时间的一半
}
}
void checkTimerButtons()
{
if (digitalRead(BUTTON1) == LOW && !timerRunning)
{
// 开始计时器
buzz(2000, 100);
startTime = millis();
timerRunning = true;
}
if (digitalRead(BUTTON2) == LOW)
{
buzz(2000, 100);
if (timerRunning)
{
// 停止计时器
stopTime = millis();
timerRunning = false;
remainingTime = timerDuration - (stopTime - startTime);
while(digitalRead(BUTTON2) == LOW);
delay(100);
}
else
{
// 切换计时器时间长度
if (menuvalue == 1)
{
timerDuration = 10 * 60 * 1000;
menuvalue =2;
}
else if (menuvalue == 2)
{
timerDuration = 30 * 60 * 1000;
menuvalue=3;
}
else if (menuvalue==3)
{
timerDuration = 60 * 60 * 1000;
menuvalue=4;
}
else if (menuvalue ==4)
{
timerDuration = 5 * 60 * 1000;
menuvalue = 1;
}
remainingTime = timerDuration;
defaulttime = timerDuration;
saveIntToEEPROM(menuvalue,ADDR_1);
while(digitalRead(BUTTON2) == LOW);
}
}
}
void setdefaulttime()
{
menuvalue = readIntFromEEPROM(ADDR_1);
Serial.print("read menuvalue:");
Serial.println(menuvalue);
if (menuvalue>0&&menuvalue<=MAXMENU)
{
switch(menuvalue)
{
case 1:
defaulttime = 5 * 60 * 1000;
break;
case 2:
defaulttime = 10 * 60 * 1000;
break;
case 3:
defaulttime = 30 * 60 * 1000;
break;
case 4:
defaulttime = 60 * 60 * 1000;
break;
default:
defaulttime = 5 * 60 * 1000;
break;
}
}
else
{
menuvalue = 1;
saveIntToEEPROM(menuvalue,ADDR_1);
defaulttime = 5 * 60 * 1000;
}
remainingTime = defaulttime;
}
void updateTimeDisplay()
{
timeClient.update();
String formattedTime = timeClient.getFormattedTime();
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(2);
display.setTextColor(WHITE);
display.println(formattedTime);
if (timerRunning)
{
duration = millis() - startTime;
remainingTime = timerDuration - duration;
display.setCursor(0, 36);
display.setTextSize(4);
int minutes = remainingTime / 1000 / 60;
int seconds = (remainingTime / 1000) % 60;
if (minutes < 10)
{
display.print("0");
}
display.print(minutes);
display.print(":");
if (seconds < 10)
{
display.print("0");
}
display.println(seconds);
}
else
{
display.setCursor(0, 36);
display.setTextSize(4);
int _minutes = remainingTime / 1000 / 60;
int _seconds = (remainingTime / 1000) % 60;
if (_minutes < 10)
{
display.print("0");
}
display.print(_minutes);
display.print(":");
if (_seconds < 10)
{
display.print("0");
}
display.println(_seconds);
}
display.display();
}
void setup()
{
// 连接WiFi网络
pinMode(BUZZER_PIN, OUTPUT);
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(WHITE);
display.println("Connecting to ");
display.println("360WiFi-016C34");
display.println("Password: ");
display.println("wangjinxuan");
display.display();
WiFi.begin("360WiFi-016C34", "wangjinxuan");
int i = 0;
while (WiFi.status() != WL_CONNECTED && i < 10)
{
delay(500);
display.clearDisplay();
display.setCursor(0, 0);
display.print("Connecting");
for (int j = 0; j < i; j++)
{
display.print(".");
}
display.display();
i++;
}
if (WiFi.status() != WL_CONNECTED)
{
display.clearDisplay();
display.setCursor(0, 0);
display.println("Failed to connect.");
display.display();
while (true)
{
// 连接失败,停止程序
}
}
// 初始化OLED屏幕
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
//初始化数值
setdefaulttime();
// 初始化按钮引脚
pinMode(BUTTON1, INPUT_PULLUP);
pinMode(BUTTON2, INPUT_PULLUP);
// 同步时间
timeClient.begin();
timeClient.setTimeOffset(28800); // 设置时区(这里为东八区)
}
void loop()
{
checkTimerButtons();
updateTimeDisplay();
if (timerRunning)
{
duration = millis() - startTime;
remainingTime = timerDuration - duration;
if (remainingTime <= 0 || remainingTime>timerDuration)
{
// 计时器已完成
timerRunning = false;
buzz(2000,1000);
delay(800);
buzz(2000,1000);
delay(800);
buzz(2000,1000);
delay(800);
remainingTime = timerDuration;
// TODO: 执行计时器完成操作
}
}
}