#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <driver/rtc_io.h>
#include "InterruptButton.h"
// define menu
#define TITLEHEIGHT 1 // 标题行高度,取值范围:0-3,每个单位占据16个像素
#define MENUITEMCOUNT 8 // 菜单个数
#define TITLECOUNT 1 // 菜单个数
// 菜单项目名称,每个中文字占3个字节
static char menuTitle[TITLECOUNT][15] = {"菜单标题"};
static char menu[MENUITEMCOUNT][15] = {"1-正弦波", "2-方波", "3-矩形波", "4-三角波",
"5-锯齿波", "6-阶梯波", "7-脉冲波", "8-白噪声"};
// 滚动菜单参数
int arrowPos = 0; // 箭头位置,取值范围: TITLEHEIGHT - 3
int itemPos[MENUITEMCOUNT]; // 菜单项目所在位置
bool updateDisplay = true; // true:允许屏幕刷新
int topItemIndex = 0; // 位于顶端的菜单项目序列号,取值范围0-(MENUITEMCOUNT- 4+TITLEHEIGHT)。
#define SCROLLSTEP 2 // 菜单滚动步进距离,单位为像素
#define SCROLLCOUNT (16 / SCROLLSTEP) // 菜单滚动次数。 步进距离 x 滚动次数 = 16像素(菜单字体高度)
#define SCROLDELAY 30 // 菜单滚动一次后的暂停时间,数字越大,滚动越慢,单位毫秒。暂停时间太短屏幕有可能来不及刷新
// Buttons
#define TOPBUTTONPIN GPIO_NUM_17
#define MIDDLEBUTTONPIN GPIO_NUM_4
#define BOTTOMBUTTONPIN GPIO_NUM_16
InterruptButton topButton(TOPBUTTONPIN, LOW);
InterruptButton middleButton(MIDDLEBUTTONPIN, LOW);
InterruptButton bottomButton(BOTTOMBUTTONPIN, LOW);
void menu0TopButtonKeyPress() {
if (arrowPos > 0) {
arrowPos--;
} else {
topItemIndex--;
if (topItemIndex < 0) topItemIndex = 0;
if (itemPos[MENUITEMCOUNT - 1] < MENUITEMCOUNT * 16 + TITLEHEIGHT * 16 - 1) {
for (int i = 0; i < SCROLLCOUNT; i++) {
for (int j = 0; j < MENUITEMCOUNT; j++) {
itemPos[j] = itemPos[j] + SCROLLSTEP;
}
updateDisplay = true;
delay(SCROLDELAY);
}
}
}
updateDisplay = true;
}
int itemSelected = 0;
void menu0MiddleButtonKeyPress() {
Serial.print("Selected ");
Serial.println(itemSelected);
updateDisplay = true;
}
void menu0BottomButtonKeyPress() {
if (arrowPos < 3 - TITLEHEIGHT) {
arrowPos++;
} else {
topItemIndex++;
if (topItemIndex > MENUITEMCOUNT - 4 + TITLEHEIGHT) topItemIndex = MENUITEMCOUNT - 4 + TITLEHEIGHT;
if (itemPos[MENUITEMCOUNT - 1] > 63) {
for (int i = 0; i < SCROLLCOUNT; i++) {
for (int j = 0; j < MENUITEMCOUNT; j++) {
itemPos[j] = itemPos[j] - SCROLLSTEP;
}
updateDisplay = true;
delay(SCROLDELAY);
}
}
}
updateDisplay = true;
}
void buttonsInit() {
InterruptButton::setMenuCount(2);
InterruptButton::setMenuLevel(0); // Use the functions bound to the first
// menu associated with the button
// Use larger values for more memory intensive functions if using
// Asynchronous mode. InterruptButton::m_RTOSservicerStackDepth = 4096;
InterruptButton::m_RTOSservicerStackDepth = 4096;
// Defaults to Asynchronous (immediate like an ISR and not actioned in main
// loop)
InterruptButton::setMode(Mode_Asynchronous);
pinMode(TOPBUTTONPIN, PULLUP);
pinMode(MIDDLEBUTTONPIN, PULLUP);
pinMode(BOTTOMBUTTONPIN, PULLUP);
//////////////////////////////////////////////////////////////////////////////////////////////
static uint8_t thisMenuLevel = 0;
topButton.bind(Event_KeyPress, thisMenuLevel, menu0TopButtonKeyPress);
// topButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// topButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
middleButton.bind(Event_KeyPress, thisMenuLevel, menu0MiddleButtonKeyPress);
// middleButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// middleButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
bottomButton.bind(Event_KeyPress, thisMenuLevel, menu0BottomButtonKeyPress);
// bottomButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// bottomButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
//////////////////////////////////////////////////////////////////////////////////////////////
thisMenuLevel = 1;
// topButton.bind(Event_KeyPress, thisMenuLevel, []() {});
// topButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// topButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
// middleButton.bind(Event_KeyPress, thisMenuLevel, []() {});
// middleButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// middleButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
// bottomButton.bind(Event_KeyPress, thisMenuLevel, []() {});
// bottomButton.bind(Event_DoubleClick, thisMenuLevel, []() {});
// bottomButton.bind(Event_LongKeyPress, thisMenuLevel, []() {});
}
// M0/ESPleftMargin/ESP8266/mega2560/Uno/Leonardo
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0,
/* reset=*/U8X8_PIN_NONE);
void setup(void) {
Serial.begin(115200);
u8g2.begin();
buttonsInit();
for (int i = 0; i < MENUITEMCOUNT; i++) {
itemPos[i] = 16 * TITLEHEIGHT + 15 + 16 * i; // 当TITLEHEIGHT=1时,取值范围:31, 47, 63, 79, 95, 111, 127, 143
}
updateDisplay = true;
}
#define WAKEUP_PIN GPIO_NUM_4
#define MAXIDELTIME 600000 // mS
int leftMargin = 32;
uint32_t idelTimer = 0;
void loop(void) {
if (updateDisplay) {
u8g2.clearBuffer();
// draw arrow
u8g2.setFont(u8g2_font_unifont_t_78_79);
u8g2.drawGlyph(10, 16 * arrowPos + 16 * TITLEHEIGHT + 15, 0x2790 + 12);
// draw title
u8g2.setFont(u8g2_font_wqy14_t_gb2312b);
u8g2.drawUTF8(32, TITLEHEIGHT * 16 - 1, menuTitle[0]); // 横坐标根据标题长度调整
// draw menu
for (int i = topItemIndex; i < MENUITEMCOUNT; i++) {
u8g2.drawUTF8(leftMargin, itemPos[i], menu[i]);
}
u8g2.sendBuffer();
// reset the timer
idelTimer = millis();
// disable the display referesh
updateDisplay = false;
}
itemSelected = arrowPos + topItemIndex;
// sleep timer
if (millis() - idelTimer > MAXIDELTIME) {
u8g2.setPowerSave(1);
attachInterrupt(
digitalPinToInterrupt(WAKEUP_PIN), []() {}, FALLING);
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, LOW);
esp_deep_sleep_start();
}
}