diff --git a/lib/lv_lib_qrcode/lv_qrcode.c b/lib/lv_lib_qrcode/lv_qrcode.c index 77f2697e6..54fe4b1c9 100644 --- a/lib/lv_lib_qrcode/lv_qrcode.c +++ b/lib/lv_lib_qrcode/lv_qrcode.c @@ -16,6 +16,7 @@ * DEFINES *********************/ #define QR_SIZE 150 +#define LV_OBJX_NAME "lv_qrcode" /********************** * TYPEDEFS @@ -47,18 +48,39 @@ */ lv_obj_t* lv_qrcode_create(lv_obj_t* parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color) { + LV_LOG_INFO("qrcode create started"); + + /*Create a basic object*/ + lv_obj_t* new_qrcode = lv_canvas_create(parent, NULL); + LV_ASSERT_MEM(new_qrcode); + if(new_qrcode == NULL) return NULL; + + /*Extend the canvas ext attr to qrcode ext attr*/ + lv_qrcode_ext_t * ext = lv_obj_allocate_ext_attr(new_qrcode, sizeof(lv_qrcode_ext_t)); + LV_ASSERT_MEM(ext); + if(ext == NULL) { + lv_obj_del(new_qrcode); + return NULL; + } + + ext->text = NULL; + ext->static_txt = 0; + ext->dot.tmp_ptr = NULL; + ext->dot_tmp_alloc = 0; + + /*Allocate QR bitmap buffer*/ uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size); uint8_t* buf = lv_mem_alloc(buf_size); LV_ASSERT_MEM(buf); if(buf == NULL) return NULL; - lv_obj_t* canvas = lv_canvas_create(parent, NULL); + lv_canvas_set_buffer(new_qrcode, buf, size, size, LV_IMG_CF_INDEXED_1BIT); + lv_canvas_set_palette(new_qrcode, 0, dark_color); + lv_canvas_set_palette(new_qrcode, 1, light_color); - lv_canvas_set_buffer(canvas, buf, size, size, LV_IMG_CF_INDEXED_1BIT); - lv_canvas_set_palette(canvas, 0, dark_color); - lv_canvas_set_palette(canvas, 1, light_color); + LV_LOG_INFO("qrcode create ready"); - return canvas; + return new_qrcode; // lv_img_dsc_t * img_buf = lv_img_buf_alloc(20, 20, LV_IMG_CF_TRUE_COLOR); // if(img_buf == NULL) { @@ -97,6 +119,8 @@ lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len) lv_canvas_fill_bg(qrcode, c, 0); // lv_canvas_zoom(); + LV_LOG_INFO("Update QR-code text with length : %d", data_len); + if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV; uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; @@ -106,14 +130,20 @@ lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len) bool ok = qrcodegen_encodeBinary(data_tmp, data_len, qr0, qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); - if(!ok) return LV_RES_INV; + if(!ok) { + LV_LOG_WARN("QR-code encoding error"); + return LV_RES_INV; + } lv_coord_t obj_w = lv_obj_get_width(qrcode); - int qr_size = qrcodegen_getSize(qr0); - int scale = obj_w / qr_size; + int qr_size = qrcodegen_getSize(qr0); // Number of vertical QR blocks + int scale = obj_w / (qr_size + 2); // +2 guaranteed a minimum of 1 block margin all round int scaled = qr_size * scale; int margin = (obj_w - scaled) / 2; + LV_LOG_INFO("Update QR-code data : obj_w[%d] QR moduls[%d] scale factor[%d]", obj_w, qr_size, scale); + + /*Expand the qr encodet binary to canvas size*/ for(int y = 0; y < scaled; y++) { for(int x = 0; x < scaled; x++) { c.full = qrcodegen_getModule(qr0, x / scale, y / scale) ? 0 : 1; @@ -121,6 +151,8 @@ lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len) } } + qrcode->signal_cb(qrcode, LV_SIGNAL_CLEANUP, NULL); + return LV_RES_OK; } @@ -168,6 +200,118 @@ lv_res_t lv_qrcode_update2(lv_obj_t* qrcode, const void* data, uint32_t data_len return LV_RES_OK; } +/** + * Set the data of a QR code object + * @param qrcode pointer to aQ code object + * @param text data to display, '\0' terminated character string. NULL to refresh with the current text. + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_text(lv_obj_t * qrcode, const void * text) +{ + LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME); + + lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode); + + /*If text is NULL then just refresh with the current text */ + if(text == NULL) text = ext->text; + + LV_ASSERT_STR(text); + + if(ext->text == text && ext->static_txt == 0) { + /*If set its own text then reallocate it (maybe its size changed)*/ + ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1); + + LV_ASSERT_MEM(ext->text); + if(ext->text == NULL) return LV_RES_INV; + } + else { + /*Free the old text*/ + if(ext->text != NULL && ext->static_txt == 0) { + lv_mem_free(ext->text); + ext->text = NULL; + } + + /*Get the size of the text*/ + size_t len = strlen(text) + 1; + + /*Allocate space for the new text*/ + ext->text = lv_mem_alloc(len); + LV_ASSERT_MEM(ext->text); + if(ext->text == NULL) return LV_RES_INV; + strcpy(ext->text, text); + + /*Now the text is dynamically allocated*/ + ext->static_txt = 0; + } + + return lv_qrcode_update(qrcode, ext->text, strlen(ext->text)); +} + +/** + * Set the data of a QR code object + * @param qrcode pointer to aQ code object + * @param text data to display, '\0' terminated character string. NULL to refresh with the current text. + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_text_static(lv_obj_t * qrcode, const void * text) +{ + LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME); + + lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode); + if(ext->static_txt == 0 && ext->text != NULL) { + lv_mem_free(ext->text); + ext->text = NULL; + } + + if(text != NULL) { + ext->static_txt = 1; + ext->text = (char *)text; + } + + return lv_qrcode_update(qrcode, text, strlen(text)); +} + +/** + * Get the text of a qrcode + * @param qrcode pointer to a qrcode object + * @return the text of the qrcode + */ +char * lv_qrcode_get_text(const lv_obj_t * qrcode) +{ + LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME); + + lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode); + + return ext->text; +} + +/** + * Set the data of a QR code object + * @param qrcode pointer to aQ code object + * @param size width and height of the QR code + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_size(lv_obj_t * qrcode, lv_coord_t size) +{ + LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME); + + lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode); + + /*Reallocate QR bitmap buffer*/ + uint32_t new_buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size); + uint8_t* buf = lv_mem_realloc((void *)ext->canvas.dsc.data, new_buf_size); + LV_ASSERT_MEM(buf); + if(buf == NULL) return LV_RES_INV; + + lv_canvas_set_buffer(qrcode, buf, size, size, LV_IMG_CF_INDEXED_1BIT); + + lv_qrcode_update(qrcode, ext->text, strlen(ext->text)); + +// qrcode->signal_cb(qrcode, LV_SIGNAL_CLEANUP, NULL); + + return LV_RES_OK; +} + /** * Delete a QR code object * @param qrcode pointer to a QR code obejct diff --git a/lib/lv_lib_qrcode/lv_qrcode.h b/lib/lv_lib_qrcode/lv_qrcode.h index 576cd4455..a9bdc107f 100644 --- a/lib/lv_lib_qrcode/lv_qrcode.h +++ b/lib/lv_lib_qrcode/lv_qrcode.h @@ -23,6 +23,28 @@ extern "C" { * TYPEDEFS **********************/ +/** Data of qrcode*/ +typedef struct { + /*Data of canvas, copyed from lv_canvas_ext_t*/ + lv_canvas_ext_t canvas; +// lv_img_ext_t img; /*Ext. of ancestor*/ +// lv_img_dsc_t dsc; + + /*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/ + /*New data for this type */ + char * text; /*Text of the label*/ + + union { + char * tmp_ptr; /* Pointer to the allocated memory containing the character which are replaced by dots (Handled + by the library)*/ + char tmp[4]; /* Directly store the characters if <=4 characters */ + } dot; + + uint8_t static_txt : 1; /*Flag to indicate the text is static*/ + uint8_t dot_tmp_alloc : 1; /*True if dot_tmp has been allocated. False if dot_tmp directly holds up to 4 bytes of + characters */ +} lv_qrcode_ext_t; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -39,13 +61,44 @@ lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_ /** * Set the data of a QR code object - * @param qrcode pointer to aQ code object + * @param qrcode pointer to QR code object * @param data data to display * @param data_len length of data in bytes * @return LV_RES_OK: if no error; LV_RES_INV: on error */ lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len); +/** + * Set the data of a QR code object + * @param qrcode pointer to QR code object + * @param text data to display, '\0' terminated character string. NULL to refresh with the current text. + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_text(lv_obj_t * qrcode, const void * text); + +/** + * Set the data of a QR code object + * @param qrcode pointer to QR code object + * @param text data to display, '\0' terminated character string. NULL to refresh with the current text. + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_text_static(lv_obj_t * qrcode, const void * text); + +/** + * Get the text of a qrcode + * @param qrcode pointer to a qrcode object + * @return the text of the qrcode + */ +char * lv_qrcode_get_text(const lv_obj_t * qrcode); + +/** + * Set the data of a QR code object + * @param qrcode pointer to QR code object + * @param size width and height of the QR code + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_set_size(lv_obj_t * qrcode, lv_coord_t size); + /** * Delete a QR code object * @param qrcode pointer to a QR code object diff --git a/lib/lv_lib_qrcode/qrcodegen.cpp b/lib/lv_lib_qrcode/qrcodegen.cpp index bd8ed669d..57bf34051 100644 --- a/lib/lv_lib_qrcode/qrcodegen.cpp +++ b/lib/lv_lib_qrcode/qrcodegen.cpp @@ -138,6 +138,11 @@ static const int PENALTY_N4 = 10; bool qrcodegen_encodeText(const char* text, uint8_t tempBuffer[], uint8_t qrcode[], enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + /* preventing crashes */ + if (maxVersion > qrcodegen_VERSION_MAX_LIMIT) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } size_t textLen = strlen(text); if(textLen == 0) @@ -172,6 +177,11 @@ bool qrcodegen_encodeText(const char* text, uint8_t tempBuffer[], uint8_t qrcode bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + /* preventing crashes */ + if (maxVersion > qrcodegen_VERSION_MAX_LIMIT) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } struct qrcodegen_Segment seg; seg.mode = qrcodegen_Mode_BYTE; diff --git a/lib/lv_lib_qrcode/qrcodegen.h b/lib/lv_lib_qrcode/qrcodegen.h index a0ab04737..74223270e 100644 --- a/lib/lv_lib_qrcode/qrcodegen.h +++ b/lib/lv_lib_qrcode/qrcodegen.h @@ -122,8 +122,23 @@ struct qrcodegen_Segment /*---- Macro constants and functions ----*/ -#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard -#define qrcodegen_VERSION_MAX 3 // The maximum version number supported in the QR Code Model 2 standard +/* Version number supported in the QR Code Model 2 standard +* higher version numbers can process longer texts but require more memory and computing time. +* +* VERSION_MAX = 3 : max text length to encode 42 alphanumeric characters +* VERSION_MAX = 5 : max text length to encode 85 alphanumeric characters +* VERSION_MAX = 7 : max text length to encode 122 alphanumeric characters +* VERSION_MAX = 9 : max text length to encode 180 alphanumeric characters +* VERSION_MAX = 11 : max text length to encode 251 alphanumeric characters +* VERSION_MAX = 15 : max text length to encode 412 alphanumeric characters +* and so on +* 25 is the highest posible version, see below +*/ +#define qrcodegen_VERSION_MIN 1 // The minimum version number +#ifndef qrcodegen_VERSION_MAX + #define qrcodegen_VERSION_MAX 7 // The maximum version number +#endif +#define qrcodegen_VERSION_MAX_LIMIT 25 // Calculates the number of bytes needed to store any QR Code up to and including the given version number, // as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' diff --git a/src/hasp/hasp_attribute.cpp b/src/hasp/hasp_attribute.cpp index b903cc932..dd0eb83c6 100644 --- a/src/hasp/hasp_attribute.cpp +++ b/src/hasp/hasp_attribute.cpp @@ -15,6 +15,10 @@ #endif /*** Image Improvement ***/ +#if HASP_USE_QRCODE > 0 +#include "lv_qrcode.h" +#endif + LV_FONT_DECLARE(unscii_8_icon); extern const char** btnmatrix_default_map; // memory pointer to lvgl default btnmatrix map extern const char* msgbox_default_map[]; // memory pointer to lvgl default btnmatrix map @@ -394,6 +398,7 @@ static void hasp_attribute_get_part_state_new(lv_obj_t* obj, const char* attr_in case LV_HASP_IMGBTN: case LV_HASP_OBJECT: case LV_HASP_TAB: + case LV_HASP_QRCODE: part = LV_BTN_PART_MAIN; break; @@ -1752,7 +1757,8 @@ static hasp_attribute_type_t attribute_common_text(lv_obj_t* obj, uint16_t attr_ #if LV_USE_WIN != 0 {LV_HASP_WINDOW, ATTR_TEXT, lv_win_set_title, lv_win_get_title}, #endif - {LV_HASP_MSGBOX, ATTR_TEXT, my_msgbox_set_text, lv_msgbox_get_text} + {LV_HASP_MSGBOX, ATTR_TEXT, my_msgbox_set_text, lv_msgbox_get_text}, + {LV_HASP_QRCODE, ATTR_TEXT, my_qrcode_set_text, my_qrcode_get_text} }; for(int i = 0; i < sizeof(list) / sizeof(list[0]); i++) { @@ -2446,6 +2452,16 @@ static hasp_attribute_type_t attribute_common_int(lv_obj_t* obj, uint16_t attr_h val = lv_obj_get_ext_click_pad_top(obj); break; // attribute_found + case ATTR_SIZE: + if(obj_check_type(obj, LV_HASP_QRCODE)) { + if(update) { + lv_qrcode_set_size(obj, val); + } else { + val = lv_obj_get_width(obj); + } + } + break; + default: return HASP_ATTR_TYPE_NOT_FOUND; // attribute_not found } @@ -2648,6 +2664,7 @@ void hasp_process_obj_attribute(lv_obj_t* obj, const char* attribute, const char case ATTR_OPACITY: case ATTR_EXT_CLICK_H: case ATTR_EXT_CLICK_V: + case ATTR_SIZE: val = strtol(payload, nullptr, DEC); ret = attribute_common_int(obj, attr_hash, val, update); break; diff --git a/src/hasp/hasp_attribute_helper.h b/src/hasp/hasp_attribute_helper.h index ee078300c..888b691c3 100644 --- a/src/hasp/hasp_attribute_helper.h +++ b/src/hasp/hasp_attribute_helper.h @@ -607,6 +607,28 @@ static inline void my_btn_set_text(lv_obj_t* obj, const char* value) } } +// OK - lvgl does not return a const char * +static const char* my_qrcode_get_text(const lv_obj_t* obj) +{ + if(!obj) { + LOG_WARNING(TAG_ATTR, F("QR-code not defined")); + return NULL; + } + + if(obj) { + if(obj_check_type(obj, LV_HASP_QRCODE)) return lv_qrcode_get_text(obj); + } else { + LOG_WARNING(TAG_ATTR, F("my_qrcode_get_text NULL Pointer encountered")); + } + + return NULL; +} + +static void my_qrcode_set_text(lv_obj_t* obj, const char* text) +{ + lv_qrcode_set_text(obj, text); +} + /** * Get the value_str for an object part and state. * @param obj pointer to a object diff --git a/src/hasp/hasp_event.cpp b/src/hasp/hasp_event.cpp index 0b52e91b0..cb7e112cd 100644 --- a/src/hasp/hasp_event.cpp +++ b/src/hasp/hasp_event.cpp @@ -86,6 +86,10 @@ void delete_event_handler(lv_obj_t* obj, lv_event_t event) my_image_release_resources(obj); break; + case LV_HASP_QRCODE: + lv_qrcode_delete(obj); + break; + case LV_HASP_GAUGE: break; diff --git a/src/hasp/hasp_object.cpp b/src/hasp/hasp_object.cpp index 68b1f8ebc..c8b989cec 100644 --- a/src/hasp/hasp_object.cpp +++ b/src/hasp/hasp_object.cpp @@ -377,6 +377,14 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) } break; + case LV_HASP_QRCODE: + case HASP_OBJ_QRCODE: + obj = lv_qrcode_create(parent_obj, 140, LV_COLOR_BLACK, LV_COLOR_WHITE); + if(obj) { + obj->user_data.objid = LV_HASP_QRCODE; + } + break; + case LV_HASP_ARC: case HASP_OBJ_ARC: obj = lv_arc_create(parent_obj, NULL); diff --git a/src/hasp/hasp_object.h b/src/hasp/hasp_object.h index 5bfe157ac..9a8b81699 100644 --- a/src/hasp/hasp_object.h +++ b/src/hasp/hasp_object.h @@ -88,6 +88,7 @@ enum lv_hasp_obj_type_t { LV_HASP_ANIMIMAGE = 38, // placeholder LV_HASP_CANVAS = 39, // placeholder LV_HASP_MASK = 40, // placeholder + LV_HASP_QRCODE = 41, // placeholder /* Custom */ LV_HASP_ALARM = 60, @@ -170,6 +171,7 @@ inline bool obj_check_type(const lv_obj_t* obj, lv_hasp_obj_type_t haspobjtype) #define HASP_OBJ_SPINBOX 25641 #define HASP_OBJ_CALENDAR 30334 #define HASP_OBJ_IMG 30499 +#define HASP_OBJ_QRCODE 50958 #define HASP_OBJ_GAUGE 33145 #define HASP_OBJ_CHART 34654 #define HASP_OBJ_LINE 34804 diff --git a/src/hasplib.h b/src/hasplib.h index 05ce613cd..3bed93824 100644 --- a/src/hasplib.h +++ b/src/hasplib.h @@ -64,6 +64,10 @@ #include "lv_sjpg.h" #endif +#if HASP_USE_QRCODE > 0 +#include "lv_qrcode.h" +#endif + #if defined(HASP_USE_CUSTOM) #include "custom/my_custom.h" #endif \ No newline at end of file