Description
What MCU/Processor/Board and compiler are you using?
Board: ESP32S3 DevkitC-1 v1.1
IDE: ESP-IDF v5.4.0 with VSCode extension
Compiler: CMake (esp-idf built in)
Hardware: ILI 9488 3.5 inch TFT Display and PCB matrix keypad
What LVGL version are you using?
9.2.2 (master)
What do you want to achieve?
I would like to navigate between 4 text areas, be able to input text into each of those text areas, and include a button to move to another screen all via a physical keypad. The keypad I am using has the keys 0 through 9, “.”, back, enter, and up, down, left, and right.
This is my first ever project using LVGL and the ESP-IDF ecosystem. I have spent the past 3 days trying everything I can and for the life of me cannot figure it out. I am getting nowhere as I seem to be having a fundamental misunderstanding somewhere
What have you tried so far?
- I have created the keypad as an indev keypad device following the documentation.
- I created two text areas, some labels, and a button to simplify what I’m trying to do right now.
- I have added everything to a group and set the indev keypad to that group.
From reading the documentation and looking at other examples, the navigation and keypad inputs should all be handled via the group now. This seems like magic and I feel as though I am missing something. Maybe adding event callbacks to each text area? Flags? If so, what do I need to add?
Currently the inputs from the keypad result with no reaction of the screen.
Code to reproduce
I am going to go ahead and put all of the code that is relevant. It is quite a lot and if I need to edit this post due to rules, please let me know.
Main.c LVGL code
All other initializations are done (display, flush, spi, lvgl, etc.). Text areas and other labels are currently displayed to screen. The interactivity is the only issue.
void initialize_screens(void){
// Create input group
lv_group_t * input_group = lv_group_create();
lv_group_set_default(input_group);
lv_indev_set_group(indev_keypad, input_group);
ESP_LOGI(TAG, "Initializing Screens");
/* Main input screen object */
lv_obj_t * input_screen = lv_obj_create(NULL);
lv_screen_load(input_screen);
// Input Screen Items
/* Create Inputs screen label at top center*/
lv_obj_t * input_label = lv_label_create(input_screen);
lv_label_set_text(input_label, "Inputs:");
lv_obj_align(input_label, LV_ALIGN_TOP_MID, 0, 10);
/* Create the Latitude input box */
lv_obj_t * Lat_ta = lv_textarea_create(input_screen);
lv_textarea_set_one_line(Lat_ta, true);
lv_textarea_set_password_mode(Lat_ta, false);
lv_textarea_set_placeholder_text(Lat_ta, "Decimal Deg. (N)");
lv_obj_set_width(Lat_ta, lv_pct(40));
lv_obj_align(Lat_ta, LV_ALIGN_LEFT_MID, 10, -40);
lv_group_add_obj(input_group, Lat_ta);
/* Create Latitude label */
lv_obj_t * Lat_label = lv_label_create(input_screen);
lv_label_set_text(Lat_label, "Latitude:");
lv_obj_align_to(Lat_label, Lat_ta, LV_ALIGN_OUT_TOP_MID, 0, 0);
/* Create the Longitude input box */
lv_obj_t * Long_ta = lv_textarea_create(input_screen);
lv_textarea_set_one_line(Long_ta, true);
lv_textarea_set_password_mode(Long_ta, false);
lv_textarea_set_placeholder_text(Long_ta, "Decimal Deg. (W)");
lv_obj_set_width(Long_ta, lv_pct(40));
lv_obj_align(Long_ta, LV_ALIGN_RIGHT_MID, -10, -40);
lv_group_add_obj(input_group, Long_ta);
/* Create Longitude label*/
lv_obj_t * long_label = lv_label_create(input_screen);
lv_label_set_text(long_label, "Longitude:");
lv_obj_align_to(long_label, Long_ta, LV_ALIGN_OUT_TOP_MID, 0, 0);
/* Create Enter button and label */
lv_obj_t * Enter_button = lv_button_create(input_screen);
lv_obj_align(Enter_button, LV_ALIGN_BOTTOM_MID, 0, -20);
lv_group_add_obj(input_group, Enter_button);
lv_obj_t * Enter_label = lv_label_create(Enter_button);
lv_label_set_text(Enter_label, "Enter");
lv_obj_center(Enter_label);
Indev keypad
This is the code for hardware initialization of the keypad, the indev initialization, the callback for the keypad, and the scanning of the keypad. It is a matrix keypad I have designed and created on a PCB and works properly. The code for the indev and callback are copied from one of the examples on the GitHub and seem to be working properly.
// Keypad.h
#ifndef KEYPAD_H
#define KEYPAD_H
#include "lvgl.h"
static lv_indev_t * indev_keypad;
void lv_port_indev_init(void);
#endif /*KEYPAD_H*/
// Keypad.c
#include <stdio.h>
#include <lvgl.h>
#include "driver/gpio.h"
#include "esp_log.h"
#include "Keypad.h"
#define NUM_ROWS 4 //INPUT to check
#define NUM_COLS 5 //OUTPUT to set
static void keypad_init(void);
static void keypad_read(lv_indev_t * indev, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);
static const char *BTNTAG = "BTN";
static uint32_t row_gpio[NUM_ROWS] = {18, 8, 9, 17};
static uint32_t col_gpio[NUM_COLS] = {14, 13, 12, 11, 10};
static const uint32_t keys[NUM_ROWS][NUM_COLS] = {
{37, 38, 39, 0, LV_KEY_UP},
{34, 35, 36, 0, LV_KEY_DOWN},
{31, 32, 33, 0, LV_KEY_LEFT},
{LV_KEY_NEXT, LV_KEY_PREV, LV_KEY_BACKSPACE, LV_KEY_ENTER, LV_KEY_DOWN}
};
/* Had to modify to new data type in declaration due to LVGL
This is the mapping of the buttons. Will figure out later,
just want to get navigation working right now
{"7", "8", "9", "NA", "Up"},
{"4", "5", "6", "NA", "Down"},
{"1", "2", "3", "NA", "Left"},
{".", "0", "Back", "Enter", "Right"}
*/
gpio_config_t config_pin = {
.pin_bit_mask = 0,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
void lv_port_indev_init(void){
keypad_init();
ESP_LOGI(BTNTAG, "Creating Keypad LVGL Object");
indev_keypad = lv_indev_create();
lv_indev_set_type(indev_keypad, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(indev_keypad, keypad_read);
}
static void keypad_init(void){
ESP_LOGI(BTNTAG, "Initializing Keypad GPIO");
for(int i = 0; i < NUM_COLS; i++){
config_pin.pin_bit_mask = (1ULL << col_gpio[i]);
config_pin.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&config_pin);
}
for(int i = 0; i < NUM_ROWS; i++){
config_pin.pin_bit_mask = (1ULL << row_gpio[i]);
config_pin.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&config_pin);
}
};
static void keypad_read(lv_indev_t * indev, lv_indev_data_t * data){
uint32_t act_key = keypad_get_key();
data->key = act_key;
if(act_key != 0){
data->state = LV_INDEV_STATE_PRESSED;
ESP_LOGI(BTNTAG, "%lu", act_key);
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
static uint32_t keypad_get_key(void){
esp_log_level_set("gpio", ESP_LOG_ERROR);
for(int i = 0; i < NUM_ROWS; i++){
config_pin.pin_bit_mask = (1ULL << row_gpio[i]);
config_pin.mode = GPIO_MODE_OUTPUT;
gpio_config(&config_pin);
gpio_set_level(row_gpio[i], 0);
for(int j = 0; j < NUM_COLS; j++){
int8_t level = gpio_get_level(col_gpio[j]);
if(level == 0){
config_pin.mode = GPIO_MODE_INPUT;
gpio_config(&config_pin);
return keys[i][j];
}
}
config_pin.mode = GPIO_MODE_INPUT;
gpio_config(&config_pin);
}
return 0;
}
I don’t have enough experience with this but I can clearly tell I am missing something obvious. If anyone knows what my issue is and could point me in the right direction, it would be greatly appreciated.
Screenshot and/or video
Hardware setup
Current screen output (cursor not seen in picture but is blinking in latitude box)