Add stats panel

This commit is contained in:
suchmememanyskill
2024-02-03 15:16:25 +01:00
parent ba015bb2e8
commit 41be4b1a31
10 changed files with 275 additions and 30 deletions

View File

@@ -19,6 +19,7 @@ lib_deps =
https://github.com/Bodmer/TFT_eSPI.git https://github.com/Bodmer/TFT_eSPI.git
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
bblanchon/ArduinoJson@^7.0.0 bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
build_flags = build_flags =
-DLV_CONF_PATH="../../../../src/conf/lv_conf.h" -DLV_CONF_PATH="../../../../src/conf/lv_conf.h"
@@ -39,23 +40,13 @@ build_flags =
-DSPI_FREQUENCY=55000000 -DSPI_FREQUENCY=55000000
-DSPI_READ_FREQUENCY=20000000 -DSPI_READ_FREQUENCY=20000000
-DSPI_TOUCH_FREQUENCY=2500000 -DSPI_TOUCH_FREQUENCY=2500000
### Porting options ###
# Defines the screen height
-DCYD_SCREEN_HEIGHT_PX=240 -DCYD_SCREEN_HEIGHT_PX=240
# Defines the screen width
-DCYD_SCREEN_WIDTH_PX=320 -DCYD_SCREEN_WIDTH_PX=320
# Defines the pixel gap used for large gaps (like between buttons)
-DCYD_SCREEN_GAP_PX=8 -DCYD_SCREEN_GAP_PX=8
# Defines the minimum pixel height of a button
-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35 -DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35
# Defines the minimum pixel width of a button
-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35 -DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35
# Defines the size of font used
-DCYD_SCREEN_FONT=&lv_font_montserrat_14 -DCYD_SCREEN_FONT=&lv_font_montserrat_14
# Defines the size of font used for small text
-DCYD_SCREEN_FONT_SMALL=&lv_font_montserrat_10 -DCYD_SCREEN_FONT_SMALL=&lv_font_montserrat_10
# Defines the size of the sizebar
-DCYD_SCREEN_SIDEBAR_SIZE_PX=40 -DCYD_SCREEN_SIDEBAR_SIZE_PX=40
# Defines the screen driver -DCYD_SCREEN_DRIVER_ESP32_2432S028R=1
-DCYD_SCREEN_DRIVER_ESP32_2432S028R=1

View File

