U8g2库为XFP1116-07AY(128x64 OLED)实现菜单功能[ep:esp8266]

使用U8g2库为XFP1116-07AY(128x64 OLED)实现菜单功能,核心是通过按键控制菜单切换光标移动选项选中,结合U8g2的绘图/文本函数实现交互逻辑支持多级菜单(主菜单→子菜单→功能执行),并兼容ESP8266的按键输入。

一、核心思路

  1. 菜单结构设计:采用“数组+索引”管理菜单(主菜单包含多个选项,部分选项跳转至子菜单);
  2. 交互控制:通过2个按键(上/下移动光标、确认进入子菜单/执行功能);
  3. 显示逻辑:每次按键后清空缓冲区,重新绘制当前菜单和光标位置;
  4. 状态保存:记录当前菜单层级和光标位置,确保切换不丢失状态。

二、硬件接线

组件ESP8266引脚说明
上移按键D5一端接D5,一端接GND(下拉)
下移按键D6一端接D6,一端接GND(下拉)
确认按键D7一端接D7,一端接GND(下拉)
XFP1116-07AYSDA=D2、SCL=D1OLED的I2C引脚(不变)

三、完整代码(多级菜单+按键控制)

#include <Wire.h>
#include <U8g2lib.h>// 1. 初始化U8g2(适配XFP1116-07AY:SH1106控制器,128x64,I2C引脚D2=SDA、D1=SCL)
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, D1, D2);// 2. 定义菜单结构(菜单层级:0=主菜单,1=子菜单1,2=子菜单2)
// 菜单选项格式:{选项名称, 子菜单层级(-1=无子类,执行功能)}
const struct MenuItem {const char* name;    // 选项文字int subMenuLevel;    // 子菜单层级(-1=执行功能,0=主菜单,1=子菜单1,2=子菜单2)
} menuList[3][4] = {// 主菜单(层级0):4个选项{{"1. 系统设置", 1},    // 进入子菜单1(系统设置){"2. 显示控制", 2},    // 进入子菜单2(显示控制){"3. 关于设备", -1},   // 无子类,执行“关于”功能{"4. 退出", -1}        // 无子类,执行“退出”功能},// 子菜单1(系统设置,层级1):3个选项{{"1.1 亮度调节", -1},  // 执行“亮度调节”{"1.2 恢复默认", -1},  // 执行“恢复默认”{"1.3 返回上一级", 0}, // 返回主菜单(层级0){NULL, -1}             // 占位符(无更多选项)},// 子菜单2(显示控制,层级2):3个选项{{"2.1 字体切换", -1},  // 执行“字体切换”{"2.2 清屏测试", -1},  // 执行“清屏测试”{"2.3 返回上一级", 0}, // 返回主菜单(层级0){NULL, -1}             // 占位符}
};// 3. 菜单状态变量(记录当前状态)
int currentMenuLevel = 0;  // 当前菜单层级(默认主菜单0)
int currentCursor = 0;     // 当前光标位置(默认第1个选项)
int menuItemCount[3] = {4, 3, 3}; // 各层级的选项数量(主菜单4个,子菜单1/2各3个)// 4. 按键引脚定义
const int KEY_UP = D5;
const int KEY_DOWN = D6;
const int KEY_CONFIRM = D7;// 5. 函数声明(提前声明,避免编译错误)
void drawMenu();          // 绘制当前菜单和光标
void handleKeyInput();    // 处理按键输入
void executeMenuAction(); // 执行选中选项的功能void setup() {// 初始化OLEDu8g2.begin();u8g2.enableUTF8Print(); // 启用UTF8(支持中文)u8g2.setFont(u8g2_font_wqy12_t_gb2312); // 中文支持字体// 初始化按键引脚(下拉输入:按键未按则为高电平,按下为低电平)pinMode(KEY_UP, INPUT_PULLUP);pinMode(KEY_DOWN, INPUT_PULLUP);pinMode(KEY_CONFIRM, INPUT_PULLUP);// 初始绘制主菜单drawMenu();
}void loop() {handleKeyInput(); // 持续检测按键delay(100);       // 消抖,避免按键误触发
}// 绘制当前菜单:标题+选项+光标
void drawMenu() {u8g2.clearBuffer(); // 清空缓冲区// 1. 绘制菜单标题(不同层级显示不同标题)u8g2.setCursor(0, 15); // 标题位置(y=15,避免顶部裁切)switch (currentMenuLevel) {case 0: u8g2.print("主菜单"); break;case 1: u8g2.print("系统设置"); break;case 2: u8g2.print("显示控制"); break;}u8g2.drawHLine(0, 20, 128); // 标题下方画一条横线(分隔标题和选项)// 2. 绘制当前菜单的所有选项(从y=35开始,每个选项间隔18像素)for (int i = 0; i < menuItemCount[currentMenuLevel]; i++) {int yPos = 35 + i * 18; // 选项y坐标(间隔18像素,适配12号字体)u8g2.setCursor(10, yPos); // 选项左移10像素,避免贴边// 光标位置:当前选中的选项前加“> ”标记if (i == currentCursor) {u8g2.print("> "); // 光标符号} else {u8g2.print("  "); // 非选中项留空,对齐格式}// 绘制选项文字u8g2.print(menuList[currentMenuLevel][i].name);}u8g2.sendBuffer(); // 刷新屏幕,显示菜单
}// 处理按键输入:上移、下移、确认
void handleKeyInput() {// 上移按键(按下时电平为LOW)if (digitalRead(KEY_UP) == LOW) {delay(50); // 消抖(避免按键抖动导致多次触发)if (digitalRead(KEY_UP) == LOW) {currentCursor--; // 光标上移// 边界处理:光标到顶部后,循环到最后一个选项if (currentCursor < 0) {currentCursor = menuItemCount[currentMenuLevel] - 1;}drawMenu(); // 重新绘制菜单// 等待按键释放(避免长按连续触发)while (digitalRead(KEY_UP) == LOW);}}// 下移按键if (digitalRead(KEY_DOWN) == LOW) {delay(50);if (digitalRead(KEY_DOWN) == LOW) {currentCursor++; // 光标下移// 边界处理:光标到顶部后,循环到第一个选项if (currentCursor >= menuItemCount[currentMenuLevel]) {currentCursor = 0;}drawMenu();while (digitalRead(KEY_DOWN) == LOW);}}// 确认按键(进入子菜单或执行功能)if (digitalRead(KEY_CONFIRM) == LOW) {delay(50);if (digitalRead(KEY_CONFIRM) == LOW) {executeMenuAction(); // 执行当前选中选项的逻辑while (digitalRead(KEY_CONFIRM) == LOW);}}
}// 执行当前选中选项的功能(进入子菜单或显示功能提示)
void executeMenuAction() {// 获取当前选中选项的“子菜单层级”int targetLevel = menuList[currentMenuLevel][currentCursor].subMenuLevel;if (targetLevel != -1) {// 情况1:有子菜单,切换到目标层级,光标重置为0currentMenuLevel = targetLevel;currentCursor = 0;drawMenu(); // 绘制子菜单} else {// 情况2:无子类,执行对应功能(显示提示信息2秒后返回当前菜单)u8g2.clearBuffer();u8g2.setCursor(10, 30); // 提示文字居中// 根据当前选项执行不同提示if (currentMenuLevel == 0) {switch (currentCursor) {case 2: u8g2.print("设备:XFP1116-07AY"); break; // 关于设备case 3: u8g2.print("已退出!"); break;          // 退出}} else if (currentMenuLevel == 1) {switch (currentCursor) {case 0: u8g2.print("亮度已调节为50%"); break;  // 亮度调节case 1: u8g2.print("已恢复默认设置"); break;   // 恢复默认}} else if (currentMenuLevel == 2) {switch (currentCursor) {case 0: u8g2.print("字体已切换为默认"); break; // 字体切换case 1: u8g2.clearBuffer(); u8g2.print("清屏测试中..."); break; // 清屏}}u8g2.sendBuffer(); // 显示提示delay(2000);       // 提示显示2秒drawMenu();        // 返回当前菜单}
}

