自制的Arduino多級菜單類

因爲要做一個小應用,裏面有些參數需要在運行時設置,需要用戶在OLED屏上做一些簡單的設定,我本想在網上找一些支持菜單開發的庫,但找了很久都找不到,在論壇裏也只是一些十分簡單的例子,全部都是代碼寫死了菜單,把菜單的顯示邏輯、按鍵響應、菜單的顯示樣式、菜單的內容完成寫成固定的了,我覺得實在有點難看,也不利於之後的再次利用,因此,花了幾天的時間寫了一個菜單類庫。這個庫有以下的特點(好象有點自吹自擂了):實現的功能
1.無限制菜單項目、參數項目數量,無限制級聯菜單。
2.極簡捷菜單項目定義;
3.通過旋轉編碼器可快速選取菜單項目(可使用兩個按鍵代替旋轉編碼器);
4.通過旋轉編碼器可快速調整參數項目值(同上);
5.參數項目的值可以設定爲 整數 或 候選列表(返回值也是整數);
6.採用回調函數的方式(類似C#的委託)實現客戶化顯示,可方便調整成自己需要的顯示樣式;
7.支持運行期間設定及讀取參數項目的值。
8.支持調整菜單時自動觸發對應事件,事件與參數項目對應綁定。
9.理論上可支持多種字體,理化上支持中文,但未測試;
10.與u8g2庫只是輕度耦合,稍作調整即可切換成其它第三方顯示庫。

待改進
1.採用C++編寫,好像運行效率並無C語言的高。
2.代碼量稍多,9KB。
3.運行內存佔用稍多。若要同時實現多種功能,建議使用大內存的Arduino板子。

例如,要構建以下的菜單:


Main Menu(主菜單,名字可改)
-menu1 Value
  -menu11
   -value111 (參數,整型)
-menu2 Cities
  -China Cities(參數,列表參數,候選列表內容爲 psCitiesChina 。)
  -USA Cities(參數,列表參數,候選列表內容爲 psCitiesUSA 。)
-menu3 Animals
  -Animal Type (參數,列表參數,候選列表內容爲 psAnimals 。)
-MENU4
  -MENU41
   -MENU411
    -MENU4111
     -MENU41111
-MENU5
-MENU6
-MENU7
-MENU8
-MENU9
-MENU0
-value00(參數,整型)
 

使用起來的代碼如下:

#include "flexmenu.h"
#include "flexmisc.h"
#include <Arduino.h>
#include <U8g2lib.h>
 
U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI u8g2(U8G2_R0, /*DO clock=*/ 13, /*DI data=*/ 11, /*CS cs=*/ 10, /*DC dc=*/ 9, /* RES reset=*/ 8);
 
TFlexMenu FlexMenu; //我自己寫的菜單類 
TFlexRotaryEncoder re(2, 3, 4); //我自己寫的旋轉編碼器類,可參看這個貼子:https://www.arduino.cn/thread-86535-1-1.html
 
char *psCitiesChina[6] ={"Beijing", "Shanghai", "Guangzhou", "Shenzhen","Tianjin", "Hangzhou"};
char *psCitiesUSA[4] ={"NewYork", "Manhattan", "Washington", "Seattle"};
char *psAnimals[5] ={"dog", "cat", "mouse", "pig", "cow"};
 
void reRotateEvent(int direct)
{
    if (direct == -1) FlexMenu.clickUp();
    if (direct == 1)  FlexMenu.clickDown();
}
 
void Int0Event()
{
  re.checkState();
}
 
void fmOnDisplayTiltle(char *content, int ttype) //顯示標題
{
    u8g2.setFont(u8g2_font_unifont_t_chinese1);
    u8g2.drawUTF8(0, 15, content);
    u8g2.drawLine(0, 16, 128, 16);
}
 
void fmOnDisplayContent(int idx, char *content, int ctype) //顯示菜單內容
{
    if (ctype == 1)  // 需顯示的是:一般菜單項
    {
        u8g2.drawUTF8(5, (idx + 1) * 16,content);
    }
    else if (ctype == 2) //需顯示的是:光標指示的一般菜單項
    {
        u8g2.drawUTF8(5, (idx + 1) * 16,content);
        u8g2.drawTriangle(0, 8 + idx * 16, 0, 16 + idx * 16, 4, 12+ idx * 16);
    }
    else if (ctype == 3) //需顯示的是:待調整的值
    {
       int ll = strlen(content) * 8;
       int sp = (128 - ll) / 2;
       u8g2.drawUTF8(sp, 3 * 16, content);
    }
}
 
void fmOnAdjustValueEvent(int id, int value, int upDown)
{
   String s = "fmOnAdjustValueEvent: id: " + id ;
   s = s + " value: " + value;
   s = s + " UpDown: " + upDown;
   //Serial.println(s);  
}
 
void fmOnOKValueEvent(int id, int value)
{
 // Serial.println("fmOnOKValueEvent: id:%d, value: %d ", id, value);  
}
 
void initMenu()
{
    TFlexMenuItem *mi1, *mi2; // 創建兩個臨時變量,用於創建層級菜單。
 
 
    //在根菜單下增加一個菜單,名爲“menu1 Value”,NULL指的是沒有父菜單項目,addMenuItem返回的是增加後該菜單項目的句柄(地址)
    mi1 = FlexMenu.addMenuItem("menu1 Value", NULL);
 
    //在“menu1 Value“菜單下增加一個菜單,名爲“menu11”,mi1 傳入的是menu1 Value的句柄
    mi2 = FlexMenu.addMenuItem("menu11", mi1);
 
    //增加一個整數型參數,這個參數的名字叫“value111”,id是 1(用於在運行時設置及讀取),最大值是 200, 最小值是3,默認值是 100,
    FlexMenu.addMenuValue(1,"value111", mi2, 100, 200, 3);
    
    mi1 = FlexMenu.addMenuItem("menu2 Cities", NULL);
 
 
    //增加一個列表型參數,這個參數的名字叫“China Cities”,id是 2(用於在運行時設置及讀取),候選的列表是psCitiesChina,默認是選第1個, 共6個候選值
    FlexMenu.addMenuValueList(2,"China Cities", mi1, 0, 6, psCitiesChina);
 
    //增加一個列表型參數,這個參數的名字叫“USA Cities”,id是 3(用於在運行時設置及讀取),候選的列表是psCitiesUSA,默認是選第1個, 共4個候選值
    FlexMenu.addMenuValueList(3,"USA Cities", mi1, 0, 4, psCitiesUSA);
    
    mi1 = FlexMenu.addMenuItem("menu3 Animals", NULL);
    FlexMenu.addMenuValueList(4,"Animal Type", mi1, 0, 5, psAnimals);
    
    mi1 = FlexMenu.addMenuItem("MENU4", NULL);
    mi1 = FlexMenu.addMenuItem("MENU41", mi1);
    mi1 = FlexMenu.addMenuItem("MENU411", mi1);
    mi1 = FlexMenu.addMenuItem("MENU4111", mi1);
    mi1 = FlexMenu.addMenuItem("MENU41111", mi1);
    
    mi1 = FlexMenu.addMenuItem("menu5", NULL);
    mi1 = FlexMenu.addMenuItem("menu6", NULL);
    mi1 = FlexMenu.addMenuItem("menu7", NULL);
    mi1 = FlexMenu.addMenuItem("menu8", NULL);
    mi1 = FlexMenu.addMenuItem("menu9", NULL);
    mi1 = FlexMenu.addMenuItem("menu0", NULL);
    
    FlexMenu.addMenuValue(100,"value00", NULL, 9, 1000, 3);
    
    FlexMenu.init();
}
 
void setup()
{
    //Serial.begin (9600);
 
    FlexMenu.pU8G2 = &u8g2;
    FlexMenu.pageRow = 3;
    FlexMenu.attachDisplayTitle(fmOnDisplayTiltle);
    FlexMenu.attachDisplayContent(fmOnDisplayContent);
    FlexMenu.attachAdjustValueEvent(fmOnAdjustValueEvent);
    FlexMenu.attachOKValueEvent(fmOnOKValueEvent);
    
    initMenu();
    
    re.attachRotateEvent(reRotateEvent);
    re.setClickResetCount(true);
 
    u8g2.begin();
    attachInterrupt(0, Int0Event, CHANGE);
}
 
void loop()
{
    FlexMenu.display();
    
    if (re.isClick()){ FlexMenu.clickOK();};
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章