|
最近做的小应用需要使用0.96oled屏显示,旋转编码器操作的汉字菜单。在百度上搜了一圈也没有啥合适的例程。
还好用Bing找到了一个英文的,https://github.com/alanesq/BasicOLEDMenu 测了一下挺好的,顺便用U8g2_for_Adafruit_GFX进行了一下汉化。贴出来大家参考一下。
在本社区潜水多年,第一次发帖,时间仓促,水平有限,请凑合着看,不足之处还望见谅。
测试硬件包括:
- NodeMCU32板子
- 0.96oled12864显示屏
- EC11旋转编码器
- 杜邦线
测试硬件
不废话了,直接上代码:
- /**************************************************************************************************
- *原版:
- * 在esp32上通过OLED屏实现简单的非阻塞模式菜单系统 - i2c版本SSD1306 - 2022年3月30日
- *
- * 转自: https://github.com/alanesq/BasicOLEDMenu
- *
- *
- **************************************************************************************************
- 概述,在oled上显示一个菜单,当选择一个项目时,它会设置一个标志并等待事件发生。128x64 oled上的最大菜单项为四个。
- 注意: 字号1 = 21 x 8 0.96oLED小屏幕使用英文显示的字符
- 字号2 = 10 x 4
- 更多有关oled的信息 参见: https://randomnerdtutorials.com/guide-for-oled-display-with-arduino/
- 有关如何使用菜单的示例,请参见“此处下方的菜单代码”部分
- 注意:如果显示屏上出现垃圾,设备锁定等,可能是旋转编码器接触不良
- **************************************************************************************************/
- /**************************************************************************************************
- *汉化版:
- * 在esp32、esp8266上通过OLED屏实现简单的非阻塞模式汉字菜单系统 - i2c版本SSD1306 - 2022年6月24日汉化改写
- * 使用旋转编码器进行操作
- **************************************************************************************************
- 概述,在原版基础上对代码进行了部分改写,主要是使用U8g2_for_Adafruit_GFX库对菜单显示进行了汉化,顺便汉化了代码注释。
- 注: 字号1 = 12t汉字
- 字号2 = 14t汉字
- 实际上14t汉字在视觉上比12t大的有限,反而占用了不少程序空间。以esp32为例,使用12t和14t两种字体占用程序空间40%,而只使用12t一种字体占用29%。
- **************************************************************************************************/
- #include <Arduino.h> // platformIO需要
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #include <U8g2_for_Adafruit_GFX.h> //一套基于U8g2字体引擎来通过Adafruit GFX来显示文字的第三方库。
- // ----------------------------------------------------------------
- // 设置
- // ----------------------------------------------------------------
- // esp32 NodeMcu32S
- #define encoder0PinA 32 // 旋转编码器CLK引脚 - 32
- #define encoder0PinB 33 // 旋转编码器DT引脚 - 33
- #define encoder0Press 25 // 旋转编码器SW引脚(按钮)- 25
- #define OLEDC 22 // oled屏clock引脚(设成 -1 表示使用缺省值) - 22
- #define OLEDD 21 // oled屏data引脚 - 21
- #define OLEDE -1 // oled屏enable引脚(设成 -1 表示未使用)
- // oLED
- #define OLED_ADDR 0x3C // OLED i2c 地址
- #define SCREEN_WIDTH 128 // OLED显示宽度,以像素表示(通常是128)
- #define SCREEN_HEIGHT 64 // OLED显示高度,以像素表示(0.96oLED小屏幕通常是64)
- #define OLED_RESET -1 // Reset引脚 (-1表示和Arduino板子共享reset引脚)
- // 其他
- const int serialDebug = 1;
- const int iLED = 16; // 板载led灯引脚
- #define BUTTONPRESSEDSTATE 0 // 旋转编码器按钮按下的逻辑值(通常是0)
- #define DEBOUNCEDELAY 100 // 按钮输入消抖
- const int menuTimeout = 10; // 菜单非活动超时(秒)
- const bool menuLargeText = 1; // 尽可能显示较大的文字(如果难以阅读较小的文字)
- const int maxmenuItems = 12; // 所有菜单中使用的最大项目数(尽可能低以节省内存)
- const int itemTrigger = 1; // 旋转编码器每个刻度的计数(在编码器转动中变化,通常为1或2)
- const int topLine = 18; // 显示器下部区域的纵向(y)起始位置(两色分色屏通常是18)
- const byte lineSpace1 = 12; // 字号1的行距(正文,小字)
- const byte lineSpace2 = 14; // 字号2的行距(标题,大字)
- const int displayMaxLines = 4; // 使用字号1时oLED屏下部可显示的最大行数(通常0.96oLED小屏幕使用英文为5)
- const int MaxmenuTitleLength = 20; // 使用字号2(标题)时,每行最多字符数(通常为英文为10)
-
- // -------------------------------------------------------------------------------------------------
- #define FONT12 u8g2_font_wqy12_t_gb2312b //字号1
- #define FONT14 u8g2_font_wqy14_t_gb2312b //字号2
- //--------------------------------------------------------------------------------------------------
- // 提前声明函数 - PlatformIO需要
- void ICACHE_RAM_ATTR doEncoder();
- void demoMenu();
- void menuActions();
- void value_non();
- void menuValues();
- void reUpdateButton();
- void serviceMenu();
- int serviceValue(bool _blocking);
- void createList(String _title, int _noOfElements, String *_list);
- void displayMessage(String _title, String _message);
- void resetMenu();
- // 菜单系统可以处于的模式
- enum menuModes {
- off, // 息屏
- menu, // 菜单处于活动状态
- value, // “输入某值”非阻塞,处于活动状态
- message, // 显示信息
- blocking // 正在执行阻塞过程(请参见输入值)
- };
- menuModes menuMode = off; // 启动时的默认模式为关闭
- struct oledMenus {
- // 菜单
- String menuTitle = ""; // 活动模式时的标题
- int noOfmenuItems = 0; // 活动菜单中的菜单项编号
- int selectedMenuItem = 0; // 选择菜单项时,将在此处标记该菜单项,直到操作并清除为止
- int highlightedMenuItem = 0; // 菜单中当前突出显示哪个项目
- String menuItems[maxmenuItems+1]; // 存储菜单项标题
- uint32_t lastMenuActivity = 0; // 菜单上次见到有所活动的时间(用于超时)
- // '数值输入'
- int mValueEntered = 0; // 存储输入菜单输入的数字
- int mValueLow = 0; // 最低允许值
- int mValueHigh = 0; // 最高允许值
- int mValueStep = 0; // 转动旋转编码器时的步长
- };
- oledMenus oledMenu; // 定义oledMenus类
- struct rotaryEncoders {
- volatile int encoder0Pos = 0; // 使用旋转编码器选择的当前值(通过中断例程更新)
- volatile bool encoderPrevA; // 用于旋转编码器消抖
- volatile bool encoderPrevB; // 用于旋转编码器消抖
- uint32_t reLastButtonChange = 0; // 上次更改按钮状态的时间(用于消抖)
- bool encoderPrevButton = 0; // 用于按钮消抖
- int reButtonDebounced = 0; // 消抖后当前按钮状态(按下时为1)
- const bool reButtonPressedState = BUTTONPRESSEDSTATE; // 按钮按下的逻辑值
- const uint32_t reDebounceDelay = DEBOUNCEDELAY; // 按钮消抖的延迟设置
- bool reButtonPressed = 0; // 按下按钮时设置的标志(必须手动重置)
- };
- rotaryEncoders rotaryEncoder;
- // oled SSD1306 通过I2C方式连接进行显示
- Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
- U8G2_FOR_ADAFRUIT_GFX u8g2_for_adafruit_gfx;
- // -------------------------------------------------------------------------------------------------
- // 以下为菜单代码
- // -------------------------------------------------------------------------------------------------
- // 启动缺省菜单
- void defaultMenu() {
- demoMenu();
- }
- //演示菜单1
- void menu1(){
- displayMessage("演示菜单2", "汉字显示\n可以换成其他代码\nOK!"); // 每行21个英文字符,10个12_t的汉字"\n" = 下一行
- }
- //演示菜单2
- void menu2(){
- resetMenu();
- menuMode = menu;
- displayMessage("演示菜单2", "汉字显示\n中文测试通过!\nOK!");
- }
- // -----------------------------------------------
- // 演示菜单
- void demoMenu() {
- resetMenu(); // 清除任何以前的菜单
- menuMode = menu; // 启用菜单模式
- oledMenu.noOfmenuItems = 8; // 设置此菜单中的项目数
- oledMenu.menuTitle = "菜单系统"; // 菜单标题(用于标识)
- oledMenu.menuItems[1] = "测试菜单"; // 设置菜单项
- oledMenu.menuItems[2] = "菜单验证";
- oledMenu.menuItems[3] = "快速菜单";
- oledMenu.menuItems[4] = "开或关";
- oledMenu.menuItems[5] = "非阻塞输入值";
- oledMenu.menuItems[6] = "阻塞式输入值";
- oledMenu.menuItems[7] = "消息";
- oledMenu.menuItems[8] = "关闭菜单";
- } // demoMenu
- // 菜单选择的操作放在此处
- void menuActions() {
- // 在"demo_menu"中选择项目时的操作
- if (oledMenu.menuTitle == "菜单系统") {
- // 演示菜单1
- if (oledMenu.selectedMenuItem == 1) {
- resetMenu();
- menuMode = menu;
- menu1();
- }
-
- // 演示菜单2
- if (oledMenu.selectedMenuItem == 2) {
- menu2();
- }
- // 演示如何从列表中快速创建菜单
- if (oledMenu.selectedMenuItem == 3) {
- if (serialDebug) Serial.println("demo_menu: create menu from a list");
- String tList[]={"子菜单0", "子菜单1", "子菜单2", "子菜单3", "子菜单4", "子菜单5"};
- createList("快速菜单", 6, &tList[0]);
- }
- // 演示仅在2个选项之间进行选择
- if (oledMenu.selectedMenuItem == 4) {
- resetMenu();
- menuMode = value;
- oledMenu.menuTitle = "开或关";
- oledMenu.mValueLow = 0;
- oledMenu.mValueHigh = 1;
- oledMenu.mValueStep = 1;
- oledMenu.mValueEntered = 0; // 设置参数
- }
- // 演示“数值输入”的用法(非阻塞)
- if (oledMenu.selectedMenuItem == 5) {
- if (serialDebug) Serial.println("demo_menu: none blocking enter value");
- resetMenu();
- value_non(); // 输入一个值
- }
- // 演示“输入值”(阻塞)的用法,该方法简单快捷,但在输入值完成之前会停止所有其他的任务
- if (oledMenu.selectedMenuItem == 6) {
- if (serialDebug) Serial.println("demo_menu: blocking enter a value");
- // set perameters
- resetMenu();
- menuMode = value;
- oledMenu.menuTitle = "阻塞式数值输入";
- oledMenu.mValueLow = 0;
- oledMenu.mValueHigh = 50;
- oledMenu.mValueStep = 1;
- oledMenu.mValueEntered = 5;
- int tEntered = serviceValue(1); // 需求值
- Serial.println("The value entered was " + String(tEntered));
- defaultMenu();
- }
- // 演示显示消息的用法
- if (oledMenu.selectedMenuItem == 7) {
- if (serialDebug) Serial.println("demo_menu: message");
- displayMessage("这是个消息", "汉字显示\n是不是正常?"); // 每行21个英文字符,10个12_t的汉字"\n" = 下一行
- }
- // 关闭菜单/oLED息屏
- else if (oledMenu.selectedMenuItem == 8) {
- if (serialDebug) Serial.println("demo_menu: menu off");
- resetMenu(); // 关闭菜单
- }
- oledMenu.selectedMenuItem = 0; // 清除菜单项选定标志
- }
- // 在demo_list菜单中选择项目时的操作
- if (oledMenu.menuTitle == "快速菜单") {
- // 返回主菜单
- if (oledMenu.selectedMenuItem == 1) {
- if (serialDebug) Serial.println("demo_list: back to main menu");
- defaultMenu();
- }
- if (oledMenu.selectedMenuItem == 2) {
- if (serialDebug) Serial.println("demo_list: back to main menu2222222222222");
- defaultMenu();
- }
- oledMenu.selectedMenuItem = 0; // 清除菜单项选定标志
- }
- } // menuActions
- // -----------------------------------------------
- // 演示数值的输入
- void value_non() {
- resetMenu(); // 清除任何以前的菜单
- menuMode = value; // 启用数值输入模式
- oledMenu.menuTitle = "非阻塞数值输入"; // 标题(用于标识输入的编号)
- oledMenu.mValueLow = 0; // 允许的最小值
- oledMenu.mValueHigh = 100; // 允许的最大值
- oledMenu.mValueStep = 1; // 步长
- oledMenu.mValueEntered = 50; // 起始值
- }
- //--------------------------------------------------------------------------------------
- // 此处为数值输入的操作
- void menuValues() {
- // "demo_value"的操作
- if (oledMenu.menuTitle == "非阻塞数值输入") {
- String tString = String(oledMenu.mValueEntered);
- if (serialDebug) Serial.println("demo_value: The value entered was " + tString);
- displayMessage("输入完成", "\n您输入的值为:\n " + tString);
- // 另外,在此处也可以使用“resetMenu()”在输入值后关闭菜单,或者使用“defaultMenu()”重新启动默认菜单
- }
- // “开或关”操作
- if (oledMenu.menuTitle == "开或关") {
- if (serialDebug) Serial.println("demo_menu: on off selection was " + String(oledMenu.mValueEntered));
- defaultMenu();
- }
- }
- // -------------------------------------------------------------------------------------------------
- // 以上为菜单代码
- // -------------------------------------------------------------------------------------------------
- // ----------------------------------------------------------------
- // -setup
- // ----------------------------------------------------------------
- // 从主设置调用
- void setup() {
- Serial.begin(115200); while (!Serial); delay(50); // 启动串行通信
- Serial.println("\n\n\nStarting menu demo\n");
- pinMode(iLED, OUTPUT); // 板载led指示灯
- // 配置旋转编码器的gpio引脚
- pinMode(encoder0Press, INPUT_PULLUP);
- pinMode(encoder0PinA, INPUT);
- pinMode(encoder0PinB, INPUT);
- // 初始化oled显示屏
- // 启用引脚
- if (OLEDE != 0) {
- pinMode(OLEDE , OUTPUT);
- digitalWrite(OLEDE, HIGH);
- }
- if (0 == OLEDC) Wire.begin();
- else Wire.begin(OLEDD, OLEDC);
- if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
- if (serialDebug) Serial.println(("\nError initialising the oled display"));
- }
- //---------------------------------------------------------------------------------------------
- u8g2_for_adafruit_gfx.begin(display); // 将u8g2程序连接到Adafruit GFX
- u8g2_for_adafruit_gfx.setFont(FONT12); // 将字体设为u8g2_font_wqy12_t_gb2312b
- //---------------------------------------------------------------------------------------------
- // 读取旋转编码器位置的中断
- rotaryEncoder.encoder0Pos = 0;
- attachInterrupt(digitalPinToInterrupt(encoder0PinA), doEncoder, CHANGE);
- //defaultMenu(); // 启动默认菜单
- // 显示问候语-按下按钮将进入“开始”菜单
- displayMessage("欢迎使用", "汉字菜单系统\n测试版V1.0");
- }
- // ----------------------------------------------------------------
- // -loop
- // ----------------------------------------------------------------
- // 从主循环调用
- void loop() {
- reUpdateButton(); // 更新旋转编码器按钮状态(如果按下激活默认菜单)
- menuUpdate(); // 更新或操作oled菜单
-
- // 闪烁板载led
- static uint32_t ledTimer = millis();
- if ( (unsigned long)(millis() - ledTimer) > 1000 ) {
- digitalWrite(iLED, !digitalRead(iLED));
- ledTimer = millis();
- }
- } // oledLoop
- // ----------------------------------------------------------------
- // -按钮消抖 (旋转编码器)
- // ----------------------------------------------------------------
- // 更新旋转编码器当前按钮状态
- void reUpdateButton() {
- bool tReading = digitalRead(encoder0Press); // 读取当前按钮状态
- if (tReading != rotaryEncoder.encoderPrevButton) rotaryEncoder.reLastButtonChange = millis(); // 如果已更改重置计时器
- if ( (unsigned long)(millis() - rotaryEncoder.reLastButtonChange) > rotaryEncoder.reDebounceDelay ) { // 如果按钮状态稳定
- if (rotaryEncoder.encoderPrevButton == rotaryEncoder.reButtonPressedState) {
- if (rotaryEncoder.reButtonDebounced == 0) { // 如果按钮已按下
- rotaryEncoder.reButtonPressed = 1; // 按下按钮时设置的标志
- if (menuMode == off) defaultMenu(); // 如果显示屏关闭,则启动默认菜单
- }
- rotaryEncoder.reButtonDebounced = 1; // 消抖后按钮状态(按下时为1)
- } else {
- rotaryEncoder.reButtonDebounced = 0;
- }
- }
- rotaryEncoder.encoderPrevButton = tReading; // 更新上次读取的状态
- }
- // ----------------------------------------------------------------
- // -更新活动菜单
- // ----------------------------------------------------------------
- void menuUpdate() {
- if (menuMode == off) return; // 如果菜单系统已关闭,不要执行其他操作
- // 如果最近没有活动,则关闭oled
- if ( (unsigned long)(millis() - oledMenu.lastMenuActivity) > (menuTimeout * 1000) ) {
- resetMenu();
- return;
- }
- switch (menuMode) {
- // 如果有活动的菜单
- case menu:
- serviceMenu();
- menuActions();
- break;
- // 如果有活动且非阻塞的“数值输入”
- case value:
- serviceValue(0);
- if (rotaryEncoder.reButtonPressed) { // 如果按钮已按下
- menuValues(); // 已输入一个值,对其进行操作
- break;
- }
- // 如果正在显示消息
- case message:
- if (rotaryEncoder.reButtonPressed == 1) defaultMenu(); // 如果按下按钮,返回默认菜单
- break;
- }
- }
- // ----------------------------------------------------------------
- // -活动菜单处理
- // ----------------------------------------------------------------
- void serviceMenu() {
- // 旋转编码器
- if (rotaryEncoder.encoder0Pos >= itemTrigger) {
- rotaryEncoder.encoder0Pos -= itemTrigger;
- oledMenu.highlightedMenuItem++;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- if (rotaryEncoder.encoder0Pos <= -itemTrigger) {
- rotaryEncoder.encoder0Pos += itemTrigger;
- oledMenu.highlightedMenuItem--;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- if (rotaryEncoder.reButtonPressed == 1) {
- oledMenu.selectedMenuItem = oledMenu.highlightedMenuItem; // 已选择项目的标志
- oledMenu.lastMenuActivity = millis(); // log time
- if (serialDebug) Serial.println("menu '" + oledMenu.menuTitle + "' item '" + oledMenu.menuItems[oledMenu.highlightedMenuItem] + "' selected");
- }
- const int _centreLine = displayMaxLines / 2 -1; // 列表中间位置
- display.clearDisplay();
- display.setTextColor(WHITE);
- // 验证有效的突出显示项
- if (oledMenu.highlightedMenuItem > oledMenu.noOfmenuItems) oledMenu.highlightedMenuItem = oledMenu.noOfmenuItems;
- if (oledMenu.highlightedMenuItem < 1) oledMenu.highlightedMenuItem = 1;
- // 标题
- //display.setCursor(0, 0);
- u8g2_for_adafruit_gfx.setCursor(0,14);
- u8g2_for_adafruit_gfx.setFont(FONT14);
- /*if (menuLargeText) {
- display.setTextSize(2);
- display.println(oledMenu.menuItems[oledMenu.highlightedMenuItem].substring(0, MaxmenuTitleLength));
- } else {
- if (oledMenu.menuTitle.length() > MaxmenuTitleLength) display.setTextSize(1);
- else display.setTextSize(2);
- display.println(oledMenu.menuTitle);
- }*/
- u8g2_for_adafruit_gfx.println(oledMenu.menuTitle);
- display.drawLine(0, topLine-1, display.width(), topLine-1, WHITE); // 在标题下画水平线
- // 菜单
- // display.setTextSize(1);
- // display.setCursor(0, topLine);
- u8g2_for_adafruit_gfx.setFont(FONT12);
- u8g2_for_adafruit_gfx.setCursor(0,topLine+12);
- /*for (int i=1; i <= displayMaxLines; i++) {
- int item = oledMenu.highlightedMenuItem - _centreLine + i;
- if (item == oledMenu.highlightedMenuItem) display.setTextColor(BLACK, WHITE);
- else display.setTextColor(WHITE);
- if (item > 0 && item <= oledMenu.noOfmenuItems) display.println(oledMenu.menuItems[item]);
- else display.println(" ");*/
- for (int i=1; i <= displayMaxLines; i++) {
- int item = oledMenu.highlightedMenuItem - _centreLine + i;
- if (item == oledMenu.highlightedMenuItem)
- {
- u8g2_for_adafruit_gfx.setFontMode(0);
- u8g2_for_adafruit_gfx.setForegroundColor(BLACK);
- u8g2_for_adafruit_gfx.setBackgroundColor(WHITE);
- }
- else {
- u8g2_for_adafruit_gfx.setFontMode(0);
- u8g2_for_adafruit_gfx.setForegroundColor(WHITE);
- u8g2_for_adafruit_gfx.setBackgroundColor(BLACK);
- }
- if (item > 0 && item <= oledMenu.noOfmenuItems) {
- u8g2_for_adafruit_gfx.println(oledMenu.menuItems[item]);
-
- }
- else display.println(" ");
-
- }
- //// 如何在菜单屏幕上显示一些更新信息
- // display.setCursor(80, 25);
- // display.println(millis());
-
- display.display();
- }
- // ----------------------------------------------------------------
- // -数值输入处理
- // ----------------------------------------------------------------
- // 如果阻塞设置为1,则所有其他任务都将停止,直到输入数值为止
- int serviceValue(bool _blocking) {
- const int _valueSpacingX = 30; // 显示的数值X位置的间距
- const int _valueSpacingY = 5; // 显示的数值Y位置的间距
- if (_blocking) {
- menuMode = blocking;
- oledMenu.lastMenuActivity = millis(); // 上次活动的日志记录时间(超时)
- }
- uint32_t tTime;
- do {
- // 旋转编码器
- if (rotaryEncoder.encoder0Pos >= itemTrigger) {
- rotaryEncoder.encoder0Pos -= itemTrigger;
- oledMenu.mValueEntered-= oledMenu.mValueStep;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- if (rotaryEncoder.encoder0Pos <= -itemTrigger) {
- rotaryEncoder.encoder0Pos += itemTrigger;
- oledMenu.mValueEntered+= oledMenu.mValueStep;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- if (oledMenu.mValueEntered < oledMenu.mValueLow) {
- oledMenu.mValueEntered = oledMenu.mValueLow;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- if (oledMenu.mValueEntered > oledMenu.mValueHigh) {
- oledMenu.mValueEntered = oledMenu.mValueHigh;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- }
- display.clearDisplay();
- display.setTextColor(WHITE);
- // 标题
- //display.setCursor(0, 0);
- u8g2_for_adafruit_gfx.setCursor(0,14);
- // if (oledMenu.menuTitle.length() > MaxmenuTitleLength) display.setTextSize(1);
- // else display.setTextSize(2);
- u8g2_for_adafruit_gfx.setFont(FONT14);
- u8g2_for_adafruit_gfx.print(oledMenu.menuTitle);
- // display.println(oledMenu.menuTitle);
- display.drawLine(0, topLine-1, display.width(), topLine-1, WHITE); // 在标题下画水平线
- // 选择的数值
- display.setCursor(_valueSpacingX, topLine + _valueSpacingY);
- display.setTextSize(3);
- display.println(oledMenu.mValueEntered);
-
-
- // 范围
- display.setCursor(0, display.height() - lineSpace1 - 1 ); // 显示区域底部
- display.setTextSize(1);
- display.println(String(oledMenu.mValueLow) + " to " + String(oledMenu.mValueHigh));
- // 状态条
- int Tlinelength = map(oledMenu.mValueEntered, oledMenu.mValueLow, oledMenu.mValueHigh, 0 , display.width());
- display.drawLine(0, display.height()-1, Tlinelength, display.height()-1, WHITE);
- display.display();
- reUpdateButton(); // 检查按钮状态
- tTime = (unsigned long)(millis() - oledMenu.lastMenuActivity); // 自上次活动以来的时间
- } while (_blocking && rotaryEncoder.reButtonPressed == 0 && tTime < (menuTimeout * 1000)); // 如果处于阻塞模式,重复此操作,直到按下按钮或超时
- if (_blocking) menuMode = off;
- return oledMenu.mValueEntered; // 在阻塞模式下使用
- }
- // ----------------------------------------------------------------
- // -列表的创建
- // ----------------------------------------------------------------
- // 更新以前的读数从列表中创建菜单
- // 例如: String tList[]={"main menu", "2", "3", "4", "5", "6"};
- // createList("demo_list", 6, &tList[0]);
- void createList(String _title, int _noOfElements, String *_list) {
- resetMenu(); // 清除任何以前的菜单
- menuMode = menu; // 启用菜单模式
- oledMenu.noOfmenuItems = _noOfElements; // 设置此菜单中的项目数
- oledMenu.menuTitle = _title; // 菜单标题(用于标识)
- for (int i=1; i <= _noOfElements; i++) {
- oledMenu.menuItems[i] = _list[i-1]; // 设置菜单项
- }
- }
- // ----------------------------------------------------------------
- // -消息显示
- // ----------------------------------------------------------------
- // 每行21个字符,"\n" = 下一行
- // 例如: < 行1 >< 行2 >< 行3 >< 行4 >
- void displayMessage(String _title, String _message) {
- resetMenu();
- menuMode = message;
- display.clearDisplay();
- //display.setTextColor(WHITE);
- u8g2_for_adafruit_gfx.setFontMode(1); // 使用u8g2透明模式 (缺省)
- u8g2_for_adafruit_gfx.setFontDirection(0); // 文字方向从左到右 (缺省)
-
- u8g2_for_adafruit_gfx.setForegroundColor(WHITE);
- // 标题
- //display.setCursor(0, 0);
- u8g2_for_adafruit_gfx.setCursor(0,14);
- if (menuLargeText) {
- //display.setTextSize(2);
- u8g2_for_adafruit_gfx.setFont(FONT14);
- //display.println(_title.substring(0, MaxmenuTitleLength));
- u8g2_for_adafruit_gfx.println(_title.substring(0, MaxmenuTitleLength));
- } else {
- if (_title.length() > MaxmenuTitleLength)
- //display.setTextSize(1);
- u8g2_for_adafruit_gfx.setFont(FONT12);
- else
- //display.setTextSize(2);
- u8g2_for_adafruit_gfx.setFont(FONT14);
- //display.println(_title);
- u8g2_for_adafruit_gfx.println(_title);
- }
- display.drawLine(0, topLine-1, display.width(), topLine-1, WHITE); // 在标题下画水平线
- // 消息
- //display.setCursor(0, topLine);
- u8g2_for_adafruit_gfx.setCursor(0,topLine+12);
- //display.setTextSize(1);
- u8g2_for_adafruit_gfx.setFont(FONT12);
- //display.println(_message);
- u8g2_for_adafruit_gfx.println(_message);
- display.display();
- }
- // ----------------------------------------------------------------
- // -重置菜单系统
- // ----------------------------------------------------------------
- void resetMenu() {
- // 重置所有菜单变量/标志
- menuMode = off;
- oledMenu.selectedMenuItem = 0;
- rotaryEncoder.encoder0Pos = 0;
- oledMenu.noOfmenuItems = 0;
- oledMenu.menuTitle = "";
- oledMenu.highlightedMenuItem = 0;
- oledMenu.mValueEntered = 0;
- rotaryEncoder.reButtonPressed = 0;
- oledMenu.lastMenuActivity = millis(); // 日志记录时间
- // 清除oled显示屏
- display.clearDisplay();
- display.display();
- }
- // ----------------------------------------------------------------
- // -旋转编码器中断
- // ----------------------------------------------------------------
- // 旋转编码器中断程序,用于在旋转时更新位置计数器
- // 中断参考资料: https://www.gammon.com.au/forum/bbshowpost.php?id=11488
- void ICACHE_RAM_ATTR doEncoder() {
- bool pinA = digitalRead(encoder0PinA);
- bool pinB = digitalRead(encoder0PinB);
- if ( (rotaryEncoder.encoderPrevA == pinA && rotaryEncoder.encoderPrevB == pinB) ) return; // 自上次以来没有变化
- // 同一方向(在一个方向上介于0,1和1,0之间,或在另一个方向上介于1,1和0,0之间)
- if (rotaryEncoder.encoderPrevA == 1 && rotaryEncoder.encoderPrevB == 0 && pinA == 0 && pinB == 1) rotaryEncoder.encoder0Pos -= 1;
- else if (rotaryEncoder.encoderPrevA == 0 && rotaryEncoder.encoderPrevB == 1 && pinA == 1 && pinB == 0) rotaryEncoder.encoder0Pos -= 1;
- else if (rotaryEncoder.encoderPrevA == 0 && rotaryEncoder.encoderPrevB == 0 && pinA == 1 && pinB == 1) rotaryEncoder.encoder0Pos += 1;
- else if (rotaryEncoder.encoderPrevA == 1 && rotaryEncoder.encoderPrevB == 1 && pinA == 0 && pinB == 0) rotaryEncoder.encoder0Pos += 1;
- // 改变方向
- else if (rotaryEncoder.encoderPrevA == 1 && rotaryEncoder.encoderPrevB == 0 && pinA == 0 && pinB == 0) rotaryEncoder.encoder0Pos += 1;
- else if (rotaryEncoder.encoderPrevA == 0 && rotaryEncoder.encoderPrevB == 1 && pinA == 1 && pinB == 1) rotaryEncoder.encoder0Pos += 1;
- else if (rotaryEncoder.encoderPrevA == 0 && rotaryEncoder.encoderPrevB == 0 && pinA == 1 && pinB == 0) rotaryEncoder.encoder0Pos -= 1;
- else if (rotaryEncoder.encoderPrevA == 1 && rotaryEncoder.encoderPrevB == 1 && pinA == 0 && pinB == 1) rotaryEncoder.encoder0Pos -= 1;
- //else if (serialDebug) Serial.println("Error: invalid rotary encoder pin state - prev=" + String(rotaryEncoder.encoderPrevA) + ","
- // + String(rotaryEncoder.encoderPrevB) + " new=" + String(pinA) + "," + String(pinB));
- // 更新以前的读数
- rotaryEncoder.encoderPrevA = pinA;
- rotaryEncoder.encoderPrevB = pinB;
- }
- // ---------------------------------------------- 结束 ----------------------------------------------
复制代码 原版代码:
BasicOLEDMenu-main.zip
(901.85 KB, 下载次数: 7)
|
|