四、代码说明

1. 菜单结构扩展

若需增加更多菜单层级或选项,只需:

  • menuList数组中新增层级(如menuList[3][...]作为“子菜单3”);
  • 同步更新menuItemCount数组(记录新层级的选项数量);
  • executeMenuAction()中添加新层级的功能逻辑。
2. 字体与显示优化
  • 若需更大字体,可替换u8g2.setFont()的参数(如u8g2_font_wqy16_t_gb2312),但需同步调整选项的yPos间隔(避免选项重叠);
  • 若需支持英文,可改用英文字体(如u8g2_font_ncenB12_tr),无需启用enableUTF8Print()
3. 按键优化
  • 若按键触发不灵敏,可调整delay(50)的消抖时间(如改为delay(30));
  • 若需支持“长按快速移动光标”,可在handleKeyInput()中添加长按检测逻辑(如判断按键按下时间超过500ms后,每100ms移动一次光标)。

五、运行效果

  1. 上电后显示“主菜单”,光标默认停在“1. 系统设置”;
  2. 按“上/下键”移动光标,光标前的“> ”标记跟随移动;
  3. 按“确认键”:
    • 选中“1. 系统设置”→ 进入“系统设置”子菜单;
    • 选中“3. 关于设备”→ 显示设备信息2秒后返回主菜单;
    • 选中“1.3 返回上一级”→ 从子菜单返回主菜单。

