Это возможность быстро добавить в ваш проект меню настрокек или мини-панель управления какими-то парметрами
https://github.com/gewisser/GyverOLEDMenu/assets/5417292/46c5d222-9373-4607-a67f-1dd8f44a5d07
Бибилиотека в себе использует библиотеку GyverOLED, как зависимость для работы с OLED дисплем https://github.com/GyverLibs/GyverOLED
Совместима со всеми Arduino платформами (используются Arduino-функции)
// дефайны меню которые можно переопределить
// #define MENU_SELECTED_H 10 // высота элемента меню
// #define MENU_PARAMS_LEFT_OFFSET 92 // смещение вправо для рендеринга значений
// #define MENU_PAGE_ITEMS_COUNT 6 // Количесвто элементов меню на одной странице
// #define MENU_FAST_K 4 // коэффициент для ускоренного приращения (см. isFast ниже)
#include <GyverOLED.h>
#include "GyverOLEDMenu.h"
GyverOLED<SSH1106_128x64> oled;
OledMenu<9, GyverOLED<SSH1106_128x64>> menu(&oled); // Где 9 - количество элементов в меню
// ===== МАКРО ФУНКЦИИ =====
/*
Макрофункции можно использовать для создания константы нужного типа прямо в методе addItem(...). См. пример.
*/
GM_N_INT(x); // Объявляет глобальную констату типа [int] и возвращает указатель на неё
GM_N_U_INT(x); // Объявляет глобальную констату типа [unsigned int] и возвращает указатель на неё
GM_N_FLOAT(x); // Объявляет глобальную констату типа [float int] и возвращает указатель на неё
GM_N_DOUBLE(x); // Объявляет глобальную констату типа [double] и возвращает указатель на неё
GM_N_BYTE(x); // Объявляет глобальную констату типа [byte] и возвращает указатель на неё
// ===== СЕРВИС =====
boolean menu.isMenuShowing; // Показано ли сейчас меню
byte menu.currentPage = 1; // Текущая страница
/*
✔️ addItem
Последовательное конфигурирование элементов меню в массиве
*/
void menu.addItem(PGM_P str); // Добавит экшен (просто кликабельный item)
void menu.addItem(PGM_P str, const int* inc, int* val, const int* min, const int* max)
void menu.addItem(PGM_P str, const unsigned int* inc, unsigned int* val, const unsigned int* min, const unsigned int* max)
void menu.addItem(PGM_P str, const double* inc, double* val, const double* min, const double* max)
void menu.addItem(PGM_P str, const float* inc, float* val, const float* min, const float* max);
void menu.addItem(PGM_P str, const byte* inc, byte* val, const byte* min, const byte* max);
void menu.addItem(PGM_P str, boolean* val);
/*
✔️ showMenu
Показать / скрыть меню (val - true/false)
update - вызовет обновление oled дисплея после готовности к отрисовке меню
*/
void menu.showMenu(boolean val, boolean update = true);
/*
✔️ refresh
Текущая страница меню заново будет отрисована
*/
void menu.refresh();
/*
✔️ pageCount
Возвращает количество страниц
*/
byte menu.pageCount();
// ===== КУРСОР =====
/*
✔️ selectNext
Переместит курсор на следующий item. В режиме редактирования этот метод сделает
инкремент значения, где isFast = true, увеличит инкремент в 4 раза (#define MENU_FAST_K 4)
*/
void menu.selectNext(boolean isFast = false);
/*
✔️ selectPrev
Переместит курсор на предидущий item. В режиме редактирования этот метод сделает
декремент значения, где isFast = true, увеличит декремент в 4 раза (#define MENU_FAST_K 4)
*/
void menu.selectPrev(boolean isFast = false)
/*
️✔️ toggleChangeSelected
Циклично переключает выделенный элемент в режим редактирования / выход из режима редактирования
*/
void menu.toggleChangeSelected();
// ===== CALLBACK =====
/*
✔️ onChange
cb - [ typedef void (*cbOnChange)(const int index, const void* val, const byte valType) ]
immediate - отвечает за то, когда будет вызван колбек.
если immediate == false, то колбэк будет вызван после выхода из режима редактирования параметра,
иначе любое изменение приведёт к вызову колбека
Колбек `cb` вызывается всякий раз, когда происходит выход из режима редактирования
или непосредсвтенно при изменении значения
index - текущий индекс меню в массиве. Первый элемент меню будет с индексом - 0
val - указатель на изменённую переменную
valType - тип изменённой переменной:
#define VAL_ACTION 0
#define VAL_INTEGER 1
#define VAL_FLOAT 2
#define VAL_DOUBLE 3
#define VAL_BYTE 4
#define VAL_BOOLEAN 5
#define VAL_U_INTEGER 6
*/
void onChange(cbOnChange cb, const boolean immediate = false);
/*
✔️ onPrintOverride
cb - [ typedef boolean (*cbOnPrintOverride)(const int index, const void* val, const byte valType) ]
Колбек `cb`(cbOnPrintOverride) вызывается всякий раз, когда должен происходить oled.print(...) для значений.
Если колбэк вернёт false, то либа "сама напечатает" значение [ oled.print(*(type)val) ],
иначе вы сами должны вызывать oled.print(...) в данный момент веремени в колбэке.
index, val и valType - такие же как и в [ typedef void (*cbOnChange) ]. См. выше.
*/
void onPrintOverride(cbOnPrintOverride cb);
#define EB_FAST_TIME 120
#include <GyverOLED.h>
#include <EncButton.h>
#include "GyverOLEDMenu.h"
EncButton eb(6, 7, 5, INPUT_PULLUP);
GyverOLED<SSH1106_128x64> oled;
OledMenu<9, GyverOLED<SSH1106_128x64>> menu(&oled);
int d_p = 10;
int d_i = 1000;
int d_d = 50;
byte tt11 = 10;
float tt1 = 0.5;
boolean lgh = false;
int tt3 = 5;
int tt4 = 1000;
void setup() {
oled.init();
Wire.setClock(400000L);
oled.clear();
oled.update();
menu.onChange(onItemChange, true);
menu.onPrintOverride(onItemPrintOverride); // если нужно сделать своё форматирование значений
menu.addItem(PSTR("<- ВЫХОД")); // 0
menu.addItem(PSTR("КОЭФ. P"), GM_N_INT(1), &d_p, GM_N_INT(0), GM_N_INT(100));
menu.addItem(PSTR("КОЭФ. I"), GM_N_INT(1), &d_i, GM_N_INT(-5), GM_N_INT(20));
menu.addItem(PSTR("КОЭФ. D"), GM_N_INT(1), &d_d, GM_N_INT(0), GM_N_INT(7000)); // 3
menu.addItem(PSTR("ВРЕМЯ ОПР."), GM_N_FLOAT(0.01), &tt1, GM_N_FLOAT(1), GM_N_FLOAT(20));
menu.addItem(PSTR("TIMER 1"), GM_N_BYTE(1), &tt11, GM_N_BYTE(1), GM_N_BYTE(255)); // 5
menu.addItem(PSTR("ПОДСВЕТКА"), &lgh); // page 2
menu.addItem(PSTR("TIMER 3"), GM_N_INT(1), &tt3, GM_N_INT(1), GM_N_INT(5));
menu.addItem(PSTR("TIMER 4"), GM_N_INT(5), &tt4, GM_N_INT(0), GM_N_INT(1000)); // 8
menu.showMenu(true);
eb.attach(cb);
}
void onItemChange(const int index, const void* val, const byte valType) {
if (valType == VAL_ACTION) {
if (index == 0) {
menu.showMenu(false);
}
}
}
boolean onItemPrintOverride(const int index, const void* val, const byte valType) {
// Допустим, что `TIMER 4`(index 8) - это минуты, которые мы можем менять. Отформатируем минуты по формату - `hh:mm`
if (index == 8) {
unsigned int hours = tt4 / 60; // [hh]
byte minutes = tt4 - (hours * 60); // [mm]
// отображаем нужном нам формате:
if (hours < 10) {
oled.print(0);
}
oled.print(hours);
oled.print(":");
if (minutes < 10) {
oled.print(0);
}
oled.print(minutes);
return true; // сигнализируем, что мы сами вызываем метод oled.print(...) с нужным нам форматированием
}
// возвращаем всегда `false`, если мы не собираемся для других пунктов меню принтить значение
return 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();
}
onItemPrintOverride
) + небольшой рефакторинг кода.addItem(...., *min, *max)
- устанавливается минимальное min
и наоборот... При нахождении багов создавайте Issue Библиотека открыта для доработки и ваших Pull Request'ов!
При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать: