#define EB_FAST_TIME 120 // 定义一个常量,用于设置快速旋转的时间阈值(毫秒)
#include <GyverOLED.h> // 引入GyverOLED库,用于驱动OLED显示器
#include <EncButton.h> // 引入EncButton库,用于处理编码器和按钮输入
#include "GyverOLEDMenu.h" // 引入GyverOLEDMenu库,用于创建和管理菜单
#include <IRremote.h> //引入红外库
#include <EEPROM.h> //引入断电存储库
// 创建一个EncButton对象,它使用数字引脚6(编码器旋转/按下)和7(编码器方向),5(编码器按钮)作为输入,启用内部上拉电阻
EncButton eb(6, 7, 5, INPUT_PULLUP);
// 实例化一块SSD1306 128x64的OLED显示屏
GyverOLED<SSD1306_128x64> oled;
// 初始化一个带有9个菜单项的OledMenu对象
OledMenu<9, GyverOLED<SSD1306_128x64>> menu(&oled);
// 定义一些用于PID控制器的参数和其他设置的变量
int d_p = 10; // PID控制器的比例常数P
int d_i = 1000; // PID控制器的积分常数I
int d_d = 50; // PID控制器的微分常数D
byte tt11 = 10; // 定义一个字节类型的定时器值
float tt1 = 0.5; // 定义一个浮点数表示的时间设置
boolean lgh = false; // 布尔值,表示照明状态开关(开启或关闭)
int tt3 = 5; // 另一个定时器的值
int tt4 = 1000; // 第四个定时器的值
bool volShow = true; // 是否显示音量大小界面
float stepSize = 1.0; // 音量步进变量,默认值为1.0
#define IR_RECEIVE_PIN 2 // 红外接收引脚
// 红外接收对象
IRrecv irrecv(IR_RECEIVE_PIN);
decode_results results; // 存储红外信号的结果
void setup() {
oled.init(); // 初始化OLED显示屏
Wire.setClock(400000L); // 设置I2C通信时钟频率为400kHz
oled.clear(); // 清空OLED显示的内容
oled.update(); // 更新OLED显示,以确保清空效果
irrecv.enableIRIn();//启动红外接收
// 定义菜单项变化的回调函数
menu.onChange(onItemChange, true);
// 定义自定义打印格式的回调函数
menu.onPrintOverride(onItemPrintOverride);
// 向菜单中添加项
menu.addItem(PSTR("<- Return")); // 0: 返回项
menu.addItem(PSTR("PID. P"), GM_N_INT(1), &d_p, GM_N_INT(0), GM_N_INT(100)); // 1: PID参数P
menu.addItem(PSTR("PID. I"), GM_N_INT(1), &d_i, GM_N_INT(-5), GM_N_INT(20)); // 2: PID参数I
menu.addItem(PSTR("PID. D"), GM_N_INT(1), &d_d, GM_N_INT(0), GM_N_INT(7000)); // 3: PID参数D
menu.addItem(PSTR("ODA TIME."), GM_N_FLOAT(0.01), &tt1, GM_N_FLOAT(1), GM_N_FLOAT(20)); // 4: 其他时间参数
menu.addItem(PSTR("TIMER 1"), GM_N_BYTE(1), &tt11, GM_N_BYTE(1), GM_N_BYTE(255)); // 5: 定时器1
// 添加第二页的菜单项
menu.addItem(PSTR("LIGHTING"), &lgh); // 6: 照明选项
menu.addItem(PSTR("TIMER 3"), GM_N_INT(1), &tt3, GM_N_INT(1), GM_N_INT(5)); // 7: 定时器3
menu.addItem(PSTR("TIMER 4"), GM_N_INT(5), &tt4, GM_N_INT(0), GM_N_INT(1000)); // 8: 定时器4
displayVolume();
eb.attach(cb); // 绑定回调函数cb,用于处理编码器的动作
}
// 显示当前音量和步进大小
void displayVolume() {
oled.clear(); // 清空OLED显示的内容
oled.update(); // 更新OLED显示,以确保清空效果
oled.setCursor(0, 0); // 设置光标位置为(0,0)
oled.print("Volume: ");
oled.print(50);
oled.print("%"); // 显示音量百分比
oled.setCursor(0, 10); // 设置光标位置为(0,10)
oled.print("Step Size: ");
oled.print(stepSize, 1); // 显示步进大小,并保留小数点后1位
oled.update(); // 更新OLED显示,以确保清空效果 // 更新显示
}
// 当菜单项变化时调用的回调函数
void onItemChange(const int index, const void* val, const byte valType) {
if (valType == VAL_ACTION) { // 检查是否为动作类型
if (index == 0) { // 如果选中返回项(索引0)
menu.showMenu(false); // 隐藏菜单
}
}
}
// 菜单项自定义显示的回调函数
boolean onItemPrintOverride(const int index, const void* val, const byte valType) {
// 处理“TIMER 4”的格式化显示
if (index == 8) { // 索引8是我们要自定义的菜单项
unsigned int hours = tt4 / 60; // 获取小时
byte minutes = tt4 - (hours * 60); // 获取分钟
// 以hh:mm格式显示
if (hours < 10) {
oled.print(0); // 如果小时小于10,前面加零
}
oled.print(hours); // 打印小时
oled.print(":"); // 打印冒号
if (minutes < 10) {
oled.print(0); // 如果分钟小于10,前面加零
}
oled.print(minutes); // 打印分钟
return true; // 返回true表示我们自定义了输出,阻止默认输出
}
return false; // 如果不是我们要处理的项,返回false
}
// 编码器动作回调
void cb() {
switch (eb.action()) { // 根据编码器的动作类型执行相应操作
case EB_TURN: // 如果是旋转事件
if (eb.dir() == 1) { // 顺时针旋转
menu.selectPrev(eb.fast()); // 选择上一个菜单项,使用快速旋转
} else { // 反时针旋转
menu.selectNext(eb.fast()); // 选择下一个菜单项,使用快速旋转
}
break;
case EB_CLICK: // 如果是按钮点击事件
menu.toggleChangeSelected(); // 切换当前选择项的更改状态
break;
}
}
void loop() {
eb.tick(); // 在循环中处理编码器和按钮的状态
if (volShow ) {
switch (eb.action()) { // 根据编码器的动作类型执行相应操作
case EB_TURN: // 如果是旋转事件
if (eb.dir() == 1) { // 顺时针旋转
// menu.selectPrev(eb.fast()); // 选择上一个菜单项,使用快速旋转
} else { // 反时针旋转
// menu.selectNext(eb.fast()); // 选择下一个菜单项,使用快速旋转
}
break;
case EB_CLICK: // 如果是按钮点击事件
// menu.toggleChangeSelected(); // 切换当前选择项的更改状态
break;
case EB_CLICKS: // 双击事件
break;
case EB_HOLD: // 长按事件
// 显示菜单,第一参数设置显示为MODAL模式
menu.showMenu(true);
volShow = false;
break;
}
} else {
if (eb.action() == EB_HOLD) {
menu.showMenu(false);
displayVolume();
volShow = true;
}
}
}