diff --git a/examples/InputRotary/InputRotary.ino b/examples/InputRotary/InputRotary.ino new file mode 100644 index 00000000..0ace41fb --- /dev/null +++ b/examples/InputRotary/InputRotary.ino @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LCD_ROWS 2 +#define LCD_COLS 16 + +#define CHARSET_SIZE 26 +// Create your charset +char charset[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; +// Active index of the charset +int8_t charsetPosition = -1; + +// Declare the call back functions +void inputCallback(char *value); +void clearInput(); + +extern MenuItem *usernameMenu[]; + +MAIN_MENU( + ITEM_SUBMENU("Set user", usernameMenu), + ITEM_BASIC("Settings"), + ITEM_BASIC("More Settings"), + ITEM_BASIC("And more Settings")); + +SUB_MENU( + usernameMenu, + mainMenu, + ITEM_INPUT("User", inputCallback), + ITEM_COMMAND("Clear", clearInput)); + +LiquidCrystalI2CAdapter lcdAdapter(0x27, LCD_COLS, LCD_ROWS); +LcdMenu menu(lcdAdapter); + +SimpleRotary encoder(2, 3, 4); + +RotaryNavConfig menuConfig = { + .encoder = &encoder, + .menu = &menu, + .longPressDuration = 1000, + .charset = charset, + .charsetSize = CHARSET_SIZE, + .charsetPosition = charsetPosition, +}; + +void setup() { + Serial.begin(9600); + menu.initialize(mainMenu); +} + +void loop() { + // Call the handleRotaryMenu function, passing the menuConfig instance + processWithRotaryEncoder(&menuConfig); +} + +// Define the callbacks +void inputCallback(char *value) { + // Do stuff with value + Serial.print(F("# ")); + Serial.println(value); +} + +void clearInput() { + menu[1]->setValue((char *)""); +} \ No newline at end of file diff --git a/examples/SimpleRotary/SimpleRotary.ino b/examples/SimpleRotary/SimpleRotary.ino new file mode 100644 index 00000000..3852ae51 --- /dev/null +++ b/examples/SimpleRotary/SimpleRotary.ino @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LCD_ROWS 2 +#define LCD_COLS 16 + +// Declare the callbacks +void callback(uint16_t pos); +void colorsCallback(uint16_t pos); +void toggleBacklight(uint16_t isOn); + +char *intMapping(uint16_t progress) { + long mapped = mapProgress(progress, 100L, 200L); + static char buffer[10]; + itoa(mapped, buffer, 10); + concat(buffer, 'm', buffer); + return buffer; +} + +String colors[] = {"Red", "Green", "Blue", "Orange", "Aqua", "Yellow", "Purple", "Pink"}; + +MAIN_MENU( + ITEM_BASIC("Connect to WiFi"), + ITEM_STRING_LIST("Color", colors, 8, colorsCallback), + ITEM_BASIC("Blink SOS"), + ITEM_PROGRESS("Dist", 10, intMapping, callback), + ITEM_TOGGLE("Backlight", toggleBacklight), + ITEM_BASIC("Blink random")); + +LiquidCrystalI2CAdapter lcdAdapter(0x27, LCD_COLS, LCD_ROWS); +LcdMenu menu(lcdAdapter); + +SimpleRotary encoder(2, 3, 4); + +RotaryNavConfig menuConfig = { + .encoder = &encoder, + .menu = &menu, + .longPressDuration = 1000, +}; + +void setup() { + Serial.begin(9600); + menu.initialize(mainMenu); +} + +void loop() { + // Call the handleRotaryMenu function, passing the menuConfig instance + processWithRotaryEncoder(&menuConfig); +} + +// Define the callbacks +void toggleBacklight(uint16_t isOn) { + lcdAdapter.lcd.setBacklight(isOn); +} + +void callback(uint16_t pos) { + Serial.println(pos); +} + +void colorsCallback(uint16_t pos) { + Serial.println(colors[pos]); +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 2d51308a..4ed81f3a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,3 +16,4 @@ lib_deps = marcoschwartz/LiquidCrystal_I2C@^1.1.4 arduino-libraries/LiquidCrystal @ ^1.0.7 feilipu/FreeRTOS@^11.0.1-5 + mprograms/SimpleRotary@^1.1.3 diff --git a/src/LcdMenu.h b/src/LcdMenu.h index 67a74ecd..d68a1f1f 100644 --- a/src/LcdMenu.h +++ b/src/LcdMenu.h @@ -461,10 +461,6 @@ class LcdMenu { uint8_t lb = strlen(item->getText()) + 2; uint8_t ub = lb + length; ub = constrain(ub, lb, maxCols - 2); - printCmd(F("TYPE-CHAR-l"), length); - printCmd(F("TYPE-CHAR-lb"), lb); - printCmd(F("TYPE-CHAR-ub"), ub); - printCmd(F("TYPE-CHAR-bl"), lcd.blinkerPosition); // // update text // diff --git a/src/utils/RotaryNavConfig.h b/src/utils/RotaryNavConfig.h new file mode 100644 index 00000000..f6338fe1 --- /dev/null +++ b/src/utils/RotaryNavConfig.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +/** + * @brief Configuration for rotary encoder navigation in the LCD menu. + * + * @param encoder Pointer to the rotary encoder instance + * @param menu Pointer to the LCD menu instance + * @param longPressDuration Duration (ms) to consider a long press + * @param charset Pointer to the character set + * @param charsetSize Size of the character set + * @param charsetPosition Position in the character set + */ +struct RotaryNavConfig { + SimpleRotary *encoder; + LcdMenu *menu; + uint16_t longPressDuration; +#ifdef ItemInput_H + const char *charset; + uint8_t charsetSize; + int8_t charsetPosition; +#endif +}; + +/** + * @brief Handles rotary encoder navigation in the LCD menu. + * + * @param config Pointer to the RotaryNavConfig struct + */ +void processWithRotaryEncoder(RotaryNavConfig *config) { + // Handle rotary encoder rotation + uint8_t rotation = config->encoder->rotate(); + if (rotation == 1) { + if (config->menu->lcd.getEditModeEnabled()) { +#ifdef ItemInput_H + if (config->charset) { + config->charsetPosition = + (config->charsetPosition + 1) % config->charsetSize; + config->menu->drawChar( + config->charset[config->charsetPosition]); + } else +#endif + config->menu->right(); // Call RIGHT action in edit mode + } else if (!config->menu->lcd.getEditModeEnabled()) { + config->menu->down(); // Call DOWN action in normal mode + } + } else if (rotation == 2) { + if (config->menu->lcd.getEditModeEnabled()) { +#ifdef ItemInput_H + if (config->charset) { + config->charsetPosition = constrain(config->charsetPosition - 1, 0, config->charsetSize - 1); + config->menu->drawChar( + config->charset[config->charsetPosition]); + } else +#endif + config->menu->left(); // Call LEFT action in edit mode + } else if (!config->menu->lcd.getEditModeEnabled()) { + config->menu->up(); // Call UP action in normal mode + } + } + + // Handle button press (short and long press) + uint8_t pressType = config->encoder->pushType(config->longPressDuration); + if (pressType == 1) { +#ifdef ItemInput_H + if (config->charsetPosition != -1) { + config->menu->type(config->charset[config->charsetPosition]); + config->charsetPosition = -1; + } +#endif + config->menu->enter(); // Call ENTER action (short press) + } else if (pressType == 2) { + config->menu->back(); // Call BACK action (long press) + } +}