该代码可直接上传使用,也可根据实际需求(如增加参数调节、数据显示)扩展executeMenuAction()中的功能逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/100269.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/100269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

easy-dataset 框架综合技术分析:面向领域特定 LLM 指令数据的合成

摘要 本报告对 easy-dataset 框架 进行全面技术剖析&#xff0c;该框架旨在解决大型语言模型&#xff08;LLM&#xff09;在特定领域应用中的核心瓶颈——高质量指令微调数据的稀缺性。随着 LLM 技术发展&#xff0c;其应用能力不再仅依赖模型参数规模&#xff0c;而是更依赖通…

【开题答辩全过程】以 4s店汽车销售系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

测试中的Bug

文章目录软件测试的生命周期软件测试的各个阶段线上环境测试中的BUG描述测试BUGBUG的级别为啥要定义BUG的级别&#xff1f;BUG有哪些级别呢&#xff1f;BUG的生命周期测试与开发发生争执怎么办&#xff1f;测试与开发会发生啥争执&#xff1f;为啥会发生这样的争执&#xff1f;…

aws共享一个镜像并有画图功能

这样可以方便的把系统安装好&#xff0c;不会重复劳动了。 这个是frequi 单独安装 wget https://github.com/freqtrade/frequi/releases/download/2.0.7/freqUI.zip freqtrade install-ui pip install -U -r requirements-plot.txt 在AWS上把已经安装好的环境共享给其他用户。…

C语言---goto语句

文章目录基本语法代码示例goto 的常见用途&#xff08;尽管不推荐&#xff09;为什么 goto 声名狼藉&#xff1f;&#xff08;goto的缺点&#xff09;如何避免使用 goto&#xff1f;&#xff08;替代方案&#xff09;goto 语句是一种无条件跳转语句&#xff0c;它用于将程序的控…

Flask框架的简单了解

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录1. 前言2. 简介3. 核心特点4. 代码实例5. 主要…

——贪心算法——

目录 1 柠檬水找零 2 将数组和减半的最少操作次数 3 最大数 4 摆动序列 5 最长递增子序列 6 递增的三元子序列 7 最长连续递增序列 8 买卖股票的最佳时机 9 买卖股票的最佳时机 II 10 K 次取反后最大化的数组和 11 按身高排序 12 优势洗牌 13 最长回文串 14 增减…

网络操作系统与分布式操作系统的区别

网络操作系统与分布式操作系统的区别架构设计网络操作系统&#xff08;NOS&#xff09;基于客户端-服务器模型&#xff0c;通过共享资源&#xff08;如文件、打印机&#xff09;提供服务&#xff0c;各节点保留独立的管理和数据处理能力。分布式操作系统&#xff08;DOS&#x…

RabbitMQ—运维篇

RabbitMQ安装 RabbitMQ需要依赖erlang&#xff0c;如果普通安装需要安装erlang并保证二者兼容&#xff0c;因此选择较为简单的docker安装方式 1.获取rabbitmq镜像 docker pull rabbitmq:3.11.19-management #rabbitmq-management表示带有客户端&#xff08;控制台&#xff09; …

【学习K230-例程21】GT6700-UDP-Client

B站视频 UDP 简介 UDP 是 User Datagram Protocol 的简称&#xff0c;中文名是用户数据报协议&#xff0c;是 OSI&#xff08;Open SystemInterconnection&#xff0c;开放式系统互联&#xff09;参考模型中一种无连接的传输层协议&#xff0c;提供面向事务的简单不可靠信息传送…

