A universal C-Library to create lcd menus on every platform. The access to any liquidcrysal display must be defined, e.g. HD44780.
Text-based LCDs with 5×8 pixels per cell are recommended. However, any type can be used, as the driver functions must be defined individually. This library can be used on almost all microcontrollers via C code and is 100% heap-free.
Declaration of lcdgui instance. Multiple instances are possible
lcdgui_inst lcdgui;A macro to create a constant string array ("On", "Off")
DEFINE_OPTION_LIST(on_off_options, "On", "Off");The DEFINE_MENU_BUFFER creates a item buffer with a maximum of 2 items. Its required to initialize a menu screen.
DEFINE_MENU_BUFFER(menu_buffer, 2);
lcdsc_menu menu;Declaration of a textitem with no action
lcdmenu_textitem item_message = MENU_TEXT_ITEM("Lcd Menu", NULL);Declaration of a option item with no action
lcdmenu_optionitem item_request = lcdmenu_create_option_item("Fan", NULL, on_off_options, 2);Caution
Menu items and screens should be declared in the global scope
lcddrv driver;
//## define driver here ##
lcdgui_init(&lcdgui, &driver);
lcdmenu_init_screen(&lcdgui, &menu, menu_buffer);
//Add items to menu
lcdmenu_add_item(&menu, &item_message);
lcdmenu_add_item(&menu, &item_request);
lcdmenu_open(&lcdgui, &menu);lcdgui_screen_dispatch_input(&lcdgui, INPUT_??);INPUT_UP, INPUT_DOWN, INPUT_ENTER, INPUT_LEFT, INPUT_RIGHT
"INPUT_LEFT" and "INPUT_RIGHT" are unused.
Tip
Controlling by rotary encoder is recommend: CCW (UP) CW (DOWN) SW (ENTER)
lcddrv driver;
driver->context = NULL; //optional. The context pointer can be used to pass additional data to the driver functions
driver->get_width = [](void*) -> uint8_t {
return 20;
};
driver->get_height = [](void*) -> uint8_t {
return 4;
};
driver->print = [](void* context, const char* arr) {
//...
};
driver->clear = [](void* context) {
//...
};
driver->set_cursor = [](void* context, uint8_t col, uint8_t row) {
//...
};
driver->set_cursor_visible = [](void* context, uint8_t visible) {
//...
};
driver->create_custom_char = [](void* context, uint8_t ph, const uint8_t* data) {
//...
};
lcdgui_init(&lcdgui, &driver); //driver being copied| Name | Description | Action funtion pointer | Default property |
|---|---|---|---|
| lcdmenu_textitem | Only text with optional action | void() | 0 |
| lcdmenu_optionitem | Option chooser item: selection one of option list | void(uint8_t) | focusable |
| lcdmenu_counteritem | count signed integer from min to max | void(uint8_t) | focusable |
| Name | Function |
|---|---|
| MENU_ITEM_PROP_FOCUSABLE | The item can be edited by trigger input ENTER. In edit (focus) mode, additional inputs can be received to the element. |
| MENU_ITEM_PROP_DISABLED | Action inputs are disabled for this item. |
| MENU_ITEM_PROP_UNSELECTABLE | This item is skipped when scrolling. Ideal for separator items or simple informational items. |
typedef struct CustomMenuItem {
lcdmenu_item base;
uint8_t toggled;
} custom_menu_item;
//-> ON
//-> OFF
void custom_item_render(void* item, cstrbuilderp builder) {
custom_menu_item* menu_item = (custom_menu_item*)item;
if (menu_item->toggled) {
cstrbuilder_append(builder, "ON");
} else {
cstrbuilder_append(builder, "OFF");
}
}
void custom_item_input(void* item, uint8_t input) {
if (input != INPUT_ENTER) {
return;
}
custom_menu_item* menu_item = (custom_menu_item*)item;
menu_item->toggled = !menu_item->toggled;
}
void create_custom_menu_item(custom_menu_item* item, uint8_t toggled) {
item->base.render = custom_item_render;
item->base.input = custom_item_input;
item->toggled = toggled;
}typedef struct ErrorScreen {
lcdsc base;
uint8_t error_code;
} lcdsc_error;
void error_screen_render(lcdsc* screen, lcddrv* driver) {
ErrorScreen* error_screen = (ErrorScreen*)screen;
void* ctx = driver->context;
driver->clear(ctx);
driver->set_cursor(ctx, 0, 0);
driver->print(ctx, "An error occurred");
driver->set_cursor(ctx, 0, 1);
char buf[13];
cstrbuilder builder = cstrbuilder_init(buf, 12);
cstrbuilder_append(&builder, "Error: ");
cstrbuilder_append_int16(&builder, error_screen->error_code);
driver->print(driver->context, buf);
}
void error_screen_input(lcdsc* screen, uint8_t input) {
if (input == INPUT_ENTER) {
lcdgui_screen_close(screen->instance);
}
}
void init_error_screen(lcdgui_inst* instance, lcdsc_error* screen, uint8_t error_code) {
screen->base.render = error_screen_render;
screen->base.input = error_screen_input;
screen->base.instance = instance;
screen->error_code = error_code;
}
lcdsc_error screen;
void init() {
init_error_screen(&lcdgui, &screen, 8);
lcdgui_screen_open(&lcdgui, (lcdsc*)(&screen), NULL);
}lcdsc_dialog dialog;
DEFINE_OPTION_LIST(yes_no_options, "yes", "no");
void init_dialog() {
lcddialog_init_screen(&lcdgui, &dialog, "Reset?", yes_no_options,
2, [](uint8_t option) {
if (option == 0) {
//resetting...
}
lcdgui_screen_close(&lcdgui);
});
lcddialog_open_child(&lcdgui, &dialog);
}