From e21b16320801ee26f16dfdb2364d5081d5f3f4e6 Mon Sep 17 00:00:00 2001 From: ballaswag Date: Wed, 10 Apr 2024 02:56:30 -0700 Subject: [PATCH] Add touch calibration. Unload/Load takes temp/extrude len. Fixed spoolman optional field. Fixed current layer count resets. Fixed mini print status not showing when the same file is printed repeatedly. --- Makefile | 4 +- k1/scripts/guppy_cmd.cfg | 23 ++ lv_touch_calibration/lv_tc.c | 193 +++++++++++++ lv_touch_calibration/lv_tc.h | 115 ++++++++ lv_touch_calibration/lv_tc_config.h | 87 ++++++ lv_touch_calibration/lv_tc_indicator_img.c | 85 ++++++ lv_touch_calibration/lv_tc_screen.c | 311 +++++++++++++++++++++ lv_touch_calibration/lv_tc_screen.h | 80 ++++++ patches/0001-lv_driver_fb_ioctls.patch | 194 +------------ src/config.cpp | 4 +- src/extruder_panel.cpp | 18 +- src/homing_panel.cpp | 1 - src/main.cpp | 56 +++- src/print_status_panel.cpp | 6 +- src/spoolman_panel.cpp | 27 +- 15 files changed, 999 insertions(+), 205 deletions(-) create mode 100644 lv_touch_calibration/lv_tc.c create mode 100644 lv_touch_calibration/lv_tc.h create mode 100644 lv_touch_calibration/lv_tc_config.h create mode 100644 lv_touch_calibration/lv_tc_indicator_img.c create mode 100644 lv_touch_calibration/lv_tc_screen.c create mode 100644 lv_touch_calibration/lv_tc_screen.h diff --git a/Makefile b/Makefile index 6d46f90..6693a41 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ include $(LVGL_DIR)/lvgl/lvgl.mk include $(LVGL_DIR)/lv_drivers/lv_drivers.mk CSRCS += $(wildcard $(LVGL_DIR)/assets/*.c) - +CSRCS += $(wildcard $(LVGL_DIR)/lv_touch_calibration/*.c) ASSET_DIR = material ifdef GUPPY_SMALL_SCREEN @@ -76,7 +76,7 @@ DEPS = $(addprefix $(BUILD_OBJ_DIR)/, $(patsubst %.o, %.d, $( OBJS = $(AOBJS) $(COBJS) $(MAINOBJ) TARGET = $(addprefix $(BUILD_OBJ_DIR)/, $(patsubst ./%, %, $(OBJS))) -INC := -I./ -I./lvgl/ -I./spdlog/include -Ilibhv/include -Iwpa_supplicant/src/common +INC := -I./ -I./lvgl/ -I./lv_touch_calibration -I./spdlog/include -Ilibhv/include -Iwpa_supplicant/src/common LDLIBS := -lm DEFINES += -D _GNU_SOURCE -DSPDLOG_COMPILED_LIB diff --git a/k1/scripts/guppy_cmd.cfg b/k1/scripts/guppy_cmd.cfg index 73a00c2..54dffb2 100644 --- a/k1/scripts/guppy_cmd.cfg +++ b/k1/scripts/guppy_cmd.cfg @@ -85,3 +85,26 @@ gcode: command: /etc/init.d/S50dropbear timeout: 600.0 verbose: True + + +#### k1 load and unload ### +[gcode_macro _GUPPY_LOAD_MATERIAL] +gcode: + {% set extruder_temp = params.EXTRUDER_TEMP|default(240)|int %} + {% set extrude_len = params.EXTRUDE_LEN|default(35)|int %} + LOAD_MATERIAL_CLOSE_FAN2 + M109 S{extruder_temp} + G91 + G1 E{extrude_len} F180 + LOAD_MATERIAL_RESTORE_FAN2 # k1 stuff + +[gcode_macro _GUPPY_QUIT_MATERIAL] +gcode: + {% set extruder_temp = params.EXTRUDER_TEMP|default(240)|int %} + SAVE_GCODE_STATE NAME=myMoveState + M109 S{extruder_temp} + G91 + G1 E20 F180 + G1 E-30 F180 + G1 E-50 F2000 + RESTORE_GCODE_STATE NAME=myMoveState diff --git a/lv_touch_calibration/lv_tc.c b/lv_touch_calibration/lv_tc.c new file mode 100644 index 0000000..662e141 --- /dev/null +++ b/lv_touch_calibration/lv_tc.c @@ -0,0 +1,193 @@ +/********************* + * INCLUDES + *********************/ + +#include "lv_tc.h" + +#include "math.h" + +#ifdef ESP_PLATFORM + #include "esp_log.h" +#endif + +/********************** + * DEFINES + *********************/ +#define TAG "lv_tc" + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_tc_indev_drv_read_cb(lv_indev_drv_t *indevDrv, lv_indev_data_t *data); + + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_tc_coeff_t calibResult = {false, 0, 0, 0, 0, 0, 0}; + +static lv_obj_t *registeredTCScreen = NULL; +static bool (*registeredInputCb)(lv_obj_t *screenObj, lv_indev_data_t *data) = NULL; +static void (*registeredSaveCb)(lv_tc_coeff_t coeff) = NULL; + + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_tc_indev_drv_init(lv_indev_drv_t *indevDrv, void (*readCb)(lv_indev_drv_t *indevDrv, lv_indev_data_t *data)) { + lv_indev_drv_init(indevDrv); + indevDrv->type = LV_INDEV_TYPE_POINTER; + indevDrv->read_cb = lv_tc_indev_drv_read_cb; + indevDrv->user_data = readCb; +} + +void _lv_tc_register_input_cb(lv_obj_t *screenObj, bool (*inputCb)(lv_obj_t *screenObj, lv_indev_data_t *data)) { + registeredTCScreen = screenObj; + registeredInputCb = inputCb; +} + +void lv_tc_register_coeff_save_cb(void (*saveCb)(lv_tc_coeff_t coeff)) { + registeredSaveCb = saveCb; +} + +lv_tc_coeff_t* lv_tc_get_coeff() { + return &calibResult; +} + +void lv_tc_set_coeff(lv_tc_coeff_t coeff, bool save) { + calibResult = coeff; + + if(save) { + lv_tc_save_coeff(); + } +} + +#if defined CONFIG_USE_CUSTOM_LV_TC_COEFFICIENTS +void lv_tc_load_coeff_from_config() { + lv_tc_coeff_t coeff = { + true, + atoff(CONFIG_LV_TC_COEFFICIENT_A), + atoff(CONFIG_LV_TC_COEFFICIENT_B), + atoff(CONFIG_LV_TC_COEFFICIENT_C), + atoff(CONFIG_LV_TC_COEFFICIENT_D), + atoff(CONFIG_LV_TC_COEFFICIENT_E), + atoff(CONFIG_LV_TC_COEFFICIENT_F) + }; + lv_tc_set_coeff(coeff, false); +} +#endif + +void lv_tc_save_coeff() { + if(registeredSaveCb) { + registeredSaveCb(calibResult); + } +} + +void lv_tc_compute_coeff(lv_point_t *scrP, lv_point_t *tchP, bool save) { //The computation is explained here: https://www.maximintegrated.com/en/design/technical-documents/app-notes/5/5296.html + const lv_tc_val_t divisor = ( + (lv_tc_val_t)tchP[0].x * ((lv_tc_val_t)tchP[2].y - (lv_tc_val_t)tchP[1].y) + - (lv_tc_val_t)tchP[1].x * (lv_tc_val_t)tchP[2].y + + (lv_tc_val_t)tchP[1].y * (lv_tc_val_t)tchP[2].x + + (lv_tc_val_t)tchP[0].y * ((lv_tc_val_t)tchP[1].x - (lv_tc_val_t)tchP[2].x) + ); + + lv_tc_coeff_t result = { + true, + ( + (lv_tc_val_t)scrP[0].x * ((lv_tc_val_t)tchP[2].y - (lv_tc_val_t)tchP[1].y) + - (lv_tc_val_t)scrP[1].x * (lv_tc_val_t)tchP[2].y + + (lv_tc_val_t)scrP[2].x * (lv_tc_val_t)tchP[1].y + + ((lv_tc_val_t)scrP[1].x - (lv_tc_val_t)scrP[2].x) * (lv_tc_val_t)tchP[0].y + ) / divisor, + - ( + (lv_tc_val_t)scrP[0].x * ((lv_tc_val_t)tchP[2].x - (lv_tc_val_t)tchP[1].x) + - (lv_tc_val_t)scrP[1].x * (lv_tc_val_t)tchP[2].x + + (lv_tc_val_t)scrP[2].x * (lv_tc_val_t)tchP[1].x + + ((lv_tc_val_t)scrP[1].x - (lv_tc_val_t)scrP[2].x) * (lv_tc_val_t)tchP[0].x + ) / divisor, + ( + (lv_tc_val_t)scrP[0].x * ((lv_tc_val_t)tchP[1].y * (lv_tc_val_t)tchP[2].x - (lv_tc_val_t)tchP[1].x * (lv_tc_val_t)tchP[2].y) + + (lv_tc_val_t)tchP[0].x * ((lv_tc_val_t)scrP[1].x * (lv_tc_val_t)tchP[2].y - (lv_tc_val_t)scrP[2].x * (lv_tc_val_t)tchP[1].y) + + (lv_tc_val_t)tchP[0].y * ((lv_tc_val_t)scrP[2].x * (lv_tc_val_t)tchP[1].x - (lv_tc_val_t)scrP[1].x * (lv_tc_val_t)tchP[2].x) + ) / divisor, + ( + (lv_tc_val_t)scrP[0].y * ((lv_tc_val_t)tchP[2].y - (lv_tc_val_t)tchP[1].y) + - (lv_tc_val_t)scrP[1].y * (lv_tc_val_t)tchP[2].y + + (lv_tc_val_t)scrP[2].y * (lv_tc_val_t)tchP[1].y + + ((lv_tc_val_t)scrP[1].y - (lv_tc_val_t)scrP[2].y) * (lv_tc_val_t)tchP[0].y + ) / divisor, + - ( + (lv_tc_val_t)scrP[0].y * ((lv_tc_val_t)tchP[2].x - (lv_tc_val_t)tchP[1].x) + - (lv_tc_val_t)scrP[1].y * (lv_tc_val_t)tchP[2].x + + (lv_tc_val_t)scrP[2].y * (lv_tc_val_t)tchP[1].x + + ((lv_tc_val_t)scrP[1].y - (lv_tc_val_t)scrP[2].y) * (lv_tc_val_t)tchP[0].x + ) / divisor, + ( + (lv_tc_val_t)scrP[0].y * ((lv_tc_val_t)tchP[1].y * (lv_tc_val_t)tchP[2].x - (lv_tc_val_t)tchP[1].x * (lv_tc_val_t)tchP[2].y) + + (lv_tc_val_t)tchP[0].x * ((lv_tc_val_t)scrP[1].y * (lv_tc_val_t)tchP[2].y - (lv_tc_val_t)scrP[2].y * (lv_tc_val_t)tchP[1].y) + + (lv_tc_val_t)tchP[0].y * ((lv_tc_val_t)scrP[2].y * (lv_tc_val_t)tchP[1].x - (lv_tc_val_t)scrP[1].y * (lv_tc_val_t)tchP[2].x) + ) / divisor + }; + + lv_tc_set_coeff(result, save); + + #ifdef ESP_PLATFORM + ESP_LOGI(TAG, "touch calibration coefficients -> [a: %f, b: %f, c: %f, d: %f, e: %f, f: %f]", result.a, result.b, + result.c, result.d, result.e, result.f); + #endif +} + +lv_point_t _lv_tc_transform_point_indev(lv_indev_data_t *data) { + if(data->state == LV_INDEV_STATE_PRESSED) { + return lv_tc_transform_point(data->point); + } else { + //Reject invalid points if the touch panel is in released state + lv_point_t point = {0, 0}; + return point; + } +} + +lv_point_t lv_tc_transform_point(lv_point_t point) { + lv_point_t transformedPoint = point; + if (calibResult.isValid) { + transformedPoint.x = roundf((lv_tc_val_t)point.x * calibResult.a + (lv_tc_val_t)point.y * calibResult.b + calibResult.c); + transformedPoint.y = roundf((lv_tc_val_t)point.x * calibResult.d + (lv_tc_val_t)point.y * calibResult.e + calibResult.f); + + lv_disp_t *disp = lv_disp_get_default(); + if (disp->driver->rotated == LV_DISP_ROT_90 || disp->driver->rotated == LV_DISP_ROT_270) { + lv_coord_t tmp = transformedPoint.y; + transformedPoint.y = transformedPoint.x; + transformedPoint.x = lv_disp_get_ver_res(NULL) - tmp - 1; + } + } + + return transformedPoint; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_tc_indev_drv_read_cb(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) { + if(!indevDrv->user_data) return; + + //Call the actual indev read callback + ((void (*)(lv_indev_drv_t*, lv_indev_data_t*))indevDrv->user_data)(indevDrv, data); + + //Pass the results to an ongoing calibration if there is one + if(registeredTCScreen && registeredInputCb && registeredTCScreen == lv_scr_act()) { + if(!registeredInputCb(registeredTCScreen, data)) { + //Override state and point if the input has been handled by the registered calibration screen + data->state = LV_INDEV_STATE_RELEASED; + lv_point_t point = {0, 0}; + data->point = point; + + return; + } + } + + data->point = _lv_tc_transform_point_indev(data); +} \ No newline at end of file diff --git a/lv_touch_calibration/lv_tc.h b/lv_touch_calibration/lv_tc.h new file mode 100644 index 0000000..c671ab9 --- /dev/null +++ b/lv_touch_calibration/lv_tc.h @@ -0,0 +1,115 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "stdbool.h" +#include "lvgl.h" + + +/********************** + * TYPEDEFS + **********************/ + +typedef float lv_tc_val_t; + +typedef struct { + bool isValid; + + lv_tc_val_t a; + lv_tc_val_t b; + lv_tc_val_t c; + lv_tc_val_t d; + lv_tc_val_t e; + lv_tc_val_t f; +} lv_tc_coeff_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize a calibrated touch input driver. + * @param indevDrv pointer to an input driver + * @param readCb function pointer to read input driver data + */ +void lv_tc_indev_drv_init(lv_indev_drv_t *indevDrv, void (*readCb)(struct _lv_indev_drv_t *indevDrv, lv_indev_data_t *data)); + +/** + * Register a calibration screen to the modified input driver. + * NOT TO BE CALLED IN APPLICATION CODE + * @param screenObj pointer to the screen object + * @param inputCb function pointer to handle the input + */ +void _lv_tc_register_input_cb(lv_obj_t *screenObj, bool (*inputCb)(lv_obj_t *screenObj, lv_indev_data_t *data)); + + +/** + * Register a calibration coefficients save callback. + * It will be called on completion of the calibration steps. + * @param saveCb function pointer to save the coefficients (a lv_tc_coeff_t structure) + */ +void lv_tc_register_coeff_save_cb(void (*saveCb)(lv_tc_coeff_t coeff)); + + +/** + * Get the current calibration coefficients. + * @returns pointer to the coefficients structure (lv_tc_coeff_t*) + */ +lv_tc_coeff_t* lv_tc_get_coeff(); + +/** + * Set the current calibration coefficients. + * @param coeff the new coefficients structure (lv_tc_coeff_t) + * @param save select whether to save the coefficients or just to keep them in volatile storage + */ +void lv_tc_set_coeff(lv_tc_coeff_t coeff, bool save); + +/** + * Load previously calibrated coefficient data if defined in the config + */ +#if defined CONFIG_USE_CUSTOM_LV_TC_COEFFICIENTS +void lv_tc_load_coeff_from_config(); +#endif + +/** + * Save the current calibration coefficients. + */ +void lv_tc_save_coeff(); + +/** + * Compute calibration coefficients for a given set of points on the screen and touch panel. + * @param scrP pointer to the first element of an array containing the screen points + * @param tchP pointer to the first element of an array containing the touch panel points + * @param save select whether to save the coefficients or just to keep them in volatile storage + */ +void lv_tc_compute_coeff(lv_point_t *scrP, lv_point_t *tchP, bool save); + + +/** + * Transform a point read by an input driver into a point on the screen. + * NOT TO BE CALLED IN APPLICATION CODE + * @param data pointer to data from the input driver (lv_indev_data_t*) + * @returns the point (lv_point_t) + */ +lv_point_t _lv_tc_transform_point_indev(lv_indev_data_t *data); + +/** + * Transform a point on the touch panel into a point on the screen. + * @param point the point on the touch panel (lv_point_t) + * @returns the point on the screen (lv_point_t) + */ +lv_point_t lv_tc_transform_point(lv_point_t point); + + + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lv_touch_calibration/lv_tc_config.h b/lv_touch_calibration/lv_tc_config.h new file mode 100644 index 0000000..59d0aa6 --- /dev/null +++ b/lv_touch_calibration/lv_tc_config.h @@ -0,0 +1,87 @@ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +/* + This is the configuration file for the touch calibration system. + Change the defines below to match your application +*/ + + +#if defined CONFIG_USE_CUSTOM_LV_TC_START_MSG +#define LV_TC_START_MSG CONFIG_LV_TC_START_MSG +#else +#define LV_TC_START_MSG "Precisely touch the cursors (using a touch pen) to perform the calibration." +#endif + +#if defined CONFIG_USE_CUSTOM_LV_TC_READY_MSG +#define LV_TC_READY_MSG CONFIG_LV_TC_READY_MSG +#else +#define LV_TC_READY_MSG "Calibration completed. You can check it by moving the cursor around on the screen." +#endif + +#if defined CONFIG_USE_CUSTOM_LV_TC_RECALIBRATE_TXT +#define LV_TC_RECALIBRATE_TXT CONFIG_LV_TC_RECALIBRATE_TXT +#else +#define LV_TC_RECALIBRATE_TXT "Recalibrate" +#endif + +#if defined CONFIG_USE_CUSTOM_LV_TC_ACCEPT_TXT +#define LV_TC_ACCEPT_TXT CONFIG_LV_TC_ACCEPT_TXT +#else +#define LV_TC_ACCEPT_TXT "Accept" +#endif + +//The format of the timeout string on the recalibration button. +//Appends to LV_TC_RECALIBRATE_TXT if LV_TC_RECALIB_TIMEOUT_S is set greater than 0 +#define LV_TC_RECALIBRATE_TIMEOUT_FORMAT " (%d)" + + +#if defined CONFIG_USE_CUSTOM_LV_TC_SCREEN_POINTS +#define LV_TC_SCREEN_POINT_1_X CONFIG_LV_TC_SCREEN_POINT_1_X +#define LV_TC_SCREEN_POINT_1_Y CONFIG_LV_TC_SCREEN_POINT_1_Y +#define LV_TC_SCREEN_POINT_2_X CONFIG_LV_TC_SCREEN_POINT_2_X +#define LV_TC_SCREEN_POINT_2_Y CONFIG_LV_TC_SCREEN_POINT_2_Y +#define LV_TC_SCREEN_POINT_3_X CONFIG_LV_TC_SCREEN_POINT_3_X +#define LV_TC_SCREEN_POINT_3_Y CONFIG_LV_TC_SCREEN_POINT_3_Y +#define LV_TC_SCREEN_ENABLE_AUTO_POINTS 0 +#else +#define LV_TC_SCREEN_POINT_1_X 80 +#define LV_TC_SCREEN_POINT_1_Y 150 +#define LV_TC_SCREEN_POINT_2_X 192 +#define LV_TC_SCREEN_POINT_2_Y 656 +#define LV_TC_SCREEN_POINT_3_X 720 +#define LV_TC_SCREEN_POINT_3_Y 80 +//Set to 1 to make the system choose the points for the calibration automatically +//based on your screen resolution +#define LV_TC_SCREEN_ENABLE_AUTO_POINTS 1 +#endif + +//The default points (will be overridden if LV_TC_SCREEN_ENABLE_AUTO_POINTS is enabled) +#define LV_TC_SCREEN_DEFAULT_POINTS {{LV_TC_SCREEN_POINT_1_X, LV_TC_SCREEN_POINT_1_Y}, {LV_TC_SCREEN_POINT_2_X, LV_TC_SCREEN_POINT_2_Y}, {LV_TC_SCREEN_POINT_3_X, LV_TC_SCREEN_POINT_3_Y}} + + +//Prevent user input immediately after the calibration is started by adding a delay (in milliseconds) +//When the process was started by pressing the screen, this makes sure that +//this press is not falsely registered as the first calibration point +//Set to 0 to disable +#if defined CONFIG_LV_TC_START_DELAY_MS +#define LV_TC_START_DELAY_MS CONFIG_LV_TC_START_DELAY_MS +#else +#define LV_TC_START_DELAY_MS 1000 +#endif + +//Make the system restart the calibration automatically after a given timeout (in seconds) +//if it is not accepted by the user. This makes sure that a faulty calibration can +//always be restarted - even when it is impossible to press the 'recalibrate' button +//Set to 0 to disable +#if defined CONFIG_LV_TC_RECALIB_TIMEOUT_S +#define LV_TC_RECALIB_TIMEOUT_S CONFIG_LV_TC_RECALIB_TIMEOUT_S +#else +#define LV_TC_RECALIB_TIMEOUT_S 30 +#endif + +#ifdef __cplusplus +} +#endif diff --git a/lv_touch_calibration/lv_tc_indicator_img.c b/lv_touch_calibration/lv_tc_indicator_img.c new file mode 100644 index 0000000..94e9a33 --- /dev/null +++ b/lv_touch_calibration/lv_tc_indicator_img.c @@ -0,0 +1,85 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_LV_TC_INDICATOR_IMG +#define LV_ATTRIBUTE_IMG_LV_TC_INDICATOR_IMG +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LV_TC_INDICATOR_IMG uint8_t lv_tc_indicator_img_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xd0, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t lv_tc_indicator_img = { + .header.cf = LV_IMG_CF_ALPHA_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 50, + .header.h = 50, + .data_size = 650, + .data = lv_tc_indicator_img_map, +}; diff --git a/lv_touch_calibration/lv_tc_screen.c b/lv_touch_calibration/lv_tc_screen.c new file mode 100644 index 0000000..2192db8 --- /dev/null +++ b/lv_touch_calibration/lv_tc_screen.c @@ -0,0 +1,311 @@ +/********************* + * INCLUDES + *********************/ + +#include "lv_tc_screen.h" + +#include "math.h" + +#include "lv_tc.h" +#include "lv_tc_config.h" + + +/********************* + * DEFINES + *********************/ + +#define MY_CLASS &lv_tc_screen_class + +#define STEP_INIT 0 +#define STEP_FIRST 1 +#define STEP_FINISH 4 +#define INDICATOR_SIZE 50 + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_tc_screen_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj); + +static void lv_tc_screen_auto_set_points(lv_obj_t *screenObj); + +static bool lv_tc_screen_input_cb(lv_obj_t *screenObj, lv_indev_data_t *data); +static void lv_tc_screen_process_input(lv_obj_t *screenObj, lv_point_t tchPoint); +static void lv_tc_screen_step(lv_obj_t *screenObj, uint8_t step, lv_point_t tchPoint); +static void lv_tc_screen_set_indicator_pos(lv_obj_t *screenObj, lv_point_t point, bool visible); + +static void lv_tc_screen_finish(lv_obj_t *screenObj); +static void lv_tc_screen_ready(lv_obj_t *screenObj); + +static void lv_tc_screen_recalibrate_btn_click_cb(lv_event_t *event); +static void lv_tc_screen_accept_btn_click_cb(lv_event_t *event); + +static void lv_tc_screen_recalibrate_timer(lv_timer_t *timer); +static void lv_tc_screen_start_delay_timer(lv_timer_t *timer); + +/********************** + * STATIC VARIABLES + **********************/ + +const lv_obj_class_t lv_tc_screen_class = { + .constructor_cb = lv_tc_screen_constructor, + .instance_size = sizeof(lv_tc_screen_t), + .base_class = &lv_obj_class +}; + + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_obj_t* lv_tc_screen_create() { + lv_obj_t *obj = lv_obj_class_create_obj(MY_CLASS, NULL); + lv_obj_class_init_obj(obj); + return obj; +} + +void lv_tc_screen_set_points(lv_obj_t* screenObj, lv_point_t *scrPoints) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + memcpy(tCScreenObj->scrPoints, scrPoints, sizeof(lv_point_t) * 3); +} + +void lv_tc_screen_start(lv_obj_t *screenObj) { + lv_tc_screen_start_with_config(screenObj, LV_TC_START_DELAY_ENABLED); +} + +void lv_tc_screen_start_with_config(lv_obj_t* screenObj, lv_tc_start_delay_t startDelayEnabled) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + if(tCScreenObj->recalibrateTimer) { + lv_timer_del(tCScreenObj->recalibrateTimer); + tCScreenObj->recalibrateTimer = NULL; + } + + lv_label_set_text_static(tCScreenObj->msgLabelObj, LV_TC_START_MSG); + lv_obj_add_flag(tCScreenObj->recalibrateBtnObj, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(tCScreenObj->acceptBtnObj, LV_OBJ_FLAG_HIDDEN); + + lv_obj_align(tCScreenObj->msgLabelObj, LV_ALIGN_CENTER, 0, -50); + lv_obj_align_to(tCScreenObj->recalibrateBtnObj, tCScreenObj->msgLabelObj, LV_ALIGN_OUT_BOTTOM_MID, -140, 20); + lv_obj_align_to(tCScreenObj->acceptBtnObj, tCScreenObj->msgLabelObj, LV_ALIGN_OUT_BOTTOM_MID, 140, 20); + + + #if LV_TC_START_DELAY_MS + lv_point_t point = {0, 0}; + lv_tc_screen_step(screenObj, STEP_INIT, point); + #endif + + //Register this screen to the calibrated indev driver + _lv_tc_register_input_cb(screenObj, lv_tc_screen_input_cb); + + //Start the input delay timer (or calibrate immediately) + #if LV_TC_START_DELAY_MS + tCScreenObj->startDelayTimer = lv_timer_create(lv_tc_screen_start_delay_timer, LV_TC_START_DELAY_MS, screenObj); + lv_timer_set_repeat_count(tCScreenObj->startDelayTimer, 1); + #else + lv_point_t point = {0, 0}; + lv_tc_screen_step(screenObj, STEP_FIRST, point); + #endif +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_tc_screen_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj) { + LV_UNUSED(class_p); + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)obj; + + tCScreenObj->inputEnabled = true; + tCScreenObj->startDelayTimer = NULL; + tCScreenObj->recalibrateTimer = NULL; + + #if LV_TC_SCREEN_ENABLE_AUTO_POINTS + lv_tc_screen_auto_set_points(obj); + #else + lv_point_t points[3] = LV_TC_SCREEN_DEFAULT_POINTS; + lv_tc_screen_set_points(obj, points); + #endif + + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + + + LV_IMG_DECLARE(lv_tc_indicator_img); + + tCScreenObj->indicatorObj = lv_img_create(obj); + lv_img_set_src(tCScreenObj->indicatorObj, &lv_tc_indicator_img); + lv_obj_set_style_img_recolor_opa(tCScreenObj->indicatorObj, LV_OPA_COVER, 0); + lv_obj_set_style_img_recolor(tCScreenObj->indicatorObj, lv_color_white(), 0); + lv_obj_clear_flag(tCScreenObj->indicatorObj, LV_OBJ_FLAG_CLICKABLE); + + + tCScreenObj->msgLabelObj = lv_label_create(obj); + lv_obj_set_style_text_align(tCScreenObj->msgLabelObj, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); + lv_obj_set_size(tCScreenObj->msgLabelObj, lv_pct(60), LV_SIZE_CONTENT); + + + tCScreenObj->recalibrateBtnObj = lv_btn_create(obj); + lv_obj_set_size(tCScreenObj->recalibrateBtnObj, lv_pct(35), LV_SIZE_CONTENT); + lv_obj_add_event_cb(tCScreenObj->recalibrateBtnObj, lv_tc_screen_recalibrate_btn_click_cb, LV_EVENT_CLICKED, obj); + + lv_obj_t *recalibrateBtnLabelObj = lv_label_create(tCScreenObj->recalibrateBtnObj); + #if LV_TC_RECALIB_TIMEOUT_S == 0 + lv_label_set_text_static(recalibrateBtnLabelObj, LV_TC_RECALIBRATE_TXT); + #endif + lv_obj_center(recalibrateBtnLabelObj); + + + tCScreenObj->acceptBtnObj = lv_btn_create(obj); + lv_obj_set_size(tCScreenObj->acceptBtnObj, lv_pct(35), LV_SIZE_CONTENT); + lv_obj_add_event_cb(tCScreenObj->acceptBtnObj, lv_tc_screen_accept_btn_click_cb, LV_EVENT_CLICKED, obj); + + lv_obj_t *acceptBtnLabelObj = lv_label_create(tCScreenObj->acceptBtnObj); + lv_label_set_text_static(acceptBtnLabelObj, LV_TC_ACCEPT_TXT); + lv_obj_center(acceptBtnLabelObj); + + lv_obj_move_foreground(tCScreenObj->indicatorObj); +} + +static void lv_tc_screen_auto_set_points(lv_obj_t *screenObj) { + //Choose the on-screen calibration points based on the active display driver's resolution + lv_coord_t marginH = lv_disp_get_hor_res(NULL) * 0.15; + lv_coord_t marginV = lv_disp_get_ver_res(NULL) * 0.15; + lv_coord_t margin = (marginH < marginV) ? marginH: marginV; + + lv_point_t points[3] = { + { margin , (float)lv_disp_get_ver_res(NULL) * 0.3 }, + {(float)lv_disp_get_hor_res(NULL) * 0.4 , lv_disp_get_ver_res(NULL) - margin}, + { lv_disp_get_hor_res(NULL) - margin, margin } + }; + lv_tc_screen_set_points(screenObj, points); +} + +static bool lv_tc_screen_input_cb(lv_obj_t *screenObj, lv_indev_data_t *data) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + if(tCScreenObj->inputEnabled && data->state == LV_INDEV_STATE_PRESSED) { + lv_tc_screen_process_input(screenObj, data->point); + } + + if(data->state == LV_INDEV_STATE_RELEASED) { + tCScreenObj->inputEnabled = true; + } + + return tCScreenObj->currentStep >= STEP_FINISH; +} + +static void lv_tc_screen_process_input(lv_obj_t* screenObj, lv_point_t tchPoint) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + if(tCScreenObj->currentStep > STEP_INIT) { + if(tCScreenObj->currentStep < STEP_FINISH) { + //Block further input until released + tCScreenObj->inputEnabled = false; + //Go to the next calibration step + lv_tc_screen_step(screenObj, tCScreenObj->currentStep + 1, tchPoint); + } else { + //When the calibration is completed, show the cursor at touch position + lv_tc_screen_set_indicator_pos(screenObj, lv_tc_transform_point(tchPoint), true); + } + } +} + +static void lv_tc_screen_step(lv_obj_t* screenObj, uint8_t step, lv_point_t tchPoint) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + tCScreenObj->currentStep = step; + + if(step > STEP_FIRST) { + //Store the touch controller output for the current point + tCScreenObj->tchPoints[step - 2] = tchPoint; + } + if(step == STEP_FINISH) { + //Finish the calibration + lv_tc_compute_coeff(tCScreenObj->scrPoints, tCScreenObj->tchPoints, false); + lv_tc_screen_finish(screenObj); + return; + } + + lv_tc_screen_set_indicator_pos(screenObj, tCScreenObj->scrPoints[step - 1], step > STEP_INIT); +} + +static void lv_tc_screen_set_indicator_pos(lv_obj_t* screenObj, lv_point_t point, bool visible) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + lv_obj_set_pos(tCScreenObj->indicatorObj, point.x - INDICATOR_SIZE / 2, point.y - INDICATOR_SIZE / 2); + + if(visible) { + lv_obj_clear_flag(tCScreenObj->indicatorObj, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(tCScreenObj->indicatorObj, LV_OBJ_FLAG_HIDDEN); + } +} + +static void lv_tc_screen_finish(lv_obj_t *screenObj) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + //Update the UI + lv_label_set_text_static(tCScreenObj->msgLabelObj, LV_TC_READY_MSG); + lv_obj_clear_flag(tCScreenObj->recalibrateBtnObj, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(tCScreenObj->acceptBtnObj, LV_OBJ_FLAG_HIDDEN); + + lv_obj_align(tCScreenObj->msgLabelObj, LV_ALIGN_CENTER, 0, -50); + lv_obj_align_to(tCScreenObj->recalibrateBtnObj, tCScreenObj->msgLabelObj, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); + lv_obj_set_x(tCScreenObj->recalibrateBtnObj, lv_pct(12)); + lv_obj_align_to(tCScreenObj->acceptBtnObj, tCScreenObj->msgLabelObj, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); + lv_obj_set_x(tCScreenObj->acceptBtnObj, lv_pct(53)); + + + //Start the recalibration timeout + #if LV_TC_RECALIB_TIMEOUT_S + lv_label_set_text_fmt(lv_obj_get_child(tCScreenObj->recalibrateBtnObj, 0), LV_TC_RECALIBRATE_TXT LV_TC_RECALIBRATE_TIMEOUT_FORMAT, (int)LV_TC_RECALIB_TIMEOUT_S); + + tCScreenObj->recalibrateTimer = lv_timer_create(lv_tc_screen_recalibrate_timer, 1000, screenObj); + lv_timer_set_repeat_count(tCScreenObj->recalibrateTimer, LV_TC_RECALIB_TIMEOUT_S); + #endif +} + +static void lv_tc_screen_ready(lv_obj_t *screenObj) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)screenObj; + + //Store the calibration coefficients in NVM + lv_tc_save_coeff(); + + if(tCScreenObj->recalibrateTimer) { + lv_timer_del(tCScreenObj->recalibrateTimer); + tCScreenObj->recalibrateTimer = NULL; + } + + //Indicate that the calibration is complete and the screen can be closed + lv_event_send(screenObj, LV_EVENT_READY, lv_tc_get_coeff()); +} + + +static void lv_tc_screen_recalibrate_btn_click_cb(lv_event_t *event) { + lv_tc_screen_start(event->user_data); +} + +static void lv_tc_screen_accept_btn_click_cb(lv_event_t *event) { + lv_tc_screen_ready(event->user_data); +} + + +static void lv_tc_screen_recalibrate_timer(lv_timer_t *timer) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)timer->user_data; + + if(timer->repeat_count == 0) { + //Restart when timed out + lv_tc_screen_start(tCScreenObj); + return; + } + lv_label_set_text_fmt(lv_obj_get_child(tCScreenObj->recalibrateBtnObj, 0), LV_TC_RECALIBRATE_TXT LV_TC_RECALIBRATE_TIMEOUT_FORMAT, (int)timer->repeat_count); +} + +static void lv_tc_screen_start_delay_timer(lv_timer_t *timer) { + lv_tc_screen_t *tCScreenObj = (lv_tc_screen_t*)timer->user_data; + + lv_point_t point = {0, 0}; + lv_tc_screen_step(tCScreenObj, STEP_FIRST, point); +} diff --git a/lv_touch_calibration/lv_tc_screen.h b/lv_touch_calibration/lv_tc_screen.h new file mode 100644 index 0000000..1fbb14d --- /dev/null +++ b/lv_touch_calibration/lv_tc_screen.h @@ -0,0 +1,80 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "stdbool.h" + +#include "lvgl.h" + + +/********************** + * TYPEDEFS + **********************/ + +typedef enum { + LV_TC_START_DELAY_DISABLED = 0, + LV_TC_START_DELAY_ENABLED = 1 +} lv_tc_start_delay_t; + +typedef struct { + lv_obj_t screenObj; /* The screen object */ + lv_obj_t *indicatorObj; /* The crosshair image object */ + lv_obj_t *msgLabelObj; /* The message label object */ + lv_obj_t *recalibrateBtnObj; /* The recalibration button object */ + lv_obj_t *acceptBtnObj; /* The confirmation button object */ + bool inputEnabled; /* A flag which determines whether the screen accepts input from the touch driver. + Makes sure pressing the crosshair is only handled once until releasing */ + lv_point_t scrPoints[3]; /* The points to be pressed (in the coordinate space of the screen) */ + lv_point_t tchPoints[3]; /* The touched points during the current calibration (in the coordinate space of the touch driver) */ + uint8_t currentStep; /* The current calibration step */ + lv_timer_t *startDelayTimer; /* The timer for delaying user input after opening the screen */ + lv_timer_t *recalibrateTimer; /* The timer for automatic recalibration */ +} lv_tc_screen_t; + +extern const lv_obj_class_t lv_tc_screen_class; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a touch calibration screen. + * @returns a pointer to the newly created screen (lv_obj_t*) + */ +lv_obj_t* lv_tc_screen_create(); + +/** + * Set the points on screen to perform the calibration with. + * @param screenObj a pointer to the calibration screen (lv_obj_t*) + * @param scrPoints a pointer to the first element of an array holding the three new points + */ +void lv_tc_screen_set_points(lv_obj_t *screenObj, lv_point_t *scrPoints); + +/** + * Start the calibration process. + * This function can also be used to restart an already ongoing calibration process. + * @param screenObj a pointer to the calibration screen (lv_obj_t*) + */ +void lv_tc_screen_start(lv_obj_t *screenObj); + +/** + * Start the calibration process. + * This function can also be used to restart an already ongoing calibration process. + * @param screenObj a pointer to the calibration screen (lv_obj_t*) + * @param startDelayEnabled set whether to enable a delay before starting the calibration + */ +void lv_tc_screen_start_with_config(lv_obj_t *screenObj, lv_tc_start_delay_t startDelayEnabled); + + + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/patches/0001-lv_driver_fb_ioctls.patch b/patches/0001-lv_driver_fb_ioctls.patch index 592a1c2..3a5fb73 100644 --- a/patches/0001-lv_driver_fb_ioctls.patch +++ b/patches/0001-lv_driver_fb_ioctls.patch @@ -1,37 +1,8 @@ diff --git a/display/fbdev.c b/display/fbdev.c -index fe8d2ff..f4a3316 100644 +index fe8d2ff..e8f0928 100644 --- a/display/fbdev.c +++ b/display/fbdev.c -@@ -140,6 +140,28 @@ void fbdev_init(void) - perror("Error reading variable information"); - return; - } -+ -+ /* printf("Original config: %dx%d (v: %dx%d), %dbpp, rotate: %d\n", vinfo.xres, vinfo.yres, */ -+ /* vinfo.xres_virtual, vinfo.yres_virtual, vinfo.bits_per_pixel, vinfo.rotate); */ -+ -+ /* int rotation = 270; */ -+ /* if((vinfo.rotate ^ rotation) & 1) { */ -+ /* unsigned int xres = vinfo.yres; */ -+ /* vinfo.yres = vinfo.xres; */ -+ /* vinfo.xres = xres; */ -+ /* vinfo.xres_virtual = vinfo.xres; */ -+ /* vinfo.yres_virtual = vinfo.yres * 2; */ -+ /* if(vinfo.yoffset == xres) */ -+ /* vinfo.yoffset = vinfo.yres; */ -+ /* } */ -+ /* vinfo.rotate = rotation; */ -+ /* if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo) == -1) { */ -+ /* perror("Failed to rotate framebuffer"); */ -+ /* } */ -+ -+ /* printf("new config: %dx%d (v: %dx%d), %dbpp, rotate: %d\n", vinfo.xres, vinfo.yres, */ -+ /* vinfo.xres_virtual, vinfo.yres_virtual, vinfo.bits_per_pixel, vinfo.rotate); */ -+ - #endif /* USE_BSD_FBDEV */ - - LV_LOG_INFO("%dx%d, %dbpp", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); -@@ -166,6 +188,30 @@ void fbdev_exit(void) +@@ -166,6 +166,30 @@ void fbdev_exit(void) close(fbfd); } @@ -76,10 +47,10 @@ index b7f2c81..5e91f91 100644 void fbdev_get_sizes(uint32_t *width, uint32_t *height, uint32_t *dpi); /** diff --git a/indev/evdev.c b/indev/evdev.c -index 4d46b5b..84fb475 100644 +index 4d46b5b..d53ae53 100644 --- a/indev/evdev.c +++ b/indev/evdev.c -@@ -218,13 +218,137 @@ void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +@@ -218,25 +218,14 @@ void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data) return ; /*Store the collected data*/ @@ -90,155 +61,18 @@ index 4d46b5b..84fb475 100644 data->point.x = evdev_root_x; data->point.y = evdev_root_y; -#endif -+ data->state = evdev_button; -+ -+ if(data->point.x < 0) -+ data->point.x = 0; -+ if(data->point.y < 0) -+ data->point.y = 0; -+ if(data->point.x >= drv->disp->driver->hor_res) -+ data->point.x = drv->disp->driver->hor_res - 1; -+ if(data->point.y >= drv->disp->driver->ver_res) -+ data->point.y = drv->disp->driver->ver_res - 1; -+ -+ return ; -+} -+ -+void evdev_read_calibrated(lv_indev_drv_t * drv, lv_indev_data_t * data) -+{ -+ struct input_event in; -+ -+ while(read(evdev_fd, &in, sizeof(struct input_event)) > 0) { -+ if(in.type == EV_REL) { -+ if(in.code == REL_X) -+ #if EVDEV_SWAP_AXES -+ evdev_root_y += in.value; -+ #else -+ evdev_root_x += in.value; -+ #endif -+ else if(in.code == REL_Y) -+ #if EVDEV_SWAP_AXES -+ evdev_root_x += in.value; -+ #else -+ evdev_root_y += in.value; -+ #endif -+ } else if(in.type == EV_ABS) { -+ if(in.code == ABS_X) -+ #if EVDEV_SWAP_AXES -+ evdev_root_y = in.value; -+ #else -+ evdev_root_x = in.value; -+ #endif -+ else if(in.code == ABS_Y) -+ #if EVDEV_SWAP_AXES -+ evdev_root_x = in.value; -+ #else -+ evdev_root_y = in.value; -+ #endif -+ else if(in.code == ABS_MT_POSITION_X) -+ #if EVDEV_SWAP_AXES -+ evdev_root_y = in.value; -+ #else -+ evdev_root_x = in.value; -+ #endif -+ else if(in.code == ABS_MT_POSITION_Y) -+ #if EVDEV_SWAP_AXES -+ evdev_root_x = in.value; -+ #else -+ evdev_root_y = in.value; -+ #endif -+ else if(in.code == ABS_MT_TRACKING_ID) { -+ if(in.value == -1) -+ evdev_button = LV_INDEV_STATE_REL; -+ else if(in.value == 0) -+ evdev_button = LV_INDEV_STATE_PR; -+ } -+ } else if(in.type == EV_KEY) { -+ if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) { -+ if(in.value == 0) -+ evdev_button = LV_INDEV_STATE_REL; -+ else if(in.value == 1) -+ evdev_button = LV_INDEV_STATE_PR; -+ } else if(drv->type == LV_INDEV_TYPE_KEYPAD) { -+#if USE_XKB -+ data->key = xkb_process_key(in.code, in.value != 0); -+#else -+ switch(in.code) { -+ case KEY_BACKSPACE: -+ data->key = LV_KEY_BACKSPACE; -+ break; -+ case KEY_ENTER: -+ data->key = LV_KEY_ENTER; -+ break; -+ case KEY_PREVIOUS: -+ data->key = LV_KEY_PREV; -+ break; -+ case KEY_NEXT: -+ data->key = LV_KEY_NEXT; -+ break; -+ case KEY_UP: -+ data->key = LV_KEY_UP; -+ break; -+ case KEY_LEFT: -+ data->key = LV_KEY_LEFT; -+ break; -+ case KEY_RIGHT: -+ data->key = LV_KEY_RIGHT; -+ break; -+ case KEY_DOWN: -+ data->key = LV_KEY_DOWN; -+ break; -+ case KEY_TAB: -+ data->key = LV_KEY_NEXT; -+ break; -+ default: -+ data->key = 0; -+ break; -+ } -+#endif /* USE_XKB */ -+ if (data->key != 0) { -+ /* Only record button state when actual output is produced to prevent widgets from refreshing */ -+ data->state = (in.value) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; -+ } -+ evdev_key_val = data->key; -+ evdev_button = data->state; -+ return; -+ } -+ } -+ } -+ -+ if(drv->type == LV_INDEV_TYPE_KEYPAD) { -+ /* No data retrieved */ -+ data->key = evdev_key_val; -+ data->state = evdev_button; -+ return; -+ } -+ if(drv->type != LV_INDEV_TYPE_POINTER) -+ return ; -+ /*Store the collected data*/ -+ -+ data->point.x = map(evdev_root_x, EVDEV_HOR_MIN, EVDEV_HOR_MAX, 0, drv->disp->driver->hor_res); -+ data->point.y = map(evdev_root_y, EVDEV_VER_MIN, EVDEV_VER_MAX, 0, drv->disp->driver->ver_res); - +- data->state = evdev_button; -@@ -240,6 +364,7 @@ void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data) + if(data->point.x < 0) + data->point.x = 0; + if(data->point.y < 0) + data->point.y = 0; +- if(data->point.x >= drv->disp->driver->hor_res) +- data->point.x = drv->disp->driver->hor_res - 1; +- if(data->point.y >= drv->disp->driver->ver_res) +- data->point.y = drv->disp->driver->ver_res - 1; +- return ; } -+ - /********************** - * STATIC FUNCTIONS - **********************/ -diff --git a/indev/evdev.h b/indev/evdev.h -index c1b2280..7839fb4 100644 ---- a/indev/evdev.h -+++ b/indev/evdev.h -@@ -58,6 +58,7 @@ bool evdev_set_file(char* dev_name); - */ - void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data); - -+void evdev_read_calibrated(lv_indev_drv_t * drv, lv_indev_data_t * data); - - /********************** - * MACROS diff --git a/src/config.cpp b/src/config.cpp index 58f33f7..d4d05ed 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -60,8 +60,8 @@ void Config::init(std::string config_path) { json cooldown_conf = {{ "cooldown", "SET_HEATER_TEMPERATURE HEATER=extruder TARGET=0\nSET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=0"}}; json default_macros_conf = { - {"load_filament", "LOAD_MATERIAL"}, - {"unload_filament", "QUIT_MATERIAL"} + {"load_filament", "_GUPPY_LOAD_MATERIAL"}, + {"unload_filament", "_GUPPY_QUIT_MATERIAL"} }; if (stat(config_path.c_str(), &buffer) == 0) { diff --git a/src/extruder_panel.cpp b/src/extruder_panel.cpp index f864ddf..6667316 100644 --- a/src/extruder_panel.cpp +++ b/src/extruder_panel.cpp @@ -199,11 +199,25 @@ void ExtruderPanel::handle_callback(lv_event_t *e) { } if (btn == unload_btn.get_container()) { - ws.gcode_script(unload_filament_macro); + if (unload_filament_macro == "_GUPPY_QUIT_MATERIAL") { + const char *temp = lv_btnmatrix_get_btn_text(temp_selector.get_selector(), + temp_selector.get_selected_idx()); + ws.gcode_script(fmt::format("{} EXTRUDER_TEMP={}", unload_filament_macro, temp)); + } else { + ws.gcode_script(unload_filament_macro); + } } if (btn == load_btn.get_container()) { - ws.gcode_script(load_filament_macro); + if (load_filament_macro == "_GUPPY_LOAD_MATERIAL") { + const char *temp = lv_btnmatrix_get_btn_text(temp_selector.get_selector(), + temp_selector.get_selected_idx()); + const char *len = lv_btnmatrix_get_btn_text(length_selector.get_selector(), + length_selector.get_selected_idx()); + ws.gcode_script(fmt::format("{} EXTRUDER_TEMP={} EXTRUDE_LEN={}", load_filament_macro, temp, len)); + } else { + ws.gcode_script(load_filament_macro); + } } if (btn == cooldown_btn.get_container()) { diff --git a/src/homing_panel.cpp b/src/homing_panel.cpp index 92c4492..83bf197 100644 --- a/src/homing_panel.cpp +++ b/src/homing_panel.cpp @@ -210,7 +210,6 @@ void HomingPanel::handle_callback(lv_event_t *event) { } void HomingPanel::handle_selector_cb(lv_event_t *event) { - HomingPanel *panel = (HomingPanel*)event->user_data; lv_obj_t * obj = lv_event_get_target(event); uint32_t idx = lv_btnmatrix_get_selected_btn(obj); distance_selector.set_selected_idx(idx); diff --git a/src/main.cpp b/src/main.cpp index 2feaab5..2010917 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,9 @@ #include "lvgl/lvgl.h" #include "lv_drivers/display/fbdev.h" #include "lv_drivers/indev/evdev.h" +#include "lv_tc.h" +#include "lv_tc_screen.h" + #include #include #include @@ -67,6 +70,19 @@ static void new_theme_apply_cb(lv_theme_t * th, lv_obj_t * obj) } } +static void handle_calibrated(lv_event_t *event) { + spdlog::info("finished calibration"); + lv_obj_t *main_screen = (lv_obj_t*)event->user_data; + lv_disp_load_scr(main_screen); +} + +static void save_calibration_coeff(lv_tc_coeff_t coeff) { + Config *conf = Config::get_instance(); + conf->set>("/touch_calibration_coeff", + {coeff.a, coeff.b, coeff.c, coeff.d, coeff.e, coeff.f}); + conf->save(); +} + #ifndef SIMULATOR std::atomic_bool is_sleeping(false); @@ -169,6 +185,31 @@ int main(void) lv_obj_set_style_bg_opa(screen_saver, LV_OPA_100, 0); lv_obj_move_background(screen_saver); + lv_obj_t *main_screen = lv_disp_get_scr_act(NULL); + auto touch_calibrated = conf->get_json("/touch_calibrated"); + if (!touch_calibrated.is_null()) { + auto is_calibrated = touch_calibrated.template get(); + if (is_calibrated) { + auto calibration_coeff = conf->get_json("/touch_calibration_coeff"); + if (calibration_coeff.is_null()) { + lv_tc_register_coeff_save_cb(save_calibration_coeff); + lv_obj_t *touch_calibrate_scr = lv_tc_screen_create(); + + lv_disp_load_scr(touch_calibrate_scr); + + lv_tc_screen_start(touch_calibrate_scr); + lv_obj_add_event_cb(touch_calibrate_scr, handle_calibrated, LV_EVENT_READY, main_screen); + spdlog::info("running touch calibration"); + } else { + // load calibration data + auto c = calibration_coeff.template get>(); + lv_tc_coeff_t coeff = { true, c[0], c[1], c[2], c[3], c[4], c[5] }; + lv_tc_set_coeff(coeff, false); + spdlog::info("loaded calibration coefficients"); + } + } + } + /*Handle LitlevGL tasks (tickless mode)*/ while(1) { lv_lock.lock(); @@ -252,14 +293,12 @@ static void hal_init(void) { if (!rotate.is_null()) { auto rotate_value = rotate.template get(); if (rotate_value > 0 && rotate_value < 4) { - disp_drv.sw_rotate = 1; - disp_drv.rotated = rotate_value; + disp_drv.sw_rotate = 1; + disp_drv.rotated = rotate_value; } } - lv_disp_drv_register(&disp_drv); spdlog::debug("resolution {} x {}", width, height); - lv_disp_t * disp = lv_disp_drv_register(&disp_drv); lv_theme_t * th = height <= 480 ? lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), true, &lv_font_montserrat_12) @@ -268,20 +307,19 @@ static void hal_init(void) { evdev_init(); static lv_indev_drv_t indev_drv_1; - lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/ + indev_drv_1.read_cb = evdev_read; // no calibration indev_drv_1.type = LV_INDEV_TYPE_POINTER; - /*This function will be called periodically (by the library) to get the mouse position and state*/ - indev_drv_1.read_cb = evdev_read; auto touch_calibrated = conf->get_json("/touch_calibrated"); if (!touch_calibrated.is_null()) { auto is_calibrated = touch_calibrated.template get(); if (is_calibrated) { - indev_drv_1.read_cb = evdev_read_calibrated; + spdlog::info("using touch calibration"); + lv_tc_indev_drv_init(&indev_drv_1, evdev_read); } } - lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1); + lv_indev_drv_register(&indev_drv_1); } #else // SIMULATOR diff --git a/src/print_status_panel.cpp b/src/print_status_panel.cpp index 00a2edd..f9a5028 100644 --- a/src/print_status_panel.cpp +++ b/src/print_status_panel.cpp @@ -314,6 +314,8 @@ void PrintStatusPanel::consume(json &j) { auto print_status = pstate.template get(); if (print_status != "printing" && print_status != "paused") { mini_print_status.hide(); + } else { + mini_print_status.show(); } mini_print_status.update_status(print_status); @@ -516,7 +518,7 @@ void PrintStatusPanel::update_flow_rate(double filament_used) { flow = filament_xsection * filament_delta / delta; spdlog::trace("caculated flow {}", flow); - flow_rate.update_label(fmt::format("{:.3f} mm3/s", flow).c_str()); + flow_rate.update_label(fmt::format("{:.2f} mm3/s", flow).c_str()); } last_filament_used = filament_used; @@ -538,7 +540,7 @@ void PrintStatusPanel::update_layers(json &info) { new_total_layer = v.template get(); } - if (new_total_layer > total_layer || new_cur_layer > cur_layer) { + if (new_total_layer != total_layer || new_cur_layer != cur_layer) { total_layer = new_total_layer; cur_layer = new_cur_layer; diff --git a/src/spoolman_panel.cpp b/src/spoolman_panel.cpp index 8702a9e..6871f32 100644 --- a/src/spoolman_panel.cpp +++ b/src/spoolman_panel.cpp @@ -95,8 +95,10 @@ void SpoolmanPanel::init() { if (!s.is_null() && !s.empty()) { spools.clear(); for (auto &e : s) { - uint32_t spool_id = e["id"].template get(); - spools.insert({spool_id, e}); + if (e.contains("id")) { + uint32_t spool_id = e["id"].template get(); + spools.insert({spool_id, e}); + } } std::vector sorted_spools; @@ -155,11 +157,22 @@ void SpoolmanPanel::populate_spools(std::vector &sorted_spools) { auto id = el["/id"_json_pointer].template get(); bool is_active = id == active_id; - auto vendor = el["/filament/vendor/name"_json_pointer].template get(); - auto filament_name = el["/filament/name"_json_pointer].template get(); - auto material = el["/filament/material"_json_pointer].template get(); - auto remaining_weight = el["/remaining_weight"_json_pointer].template get(); - auto remaining_len = el["/remaining_length"_json_pointer].template get() / 100; // mm to m; + auto vendor_json = el["/filament/vendor/name"_json_pointer]; + auto vendor = !vendor_json.is_null() ? vendor_json.template get() : ""; + + auto filament_name_json = el["/filament/name"_json_pointer]; + auto filament_name = !filament_name_json.is_null() ? filament_name_json.template get() : ""; + + auto material_json = el["/filament/material"_json_pointer]; + auto material = !material_json.is_null() ? material_json.template get(): ""; + + auto remaining_weight_json = el["/remaining_weight"_json_pointer]; + auto remaining_weight = !remaining_weight_json.is_null() ? remaining_weight_json.template get() : 0.0; + + auto remaining_len_json = el["/remaining_length"_json_pointer]; + auto remaining_len = !remaining_len_json.is_null() + ? remaining_len_json.template get() / 100 // mm to m; + : 0.0; lv_table_set_cell_value(spool_table, row_idx, 0, std::to_string(id).c_str()); lv_table_set_cell_value(spool_table, row_idx, 1,