@@ -6,6 +6,7 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#include "macros_query.h" #include "macros_query.h"
#include <UrlEncode.h>
const char *printer_state_messages[] = { const char *printer_state_messages[] = {
"Error", "Error",
@@ -43,8 +44,9 @@ void unfreeze_render_thread(){
void send_gcode(bool wait, const char *gcode) void send_gcode(bool wait, const char *gcode)
{ {
Serial.printf("Sending gcode: %s\n", gcode);
char buff[256] = {}; char buff[256] = {};
sprintf(buff, "http://%s:%d/printer/gcode/script?script=%s", global_config.klipperHost, global_config.klipperPort, gcode); sprintf(buff, "http://%s:%d/printer/gcode/script?script=%s", global_config.klipperHost, global_config.klipperPort, urlEncode(gcode).c_str());
HTTPClient client; HTTPClient client;
client.begin(buff); client.begin(buff);
@@ -67,7 +69,7 @@ void fetch_printer_data()
{ {
freeze_request_thread(); freeze_request_thread();
char buff[256] = {}; char buff[256] = {};
sprintf(buff, "http://%s:%d/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks", global_config.klipperHost, global_config.klipperPort); sprintf(buff, "http://%s:%d/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks&fan", global_config.klipperHost, global_config.klipperPort);
HTTPClient client; HTTPClient client;
client.useHTTP10(true); client.useHTTP10(true);
client.begin(buff); client.begin(buff);
@@ -139,8 +141,18 @@ void fetch_printer_data()
printer.position[0] = status["gcode_move"]["gcode_position"][0]; printer.position[0] = status["gcode_move"]["gcode_position"][0];
printer.position[1] = status["gcode_move"]["gcode_position"][1]; printer.position[1] = status["gcode_move"]["gcode_position"][1];
printer.position[2] = status["gcode_move"]["gcode_position"][2]; printer.position[2] = status["gcode_move"]["gcode_position"][2];
printer.gcode_offset[0] = status["gcode_move"]["homing_origin"][0];
printer.gcode_offset[1] = status["gcode_move"]["homing_origin"][1];
printer.gcode_offset[2] = status["gcode_move"]["homing_origin"][2];
bool absolute_coords = status["gcode_move"]["absolute_coordinates"]; bool absolute_coords = status["gcode_move"]["absolute_coordinates"];
printer.absolute_coords = absolute_coords == true; printer.absolute_coords = absolute_coords == true;
printer.speed_mult = status["gcode_move"]["speed_factor"];
printer.extrude_mult = status["gcode_move"]["extrude_factor"];
}
if (status.containsKey("fan"))
{
printer.fan_speed = status["fan"]["speed"];
} }
if (status.containsKey("virtual_sdcard")) if (status.containsKey("virtual_sdcard"))

View File

@@ -25,6 +25,10 @@ typedef struct _Printer {
float filament_used_mm; float filament_used_mm;
char* print_filename; // 0 -> 1 char* print_filename; // 0 -> 1
float print_progress; float print_progress;
float fan_speed; // 0 -> 1
float gcode_offset[3];
float speed_mult;
float extrude_mult;
} Printer; } Printer;
extern Printer printer; extern Printer printer;

View File

@@ -80,7 +80,8 @@ void ip_init_inner(){
lv_obj_t * resetBtn = lv_btn_create(lv_scr_act()); lv_obj_t * resetBtn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(resetBtn, reset_btn_event_handler, LV_EVENT_ALL, NULL); lv_obj_add_event_cb(resetBtn, reset_btn_event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(resetBtn, LV_ALIGN_CENTER, 0, 40); lv_obj_set_height(resetBtn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(resetBtn, LV_ALIGN_CENTER, 0, CYD_SCREEN_GAP_PX + CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t * btnLabel = lv_label_create(resetBtn); lv_obj_t * btnLabel = lv_label_create(resetBtn);
lv_label_set_text(btnLabel, "Reset"); lv_label_set_text(btnLabel, "Reset");

View File

@@ -19,9 +19,7 @@ static void move_printer(const char* axis, float amount) {
send_gcode(true, "G91"); send_gcode(true, "G91");
} }
const char * space = "%20"; sprintf(gcode, "G1 %s%s%.1f F6000", axis, extra, amount);
sprintf(gcode, "G1%s%s%s%.1f%sF6000", space, axis, extra, amount, space);
send_gcode(true, gcode); send_gcode(true, gcode);
if (absolute_coords) { if (absolute_coords) {
@@ -195,6 +193,11 @@ static void root_panel_state_update(lv_event_t * e){
} }
void move_panel_init(lv_obj_t* panel){ void move_panel_init(lv_obj_t* panel){
if (printer.state == PRINTER_STATE_PRINTING){
stats_panel_init(panel);
return;
}
last_homing_state = !printer.homed_axis; last_homing_state = !printer.homed_axis;
lv_obj_add_event_cb(panel, root_panel_state_update, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(panel, root_panel_state_update, LV_EVENT_MSG_RECEIVED, NULL);

View File

@@ -7,4 +7,5 @@ void temp_panel_init(lv_obj_t* panel);
void print_panel_init(lv_obj_t* panel); void print_panel_init(lv_obj_t* panel);
void move_panel_init(lv_obj_t* panel); void move_panel_init(lv_obj_t* panel);
void progress_panel_init(lv_obj_t* panel); void progress_panel_init(lv_obj_t* panel);
void macros_panel_init(lv_obj_t* panel); void macros_panel_init(lv_obj_t* panel);
void stats_panel_init(lv_obj_t* panel);

View File

@@ -0,0 +1,166 @@
#include "panel.h"
#include "../ui_utils.h"
#include "../../core/data_setup.h"
#include <stdio.h>
#include <Esp.h>
static void set_fan_speed_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
char data[64];
sprintf(data, "Fan: %.0f%%", printer.fan_speed * 100);
lv_label_set_text(label, data);
}
static void set_fan_speed(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
char gcode[64];
sprintf(gcode, "M106 S%d", speed);
send_gcode(true, gcode);
}
const char* fan_speeds[] = { "0%", "15%", "25%", "35%" };
const int fan_speeds_values[] = { 0, 38, 64, 90 };
const char* fan_speeds_2[] = { "50%", "75%", "100%"};
const int fan_speeds_values_2[] = { 128, 192, 255 };
lv_button_column_t fan_speed_columns[] = {
{ set_fan_speed, fan_speeds, (const void**)fan_speeds_values, 4},
{ set_fan_speed, fan_speeds_2, (const void**)fan_speeds_values_2, 3}
};
static void set_zoffset_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
char data[64];
sprintf(data, "Z Offset: %.03f", printer.gcode_offset[2]);
lv_label_set_text(label, data);
}
static void set_zoffset(lv_event_t * e){
char* offset = (char*)lv_event_get_user_data(e);
char gcode[64];
sprintf(gcode, "SET_GCODE_OFFSET Z_ADJUST=%s", offset);
send_gcode(true, gcode);
}
const char* zoffsets[] = { "-0.005", "-0.01", "-0.025", "-0.05" };
const char* zoffsets_2[] = { "+0.005", "+0.01", "+0.025", "+0.05" };
lv_button_column_t zoffset_columns[] = {
{ set_zoffset, zoffsets, (const void**)zoffsets, 4},
{ set_zoffset, zoffsets_2, (const void**)zoffsets_2, 4}
};
static void set_speed_mult_text(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char data[64];
sprintf(data, "Speed: %.0f%%", printer.speed_mult * 100);
lv_label_set_text(label, data);
}
static void set_speed_mult(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
char gcode[64];
sprintf(gcode, "M220 S%d", speed);
send_gcode(true, gcode);
}
static void set_speed_mult_offset(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
float result = printer.speed_mult * 100 + speed;
printer.speed_mult = result / 100;
char gcode[64];
sprintf(gcode, "M220 S%.0f", result);
send_gcode(true, gcode);
}
const char* speed_presets[] = { "50%", "100%", "150%", "200%" };
const int speed_presets_values[] = { 50, 100, 150, 200 };
const char* speed_presets_minus[] = { "-1%", "-5%", "-10%", "-25%" };
const int speed_presets_minus_values[] = { -1, -5, -10, -25 };
const char* speed_presets_plus[] = { "+1%", "+5%", "+10%", "+25%" };
const int speed_presets_plus_values[] = { 1, 5, 10, 25 };
lv_button_column_t speed_mult_columns[] = {
{ set_speed_mult, speed_presets, (const void**)speed_presets_values, 4},
{ set_speed_mult_offset, speed_presets_minus, (const void**)speed_presets_minus_values, 4},
{ set_speed_mult_offset, speed_presets_plus, (const void**)speed_presets_plus_values, 4}
};
static void set_extrude_mult_text(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char data[64];
sprintf(data, "Flow: %.0f%%", printer.extrude_mult * 100);
lv_label_set_text(label, data);
}
static void set_extrude_mult(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
char gcode[64];
sprintf(gcode, "M221 S%d", speed);
send_gcode(true, gcode);
}
static void set_extrude_mult_offset(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
float result = printer.extrude_mult * 100 + speed;
printer.extrude_mult = result / 100;
char gcode[64];
sprintf(gcode, "M221 S%.0f", result);
send_gcode(true, gcode);
}
const char* extrude_presets[] = { "95%", "100%", "105%", "110%" };
const int extrude_presets_values[] = { 95, 100, 105, 110 };
const char* extrude_offset[] = { "+5%", "+1%", "-1%", "-5%" };
const int extrude_offset_values[] = { 5, 1, -1, -5 };
lv_button_column_t extrude_mult_columns[] = {
{ set_extrude_mult, extrude_presets, (const void**)extrude_presets_values, 4},
{ set_extrude_mult_offset, extrude_offset, (const void**)extrude_offset_values, 4}
};
static void open_fan_speed_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_fan_speed_text, fan_speed_columns, 2);
lv_msg_send(DATA_PRINTER_DATA, &printer);
}
static void open_zoffset_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_zoffset_text, zoffset_columns, 2);
lv_msg_send(DATA_PRINTER_DATA, &printer);
}
static void open_speed_mult_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_speed_mult_text, speed_mult_columns, 3);
lv_msg_send(DATA_PRINTER_DATA, &printer);
}
static void open_extrude_mult_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_extrude_mult_text, extrude_mult_columns, 2);
lv_msg_send(DATA_PRINTER_DATA, &printer);
}
void create_state_button(lv_obj_t * root, lv_event_cb_t label, lv_event_cb_t button){
lv_obj_t * btn = lv_btn_create(root);
lv_obj_set_size(btn, CYD_SCREEN_PANEL_WIDTH_PX / 2 - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, button, LV_EVENT_CLICKED, NULL);
lv_obj_t * label_obj = lv_label_create(btn);
lv_obj_add_event_cb(label_obj, label, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label_obj, NULL);
lv_obj_align(label_obj, LV_ALIGN_CENTER, 0, 0);
}
void stats_panel_init(lv_obj_t* panel) {
auto panel_width = CYD_SCREEN_PANEL_WIDTH_PX / 2 - CYD_SCREEN_GAP_PX * 3;
lv_obj_t * right_panel = lv_create_empty_panel(panel);
lv_obj_set_size(right_panel, panel_width, CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_layout_flex_column(right_panel, LV_FLEX_ALIGN_CENTER);
lv_obj_align(right_panel, LV_ALIGN_TOP_RIGHT, -1 * CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
create_state_button(right_panel, set_fan_speed_text, open_fan_speed_panel);
create_state_button(right_panel, set_zoffset_text, open_zoffset_panel);
create_state_button(right_panel, set_speed_mult_text, open_speed_mult_panel);
create_state_button(right_panel, set_extrude_mult_text, open_extrude_mult_panel);
}

View File

@@ -80,15 +80,14 @@ static void keyboard_callback(lv_event_t * e){
} }
char gcode[64]; char gcode[64];
const char* space = "%20";
switch (keyboard_target){ switch (keyboard_target){
case TARGET_HOTEND: case TARGET_HOTEND:
sprintf(gcode, "M104%sS%d", space, temp); sprintf(gcode, "M104 S%d", temp);
send_gcode(true, gcode); send_gcode(true, gcode);
break; break;
case TARGET_BED: case TARGET_BED:
sprintf(gcode, "M140%sS%d", space, temp); sprintf(gcode, "M140 S%d", temp);
send_gcode(true, gcode); send_gcode(true, gcode);
break; break;
case TARGET_HOTEND_CONFIG_1: case TARGET_HOTEND_CONFIG_1:
@@ -156,8 +155,8 @@ static void cooldown_temp(lv_event_t * e){
return; return;
} }
send_gcode(true, "M104%20S0"); send_gcode(true, "M104 S0");
send_gcode(true, "M140%20S0"); send_gcode(true, "M140 S0");
} }
static void btn_extrude(lv_event_t * e){ static void btn_extrude(lv_event_t * e){
@@ -166,7 +165,7 @@ static void btn_extrude(lv_event_t * e){
} }
send_gcode(true, "M83"); send_gcode(true, "M83");
send_gcode(true, "G1%20E25%20F300"); send_gcode(true, "G1 E25 F300");
} }
static void set_temp_via_preset(lv_event_t * e){ static void set_temp_via_preset(lv_event_t * e){
@@ -180,11 +179,10 @@ static void set_temp_via_preset(lv_event_t * e){
} }
char gcode[64]; char gcode[64];
const char* space = "%20";
if (target <= TARGET_HOTEND_CONFIG_3) if (target <= TARGET_HOTEND_CONFIG_3)
sprintf(gcode, "M104%sS%d", space, value); sprintf(gcode, "M104 S%d", value);
else else
sprintf(gcode, "M140%sS%d", space, value); sprintf(gcode, "M140 S%d", value);
send_gcode(true, gcode); send_gcode(true, gcode);
} }
@@ -201,7 +199,7 @@ static void btn_retract(lv_event_t * e){
} }
send_gcode(true, "M83"); send_gcode(true, "M83");
send_gcode(true, "G1%20E-25%20F300"); send_gcode(true, "G1 E-25 F300");
} }
void temp_panel_init(lv_obj_t * panel){ void temp_panel_init(lv_obj_t * panel){

View File

@@ -1,5 +1,6 @@
#include "lvgl.h" #include "lvgl.h"
#include "ui_utils.h" #include "ui_utils.h"
#include "../core/data_setup.h"
lv_obj_t* lv_create_empty_panel(lv_obj_t* root) { lv_obj_t* lv_create_empty_panel(lv_obj_t* root) {
lv_obj_t* panel = lv_obj_create(root); lv_obj_t* panel = lv_obj_create(root);
@@ -23,4 +24,64 @@ void lv_layout_flex_row(lv_obj_t* obj, lv_flex_align_t allign, lv_coord_t pad_co
lv_obj_set_flex_align(obj, allign, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_flex_align(obj, allign, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(obj, pad_column, 0); lv_obj_set_style_pad_column(obj, pad_column, 0);
lv_obj_set_style_pad_row(obj, pad_row, 0); lv_obj_set_style_pad_row(obj, pad_row, 0);
}
static void lv_fullscreen_menu_close(lv_event_t * e){
lv_obj_t * panel = (lv_obj_t*)lv_event_get_user_data(e);
lv_obj_del(panel);
}
void lv_create_fullscreen_button_matrix_popup(lv_obj_t * root, lv_event_cb_t title, lv_button_column_t* columns, int column_count){
const auto full_panel_width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 3;
const auto full_panel_inner_width = full_panel_width - CYD_SCREEN_GAP_PX * 2 - 4;
const auto full_panel_height = CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX;
const auto full_panel_inner_height = full_panel_height - CYD_SCREEN_GAP_PX * 2 - 4;
auto column_width = full_panel_inner_width / column_count - CYD_SCREEN_GAP_PX;
auto column_height = full_panel_inner_height - CYD_SCREEN_GAP_PX - CYD_SCREEN_MIN_BUTTON_HEIGHT_PX;
lv_obj_t * panel = lv_obj_create(root);
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, 0);
lv_obj_set_size(panel, full_panel_width, full_panel_height);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * top_menu_row = lv_create_empty_panel(panel);
lv_obj_set_size(top_menu_row, full_panel_inner_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(top_menu_row, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_t * btn = lv_btn_create(top_menu_row);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(btn, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(btn, lv_fullscreen_menu_close, LV_EVENT_CLICKED, panel);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
lv_obj_center(label);
label = lv_label_create(top_menu_row);
lv_label_set_text(label, "-");
lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_add_event_cb(label, title, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_t * rows = lv_create_empty_panel(panel);
lv_obj_set_size(rows, full_panel_inner_width, column_height);
lv_obj_align(rows, LV_ALIGN_BOTTOM_LEFT, 0, 0);
lv_layout_flex_row(rows, LV_FLEX_ALIGN_SPACE_BETWEEN, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
for (int i = 0; i < column_count; i++){
lv_obj_t * column = lv_create_empty_panel(rows);
lv_obj_clear_flag(column, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(column, column_width, column_height);
lv_layout_flex_column(column, LV_FLEX_ALIGN_CENTER, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
for (int j = 0; j < columns[i].length; j++){
lv_obj_t * btn = lv_btn_create(column);
lv_obj_set_size(btn, column_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, columns[i].event, LV_EVENT_CLICKED, (void*)columns[i].data[j]);
label = lv_label_create(btn);
lv_label_set_text(label, columns[i].labels[j]);
lv_obj_center(label);
}
}
} }

View File

@@ -3,6 +3,14 @@
#define CYD_SCREEN_PANEL_WIDTH_PX \ #define CYD_SCREEN_PANEL_WIDTH_PX \
(CYD_SCREEN_WIDTH_PX - CYD_SCREEN_SIDEBAR_SIZE_PX) (CYD_SCREEN_WIDTH_PX - CYD_SCREEN_SIDEBAR_SIZE_PX)
typedef struct {
lv_event_cb_t event;
const char** labels;
const void** data;
int length;
} lv_button_column_t;
lv_obj_t* lv_create_empty_panel(lv_obj_t* root); lv_obj_t* lv_create_empty_panel(lv_obj_t* root);
void lv_layout_flex_column(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX); void lv_layout_flex_column(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX);
void lv_layout_flex_row(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX); void lv_layout_flex_row(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX);
void lv_create_fullscreen_button_matrix_popup(lv_obj_t * root, lv_event_cb_t title, lv_button_column_t* columns, int column_count);