LazyLLM教程 | 第9讲:微调实践:让大模型和向量模型更懂你的领域

前面教程中&#xff0c;我们通过优化检索策略、召回重排略以及基于大模型的查询重写策略来提升了RAG系统的检索精度&#xff0c;但最终回复的结果还需要经过大模型的融合和处理&#xff0c;模型能力的强弱直接影响到最终的结果。这就好比一道好的菜不仅需要有高质量的食材&…

六、vue3后台项目系列——页面自适应设计+pinia,vuex的使用

前言&#xff1a;在页面加入自适应是提高用户体验的一种形式&#xff0c;甚至有时候是手机用户&#xff0c;我们就需要做一个自适应处理&#xff0c;其中肯定会涉及一些状态条件的判断&#xff0c;而这些关键的条件就是我们用来切换样式的关键&#xff0c;所以我们需要使用状态…

视频讲解|Python用ResNet残差神经网络在大脑出血CT图像描数据预测应用

全文链接&#xff1a;https://tecdat.cn/?p43843 原文出处&#xff1a;拓端抖音号拓端tecdat 分析师&#xff1a;Zikun Zhang 视频讲解Python用ResNet残差神经网络在大脑出血CT图像描数据预测在临床医疗影像诊断中&#xff0c;大脑出血的快速准确识别直接关系到患者的救治效率…

Mysql中有那些锁

按照锁的力度分&#xff1a;1.行级锁2.表级锁3.全局锁4.页级锁innodb不支持页锁全局锁全局锁指的是对整个数据库实例加锁&#xff0c;一般用于数据库的表级锁表锁 是对整张表进行加锁。表级锁还有以下几种&#xff1a;意向锁&#xff1a;意向锁是指&#xff0c;我们在事务请求表…

基于 CoT 思维链协调多 MCP 工具:依托亚马逊云科技服务打造全流程智能的 Amazon Redshift 运维体系

基于 CoT 思维链协调多 MCP 工具&#xff1a;依托亚马逊云科技服务打造全流程智能的 Amazon Redshift 运维体系 新用户可获得高达 200 美元的服务抵扣金 亚马逊云科技新用户可以免费使用亚马逊云科技免费套餐&#xff08;Amazon Free Tier&#xff09;。注册即可获得 100 美元的…

手机群控平台的智能管控技术深度解析

手机群控平台作为数字化运营的核心工具&#xff0c;正在重塑移动设备管理的技术边界。其核心价值在于通过集中化控制实现批量化操作&#xff0c;同时借助智能化算法提升管控效率。本文将深入探讨其技术架构与实现方案。平台架构与核心技术手机群控平台采用分布式架构设计&#…

Spring Boot 生命周期与核心扩展点全解析(含实操案例)

在Spring Boot开发中,理解应用的生命周期是实现优雅启动、资源管理与故障处理的关键。不同于传统Spring框架需要繁琐的XML配置,Spring Boot通过自动配置简化了开发流程,但其生命周期的底层逻辑仍延续并增强了Spring的核心机制。本文将从“生命周期阶段划分”“核心扩展点原理…

69-SQLite应用

1. SQLite操作 1.1了解数据库1.2 操作数据库步骤# -*- coding: utf-8 -*- """ Project : 01-python-learn File : 03_SQLite3添加数据.py IDE : PyCharm Author : 刘庆东 Date : 2025/9/15 14:05 """ # 1. 导入模块 import sqlite3 …

Nginx - 正向vs反向代理

参考: https://blog.csdn.net/william_n/article/details/127387009 AI问答​​ Nginx 正向代理 vs 反向代理详解​​ ​​1. 正向代理&#xff08;Forward Proxy&#xff09;​​ ​​定义​​ ​​客户端主动配置​​的代理服务器&#xff0c;代表客户端向外部服务器发送请…

装饰器模式在Spring中的案例

设计模式-装饰器模式 装饰器模式所解决的问题是&#xff0c;在不改变原来方法代码的情况下对方法进行修饰&#xff0c;从而丰富方法功能。 Spring架构中的装饰器模式 在Spring架构中&#xff0c;以线程池进行举例。 线程池 线程池是一个对线程集中管理的对象&#xff0c;集中管…