diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ddb9a0ed..2b75f6da 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,4 +1,4 @@ -name: Build master branch +name: Build branch on: [push, workflow_dispatch] diff --git a/.gitignore b/.gitignore index ab6f693b..3177d26d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,13 +5,14 @@ .git .pio data/* +!data/edit.htm +!data/edit.htm.gz +src/user_setups/active/* include/user_config_override.h src/user_config_override.h user_config_override.h platformio_override.ini user_setups/active/* -src/user_setups/active/* -test/config.yaml build_output/* build_output/firmware/*.bin build_output/firmware/*.exe @@ -30,3 +31,5 @@ src/custom/* .vscode/c_cpp_properties.json .vscode/launch.json *.bak + +test/config.yaml diff --git a/data/edit.htm b/data/edit.htm new file mode 100644 index 00000000..4d3f47ec --- /dev/null +++ b/data/edit.htm @@ -0,0 +1,439 @@ + + + + + + File Editor + + + + + + +
+
+
+ + + + \ No newline at end of file diff --git a/data/edit.htm.gz b/data/edit.htm.gz new file mode 100644 index 00000000..d9519e4a Binary files /dev/null and b/data/edit.htm.gz differ diff --git a/include/hasp_conf.h b/include/hasp_conf.h index ba2bbfff..8d9aabbc 100644 --- a/include/hasp_conf.h +++ b/include/hasp_conf.h @@ -43,6 +43,10 @@ #define HASP_USE_MQTT (HASP_HAS_NETWORK) #endif +#ifndef HASP_USE_BROADCAST +#define HASP_USE_BROADCAST 1 +#endif + #ifndef MQTT_NODENAME #define MQTT_NODENAME "plate" #endif @@ -63,6 +67,10 @@ #define HASP_USE_TELNET 0 #endif +#ifndef HASP_USE_CONSOLE +#define HASP_USE_CONSOLE 1 +#endif + /* Filesystem */ #define HASP_HAS_FILESYSTEM (ARDUINO_ARCH_ESP32 > 0 || ARDUINO_ARCH_ESP8266 > 0) @@ -102,10 +110,6 @@ #define HASP_NUM_GPIO_CONFIG 8 #endif -#ifndef HASP_NUM_INPUTS -#define HASP_NUM_INPUTS 4 // Number of ACE Buttons -#endif - // #ifndef HASP_NUM_OUTPUTS // #define HASP_NUM_OUTPUTS 3 // #endif @@ -217,6 +221,10 @@ static WiFiSpiClass WiFi; #include "sys/svc/hasp_http.h" #endif +#if HASP_USE_CONSOLE > 0 +#include "sys/svc/hasp_console.h" +#endif + #if HASP_USE_TELNET > 0 #include "sys/svc/hasp_telnet.h" #endif @@ -240,6 +248,14 @@ static WiFiSpiClass WiFi; #include "sys/svc/hasp_slave.h" #endif +#ifndef HASP_ATTRIBUTE_FAST_MEM +#define HASP_ATTRIBUTE_FAST_MEM +#endif + +#ifndef IRAM_ATTR +#define IRAM_ATTR +#endif + #ifndef FPSTR #define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) #endif @@ -283,6 +299,7 @@ static WiFiSpiClass WiFi; #define memcpy_P memcpy #define strcasecmp_P strcasecmp #define strcmp_P strcmp +#define strcpy_P strcpy #define strstr_P strstr #define halRestartMcu() #define millis SDL_GetTicks diff --git a/include/lv_conf.h b/include/lv_conf.h index c7ea9aad..7a1d57a6 100644 --- a/include/lv_conf.h +++ b/include/lv_conf.h @@ -1,7 +1,3 @@ -#ifdef USE_CONFIG_OVERRIDE -#include "user_config_override.h" -#endif - #include "lv_conf_v7.h" #define LV_THEME_DEFAULT_FLAGS LV_THEME_DEFAULT_FLAG diff --git a/include/lv_conf_v7.h b/include/lv_conf_v7.h index 74897617..707000e7 100644 --- a/include/lv_conf_v7.h +++ b/include/lv_conf_v7.h @@ -124,7 +124,9 @@ typedef int16_t lv_coord_t; * Can be changed in the Input device driver (`lv_indev_drv_t`)*/ /* Input device read period in milliseconds */ -#define LV_INDEV_DEF_READ_PERIOD 30 +#ifndef LV_INDEV_DEF_READ_PERIOD +#define LV_INDEV_DEF_READ_PERIOD 20 +#endif /* Drag threshold in pixels */ #define LV_INDEV_DEF_DRAG_LIMIT 10 @@ -185,16 +187,16 @@ typedef void* lv_group_user_data_t; typedef void* lv_fs_drv_user_data_t; /*File system interface*/ -#define LV_USE_FS_IF 0 +#define LV_USE_FS_IF 1 #if LV_USE_FS_IF # define LV_FS_IF_FATFS '\0' #if defined(STM32F4xx) // || defined(ARDUINO_ARCH_ESP8266) # define LV_FS_IF_PC '\0' -# define LV_FS_IF_SPIFFS '\0' // internal esp Flash +//# define LV_FS_IF_SPIFFS '\0' // internal esp Flash #else -# define LV_FS_IF_PC '\0' +# define LV_FS_IF_PC 'L' // Local filesystem # define LV_FS_IF_POSIX '\0' -# define LV_FS_IF_SPIFFS '\0' // no internal esp Flash +//# define LV_FS_IF_SPIFFS '\0' // no internal esp Flash #endif #endif /*LV_USE_FS_IF*/ @@ -224,6 +226,10 @@ typedef void* lv_fs_drv_user_data_t; /*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/ typedef void* lv_img_decoder_user_data_t; +#if (HASP_USE_PNGDECODE > 0) && (LV_USE_FILESYSTEM > 0) +//#define LV_PNG_USE_LV_FILESYSTEM 1 +#endif + /*===================== * Compiler settings *====================*/ @@ -231,7 +237,9 @@ typedef void* lv_img_decoder_user_data_t; #define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_task_handler` function */ +#ifndef LV_ATTRIBUTE_TASK_HANDLER #define LV_ATTRIBUTE_TASK_HANDLER +#endif /* With size optimization (-Os) the compiler might not align data to * 4 or 8 byte boundary. This alignment will be explicitly applied where needed. @@ -341,6 +349,36 @@ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the in #if TFT_WIDTH>=320 || TFT_WIDTH>=480 +#ifdef WT32SC01 + +#ifndef HASP_FONT_1 +#define HASP_FONT_1 robotocondensed_regular_16_ascii /* 5% Width */ +#endif +#ifndef HASP_FONT_2 +#define HASP_FONT_2 robotocondensed_regular_24_ascii /* 5% Width */ +#endif +#ifndef HASP_FONT_3 +#define HASP_FONT_3 robotocondensed_regular_32_ascii /* 10% Width */ +#endif +#ifndef HASP_FONT_4 +#define HASP_FONT_4 robotocondensed_regular_48_ascii /* 10% Height */ +#endif + +#ifndef ROBOTOCONDENSED_REGULAR_16_ASCII +#define ROBOTOCONDENSED_REGULAR_16_ASCII 1 +#endif +#ifndef ROBOTOCONDENSED_REGULAR_24_ASCII +#define ROBOTOCONDENSED_REGULAR_24_ASCII 1 +#endif +#ifndef ROBOTOCONDENSED_REGULAR_32_ASCII +#define ROBOTOCONDENSED_REGULAR_32_ASCII 1 +#endif +#ifndef ROBOTOCONDENSED_REGULAR_48_ASCII +#define ROBOTOCONDENSED_REGULAR_48_ASCII 1 +#endif + +#else // not WT32SC01 + #ifndef HASP_FONT_1 #define HASP_FONT_1 robotocondensed_regular_16_latin1 /* 5% Width */ #endif @@ -367,6 +405,8 @@ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the in #define ROBOTOCONDENSED_REGULAR_48_LATIN1 1 #endif +#endif // WT32SC01 + #ifndef HASP_FONT_SIZE_1 #define HASP_FONT_SIZE_1 16 #endif @@ -380,7 +420,7 @@ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the in #define HASP_FONT_SIZE_4 48 #endif -#else +#else // not 320x480 #ifndef HASP_FONT_1 #define HASP_FONT_1 robotocondensed_regular_12_latin1 /* 5% Width */ @@ -732,6 +772,7 @@ typedef struct { /*LED (dependencies: -)*/ #define LV_USE_LED 1 +#define LV_LED_BRIGHT_MIN 0 /*Line (dependencies: -*/ #define LV_USE_LINE 1 diff --git a/include/user_config_override-template.h b/include/user_config_override-template.h index 9e64d0e1..fd01a783 100644 --- a/include/user_config_override-template.h +++ b/include/user_config_override-template.h @@ -88,28 +88,9 @@ #define D_HTTP_COLOR_BUTTON_RESET "#d43535" // Restart/Reset/Delete button color - Strong red */ -/*************************************************** - * Font Settings - **************************************************/ -// #define HASP_FONT_1 robotocondensed_regular_16_latin1 // Use available fonts from src/fonts directory -// #define HASP_FONT_2 robotocondensed_regular_22_latin1 -// #define HASP_FONT_3 robotocondensed_regular_40_latin1 -// #define HASP_FONT_4 robotocondensed_regular_48_latin1 - -// #define ROBOTOCONDENSED_REGULAR_16_LATIN1 1 // Enable the selected fonts -// #define ROBOTOCONDENSED_REGULAR_22_LATIN1 1 -// #define ROBOTOCONDENSED_REGULAR_40_LATIN1 1 -// #define ROBOTOCONDENSED_REGULAR_48_LATIN1 1 - -// #define HASP_FONT_SIZE_1 16 // Define used font sizes -// #define HASP_FONT_SIZE_2 22 -// #define HASP_FONT_SIZE_3 40 -// #define HASP_FONT_SIZE_4 48 - /*************************************************** * Other Settings **************************************************/ //#define HASP_USE_HA // Enable Home Assistant auto-discovery -//#define HASP_GPIO_TEMPLATE "[2360346,2491680,2623009,2097420,2097678,2097947,0,0]" // Lanbon L8-HS: 3 Relays + Moodlight GPIO config -#endif +#endif \ No newline at end of file diff --git a/lib/lv_fs_if/lv_fs_pc.c b/lib/lv_fs_if/lv_fs_pc.c index 6bdca94d..d32e1418 100644 --- a/lib/lv_fs_if/lv_fs_pc.c +++ b/lib/lv_fs_if/lv_fs_pc.c @@ -24,10 +24,12 @@ * DEFINES *********************/ #ifndef LV_FS_PC_PATH -#ifndef WIN32 -#define LV_FS_PC_PATH "/fs" /*Projet root*/ +#if defined(ESP32) +#define LV_FS_PC_PATH "/littlefs" /*Projet root*/ +#elif defined(WIN32) +#define LV_FS_PC_PATH "./" /*Projet root*/ #else -#define LV_FS_PC_PATH ".\\" /*Projet root*/ +#define LV_FS_PC_PATH "" /*Projet root*/ #endif #endif /*LV_FS_PATH*/ @@ -144,11 +146,11 @@ static lv_fs_res_t fs_open(lv_fs_drv_t* drv, void* file_p, const char* path, lv_ #ifndef WIN32 char buf[256]; sprintf(buf, LV_FS_PC_PATH "/%s", path); - printf("%s\n", buf); #else char buf[256]; sprintf(buf, LV_FS_PC_PATH "\\%s", path); #endif + printf("Opening file: %s\n", path); file_t f = fopen(buf, flags); if(f == NULL) { diff --git a/lib/lv_fs_if/lv_fs_spiffs.cpp b/lib/lv_fs_if/lv_fs_spiffs.cpp index c33fb2c1..dbc184fb 100644 --- a/lib/lv_fs_if/lv_fs_spiffs.cpp +++ b/lib/lv_fs_if/lv_fs_spiffs.cpp @@ -22,6 +22,8 @@ #define LV_FS_SPIFFS SPIFFS #elif HASP_USE_LITTLEFS > 0 #include "LITTLEFS.h" + +#include "esp_littlefs.h" #define LV_FS_SPIFFS LITTLEFS #endif #elif defined(ARDUINO_ARCH_ESP8266) @@ -45,13 +47,13 @@ **********************/ /* Create a type to store the required data about your file.*/ -typedef File lv_spiffs_file_t; +typedef FILE* lv_spiffs_file_t; /*Similarly to `file_t` create a type for directory reading too */ #if defined(ARDUINO_ARCH_ESP32) -typedef File lv_spiffs_dir_t; +typedef FILE* lv_spiffs_dir_t; #elif defined(ARDUINO_ARCH_ESP8266) -typedef Dir lv_spiffs_dir_t; +typedef Dir* lv_spiffs_dir_t; #define FILE_READ "r" #define FILE_WRITE "r+" #endif @@ -93,6 +95,7 @@ void lv_fs_if_spiffs_init(void) /*---------------------------------------------------- * Initialize your storage device and File System * -------------------------------------------------*/ + Log.verbose(88, "File system init start"); fs_init(); /*--------------------------------------------------- @@ -114,16 +117,17 @@ void lv_fs_if_spiffs_init(void) fs_drv.tell_cb = fs_tell; fs_drv.free_space_cb = fs_free; fs_drv.size_cb = fs_size; - fs_drv.remove_cb = fs_remove; - fs_drv.rename_cb = fs_rename; - fs_drv.trunc_cb = fs_trunc; + // fs_drv.remove_cb = fs_remove; + // fs_drv.rename_cb = fs_rename; + // fs_drv.trunc_cb = fs_trunc; - fs_drv.rddir_size = sizeof(lv_spiffs_dir_t); - fs_drv.dir_close_cb = fs_dir_close; - fs_drv.dir_open_cb = fs_dir_open; - fs_drv.dir_read_cb = fs_dir_read; + // fs_drv.rddir_size = sizeof(lv_spiffs_dir_t); + // fs_drv.dir_close_cb = fs_dir_close; + // fs_drv.dir_open_cb = fs_dir_open; + // fs_drv.dir_read_cb = fs_dir_read; lv_fs_drv_register(&fs_drv); + Log.verbose(88, "File system init complete"); } /********************** @@ -133,7 +137,42 @@ void lv_fs_if_spiffs_init(void) /* Initialize your Storage device and File system. */ static void fs_init(void) { - LV_FS_SPIFFS.begin(); + esp_vfs_littlefs_conf_t conf = {.base_path = "/lfs", .partition_label = "spiffs", .format_if_mount_failed = false}; + + esp_err_t res = esp_vfs_littlefs_register(&conf); + + if(res != ESP_OK) { + if(res == ESP_FAIL) { + Log.error(88, "Failed to mount or format filesystem"); + } else if(res == ESP_ERR_NOT_FOUND) { + Log.error(88, "Failed to find LittleFS partition"); + } else { + Log.error(88, "Failed to initialize LittleFS (%s)", esp_err_to_name(res)); + } + return; + } + + size_t total = 0, used = 0; + res = esp_littlefs_info(conf.partition_label, &total, &used); + if(res != ESP_OK) { + Log.error(88, "Failed to get LittleFS partition information (%s)", esp_err_to_name(res)); + } else { + Log.verbose(88, "Partition size: total: %d, used: %d", total, used); + } + + // Use POSIX and C standard library functions to work with files. + // First create a file. + Log.verbose(88, "Opening file /lfs/hello.txt"); + FILE* f = fopen("/lfs/hello.txt", "w"); + if(f == NULL) { + Log.error(88, "Failed to open file for writing"); + return; + } + fprintf(f, "LittleFS Rocks!\n"); + fclose(f); + Log.verbose(88, "File written"); + + Log.verbose(88, "LittleFS init OK"); } /** @@ -148,35 +187,35 @@ static lv_fs_res_t fs_open(lv_fs_drv_t* drv, void* file_p, const char* path, lv_ { (void)drv; /*Unused*/ - char filename[32]; - snprintf_P(filename, sizeof(filename), PSTR("/%s"), path); + const char* flags = ""; - lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; - if(fp == NULL) return LV_FS_RES_INV_PARAM; + if(mode == LV_FS_MODE_WR) + flags = "w"; + else if(mode == LV_FS_MODE_RD) + flags = "r"; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) + flags = "rb+"; - LOG_VERBOSE(TAG_LVFS, F("Opening %s"), filename); - lv_spiffs_file_t file = LV_FS_SPIFFS.open(filename, mode == LV_FS_MODE_WR ? FILE_WRITE : FILE_READ); + /*Make the path relative to the current directory (the projects root folder)*/ - LOG_VERBOSE(TAG_LVFS, F("%d"), __LINE__); - if(!file) { - LOG_VERBOSE(TAG_LVFS, F("Invalid file")); - return LV_FS_RES_NOT_EX; + char complete_path[strlen(path) + 1]; + complete_path[0] = '/'; + complete_path[1] = '\0'; + strcat(complete_path, path); - // } else if((*fp).isDirectory()) { - // LOG_VERBOSE(TAG_LVFS, F("Cannot open directory as a file")); - // return LV_FS_RES_UNKNOWN; + Log.verbose(88, "Opening file %s", path); + lv_spiffs_file_t f = fopen(path, flags); + if(f == NULL) return LV_FS_RES_UNKNOWN; - } else { - // f.seek(0, SeekSet); - // LOG_VERBOSE(TAG_LVFS,F("Assigning %s"), f.name()); - LOG_VERBOSE(TAG_LVFS, F("%d"), __LINE__); - // LOG_VERBOSE(TAG_LVFS,F("Copying %s"), f.name()); - LOG_VERBOSE(TAG_LVFS, F("%d - %x - %d"), __LINE__, fp, sizeof(lv_spiffs_file_t)); - // memcpy(fp,&file,sizeof(lv_spiffs_file_t)); - LOG_VERBOSE(TAG_LVFS, F("%d"), __LINE__); - *fp = file; - return LV_FS_RES_OK; - } + /*Be sure we are the beginning of the file*/ + fseek(f, 0, SEEK_SET); + + /* 'file_p' is pointer to a file descriptor and + * we need to store our file descriptor here*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + *fp = f; + Log.verbose(88, "Open eof file_p %d", feof(*fp)); + return LV_FS_RES_OK; } /** @@ -188,23 +227,10 @@ static lv_fs_res_t fs_open(lv_fs_drv_t* drv, void* file_p, const char* path, lv_ */ static lv_fs_res_t fs_close(lv_fs_drv_t* drv, void* file_p) { - (void)drv; /*Unused*/ - lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; - if(fp == NULL) return LV_FS_RES_INV_PARAM; - - lv_spiffs_file_t file = *fp; - - if(!file) { - LOG_VERBOSE(TAG_LVFS, F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else if(file.isDirectory()) { - return LV_FS_RES_UNKNOWN; - - } else { - file.close(); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + fclose(*fp); + return LV_FS_RES_OK; } /** @@ -219,33 +245,11 @@ static lv_fs_res_t fs_close(lv_fs_drv_t* drv, void* file_p) */ static lv_fs_res_t fs_read(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br) { - (void)drv; /*Unused*/ - lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; - if(fp == NULL) return LV_FS_RES_INV_PARAM; - - lv_spiffs_file_t file = *fp; - - if(!file) { - LOG_ERROR(TAG_LVFS, F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else { - // LOG_VERBOSE(TAG_LVFS, F("Reading %u bytes from %s at position %u"), btr, file.name(), file.position()); - uint32_t len = 0; - char* chp = (char*)buf; - if(chp != NULL && btr > 0) - len = file.readBytes(chp, btr); - else - LOG_VERBOSE(TAG_LVFS, F("Buffer is NULL"), btr, file.name(), file.position()); - - if(br != NULL) - *br = len; - else - LOG_VERBOSE(TAG_LVFS, F("BYTESREAD is NULL"), btr, file.name(), file.position()); - - Serial.print("!"); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + Log.verbose(88, "Read eof %d", feof(*fp)); + *br = fread(buf, 1, btr, *fp); + return LV_FS_RES_OK; } /** @@ -259,18 +263,10 @@ static lv_fs_res_t fs_read(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t b */ static lv_fs_res_t fs_write(lv_fs_drv_t* drv, void* file_p, const void* buf, uint32_t btw, uint32_t* bw) { - (void)drv; /*Unused*/ - lv_spiffs_file_t file = *(lv_spiffs_file_t*)file_p; - // File file = fp; - - if(!file) { - // LOG_VERBOSE(TAG_LVFS,F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else { - *bw = (uint32_t)file.write((byte*)buf, btw); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + Log.verbose(88, "Write eof %d", feof(*fp)); + *bw = fwrite(buf, 1, btw, *fp); } /** @@ -283,18 +279,10 @@ static lv_fs_res_t fs_write(lv_fs_drv_t* drv, void* file_p, const void* buf, uin */ static lv_fs_res_t fs_seek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) { - (void)drv; /*Unused*/ - lv_spiffs_file_t file = *(lv_spiffs_file_t*)file_p; - // File file = fp; - - if(!file) { - // LOG_VERBOSE(TAG_LVFS,F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else { - file.seek(pos, SeekSet); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + fseek(*fp, pos, SEEK_SET); + return LV_FS_RES_OK; } /** @@ -306,18 +294,12 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) */ static lv_fs_res_t fs_size(lv_fs_drv_t* drv, void* file_p, uint32_t* size_p) { - (void)drv; /*Unused*/ - lv_spiffs_file_t file = *(lv_spiffs_file_t*)file_p; - // File file = fp; - - if(!file) { - // LOG_VERBOSE(TAG_LVFS,F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else { - *size_p = (uint32_t)file.size(); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + fseek(*fp, 0L, SEEK_END); + *size_p = ftell(*fp); + fseek(*fp, 0L, SEEK_SET); + return LV_FS_RES_OK; } /** @@ -330,18 +312,10 @@ static lv_fs_res_t fs_size(lv_fs_drv_t* drv, void* file_p, uint32_t* size_p) */ static lv_fs_res_t fs_tell(lv_fs_drv_t* drv, void* file_p, uint32_t* pos_p) { - (void)drv; /*Unused*/ - lv_spiffs_file_t file = *(lv_spiffs_file_t*)file_p; - // File file = fp; - - if(!file) { - // LOG_VERBOSE(TAG_LVFS,F("Invalid file")); - return LV_FS_RES_NOT_EX; - - } else { - *pos_p = (uint32_t)file.position(); - return LV_FS_RES_OK; - } + (void)drv; /*Unused*/ + lv_spiffs_file_t* fp = (lv_spiffs_file_t*)file_p; /*Just avoid the confusing casings*/ + *pos_p = ftell(*fp); + return LV_FS_RES_OK; } /** @@ -439,25 +413,25 @@ static lv_fs_res_t fs_free(lv_fs_drv_t* drv, uint32_t* total_p, uint32_t* free_p * @param path path to a directory * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ -static lv_fs_res_t fs_dir_open(lv_fs_drv_t* drv, void* dir_p, const char* path) -{ - lv_spiffs_dir_t dir; +// static lv_fs_res_t fs_dir_open(lv_fs_drv_t* drv, void* dir_p, const char* path) +// { +// lv_spiffs_dir_t dir; -#if defined(ARDUINO_ARCH_ESP32) - dir = LV_FS_SPIFFS.open(path); - if(!dir) { - return LV_FS_RES_UNKNOWN; - } -#endif +// #if defined(ARDUINO_ARCH_ESP32) +// dir = &LV_FS_SPIFFS.open(path); +// if(!dir) { +// return LV_FS_RES_UNKNOWN; +// } +// #endif -#if defined(ARDUINO_ARCH_ESP8266) - dir = LV_FS_SPIFFS.openDir(path); -#endif +// #if defined(ARDUINO_ARCH_ESP8266) +// dir = LV_FS_SPIFFS.openDir(path); +// #endif - lv_spiffs_dir_t* dp = (lv_spiffs_dir_t*)dir_p; /*Just avoid the confusing casings*/ - *dp = dir; - return LV_FS_RES_OK; -} +// lv_spiffs_dir_t* dp = (lv_spiffs_dir_t*)dir_p; /*Just avoid the confusing casings*/ +// *dp = dir; +// return LV_FS_RES_OK; +// } /** * Read the next filename form a directory. @@ -467,31 +441,31 @@ static lv_fs_res_t fs_dir_open(lv_fs_drv_t* drv, void* dir_p, const char* path) * @param fn pointer to a buffer to store the filename * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ -static lv_fs_res_t fs_dir_read(lv_fs_drv_t* drv, void* dir_p, char* fn) -{ - lv_spiffs_dir_t dir = *(lv_spiffs_dir_t*)dir_p; /*Convert type*/ +// static lv_fs_res_t fs_dir_read(lv_fs_drv_t* drv, void* dir_p, char* fn) +// { +// lv_spiffs_dir_t dir = *(lv_spiffs_dir_t*)dir_p; /*Convert type*/ -#if defined(ARDUINO_ARCH_ESP32) - File file = dir.openNextFile(); - if(file) { - strcpy(fn, file.name()); - return LV_FS_RES_OK; - } else { - return LV_FS_RES_UNKNOWN; - } -#endif +// #if defined(ARDUINO_ARCH_ESP32) +// File file = dir.openNextFile(); +// if(file) { +// strcpy(fn, file.name()); +// return LV_FS_RES_OK; +// } else { +// return LV_FS_RES_UNKNOWN; +// } +// #endif -#if defined(ARDUINO_ARCH_ESP8266) - if(dir.next()) { - strcpy(fn, dir.fileName().c_str()); - return LV_FS_RES_OK; - } else { - return LV_FS_RES_UNKNOWN; - } -#endif +// #if defined(ARDUINO_ARCH_ESP8266) +// if(dir.next()) { +// strcpy(fn, dir.fileName().c_str()); +// return LV_FS_RES_OK; +// } else { +// return LV_FS_RES_UNKNOWN; +// } +// #endif - return LV_FS_RES_NOT_IMP; -} +// return LV_FS_RES_NOT_IMP; +// } /** * Close the directory reading diff --git a/lib/lv_lib_qrcode/lv_qrcode.c b/lib/lv_lib_qrcode/lv_qrcode.c index 9ee582f9..77f2697e 100644 --- a/lib/lv_lib_qrcode/lv_qrcode.c +++ b/lib/lv_lib_qrcode/lv_qrcode.c @@ -45,14 +45,14 @@ * @param light_color light color of the QR code * @return pointer to the created QR code object */ -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_obj_t* lv_qrcode_create(lv_obj_t* parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color) { uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size); - uint8_t * buf = lv_mem_alloc(buf_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_obj_t* canvas = lv_canvas_create(parent, NULL); lv_canvas_set_buffer(canvas, buf, size, size, LV_IMG_CF_INDEXED_1BIT); lv_canvas_set_palette(canvas, 0, dark_color); @@ -90,7 +90,7 @@ lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_ * @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) +lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len) { lv_color_t c; c.full = 1; @@ -124,7 +124,7 @@ lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_le return LV_RES_OK; } -lv_res_t lv_qrcode_update2(lv_obj_t * qrcode, const void * data, uint32_t data_len) +lv_res_t lv_qrcode_update2(lv_obj_t* qrcode, const void* data, uint32_t data_len) { lv_color_t c; // c.full = 1; @@ -143,31 +143,27 @@ lv_res_t lv_qrcode_update2(lv_obj_t * qrcode, const void * data, uint32_t data_l if(!ok) return LV_RES_INV; // lv_coord_t obj_w = lv_obj_get_width(qrcode); - int qr_size = qrcodegen_getSize(qr_pixels); - int scale = 1; - int scaled = 0; - int margin = 0; - LV_LOG_ERROR("5 OK"); + // int scale = 1; + // int scaled = 0; + // int qr_size = qrcodegen_getSize(qr_pixels); + int margin = 0; - lv_img_ext_t * ext = (lv_img_ext_t *)lv_obj_get_ext_attr(qrcode); + lv_img_ext_t* ext = (lv_img_ext_t*)lv_obj_get_ext_attr(qrcode); if(!ext || !ext->src) return LV_RES_INV; - LV_LOG_ERROR("6 OK"); lv_img_header_t header; lv_img_decoder_get_info(ext->src, &header); - LV_LOG_ERROR("7 OK"); lv_img_decoder_dsc_t dec_dsc; lv_res_t res = lv_img_decoder_open(&dec_dsc, ext->src, LV_COLOR_CYAN); - LV_LOG_ERROR("8 OK"); + (void)res; // unused for(int y = 0; y < dec_dsc.header.h; y++) { for(int x = 0; x < dec_dsc.header.w; x++) { c = qrcodegen_getModule(qr_pixels, x, y) ? LV_COLOR_WHITE : LV_COLOR_BLACK; - lv_img_buf_set_px_color(dec_dsc.src, x + margin, y + margin, c); + lv_img_buf_set_px_color((lv_img_dsc_t*)dec_dsc.src, x + margin, y + margin, c); } } - LV_LOG_ERROR("9 OK"); return LV_RES_OK; } @@ -176,9 +172,9 @@ lv_res_t lv_qrcode_update2(lv_obj_t * qrcode, const void * data, uint32_t data_l * Delete a QR code object * @param qrcode pointer to a QR code obejct */ -void lv_qrcode_delete(lv_obj_t * qrcode) +void lv_qrcode_delete(lv_obj_t* qrcode) { - lv_img_dsc_t * img = lv_canvas_get_img(qrcode); + lv_img_dsc_t* img = lv_canvas_get_img(qrcode); lv_mem_free(img->data); lv_mem_free(img); lv_obj_del(qrcode); diff --git a/lib/lv_lib_qrcode/qrcodegen.cpp b/lib/lv_lib_qrcode/qrcodegen.cpp index ef325db9..45a62e1a 100644 --- a/lib/lv_lib_qrcode/qrcodegen.cpp +++ b/lib/lv_lib_qrcode/qrcodegen.cpp @@ -55,7 +55,7 @@ // - They are completely thread-safe if the caller does not give the // same writable buffer to concurrent calls to these functions. -testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int * bitLen); +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int* bitLen); testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); @@ -135,7 +135,7 @@ static const int PENALTY_N4 = 10; /*---- High-level QR Code encoding functions ----*/ // Public function - see documentation comment in header file. -bool qrcodegen_encodeText(const char * text, uint8_t tempBuffer[], uint8_t qrcode[], enum qrcodegen_Ecc ecl, +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) { @@ -187,7 +187,7 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod // Appends the given number of low-order bits of the given value to the given byte-based // bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. -testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int * bitLen) +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int* bitLen) { assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); for(int i = numBits - 1; i >= 0; i--, (*bitLen)++) buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); @@ -235,7 +235,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; for(size_t i = 0; i < len; i++) { - const struct qrcodegen_Segment * seg = &segs[i]; + const struct qrcodegen_Segment* seg = &segs[i]; appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); for(int j = 0; j < seg->bitLength; j++) @@ -305,10 +305,10 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // (not concatenate) the bytes into a single sequence uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); - const uint8_t * dat = data; + const uint8_t* dat = data; for(int i = 0; i < numBlocks; i++) { - int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - uint8_t * ecc = &data[dataLen]; // Temporary storage + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + uint8_t* ecc = &data[dataLen]; // Temporary storage calcReedSolomonRemainder(dat, datLen, generator, blockEccLen, ecc); for(int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data if(j == shortBlockDataLen) k -= numShortBlocks; @@ -782,7 +782,7 @@ static bool getBit(int x, int i) /*---- Segment handling ----*/ // Public function - see documentation comment in header file. -bool qrcodegen_isAlphanumeric(const char * text) +bool qrcodegen_isAlphanumeric(const char* text) { char buffer[64]; snprintf_P(buffer, sizeof(buffer), ALPHANUMERIC_CHARSET); @@ -795,7 +795,7 @@ bool qrcodegen_isAlphanumeric(const char * text) } // Public function - see documentation comment in header file. -bool qrcodegen_isNumeric(const char * text) +bool qrcodegen_isNumeric(const char* text) { assert(text != NULL); for(; *text != '\0'; text++) { @@ -860,7 +860,7 @@ struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, u } // Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeNumeric(const char * digits, uint8_t buf[]) +struct qrcodegen_Segment qrcodegen_makeNumeric(const char* digits, uint8_t buf[]) { assert(digits != NULL); struct qrcodegen_Segment result; @@ -893,7 +893,7 @@ struct qrcodegen_Segment qrcodegen_makeNumeric(const char * digits, uint8_t buf[ } // Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char * text, uint8_t buf[]) +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char* text, uint8_t buf[]) { char buffer[64]; snprintf_P(buffer, sizeof(buffer), ALPHANUMERIC_CHARSET); @@ -911,7 +911,7 @@ struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char * text, uint8_t b unsigned int accumData = 0; int accumCount = 0; for(; *text != '\0'; text++) { - const char * temp = strchr(buffer, *text); // ALPHANUMERIC_CHARSET + const char* temp = strchr(buffer, *text); // ALPHANUMERIC_CHARSET assert(temp != NULL); accumData = accumData * 45 + (unsigned int)(temp - buffer); // ALPHANUMERIC_CHARSET accumCount++; @@ -963,8 +963,8 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int assert(segs != NULL || len == 0); long result = 0; for(size_t i = 0; i < len; i++) { - int numChars = segs[i].numChars; - int bitLength = segs[i].bitLength; + int16_t numChars = segs[i].numChars; + int16_t bitLength = segs[i].bitLength; assert(0 <= numChars && numChars <= INT16_MAX); assert(0 <= bitLength && bitLength <= INT16_MAX); int ccbits = numCharCountBits(segs[i].mode, version); diff --git a/lib/lv_lib_zifont/lv_zifont.cpp b/lib/lv_lib_zifont/lv_zifont.cpp index 176c26a4..f801a907 100644 --- a/lib/lv_lib_zifont/lv_zifont.cpp +++ b/lib/lv_lib_zifont/lv_zifont.cpp @@ -55,9 +55,9 @@ enum zifont_codepage_t8_t { ASCII = 0x01, ISO_8859_1 = 0x03, UTF_8 = 0x18 }; /********************** * STATIC PROTOTYPES **********************/ -const uint8_t* IRAM_ATTR lv_font_get_bitmap_fmt_zifont(const lv_font_t* font, uint32_t unicode_letter); -bool IRAM_ATTR lv_font_get_glyph_dsc_fmt_zifont(const lv_font_t* font, lv_font_glyph_dsc_t* dsc_out, - uint32_t unicode_letter, uint32_t unicode_letter_next); +HASP_ATTRIBUTE_FAST_MEM const uint8_t* lv_font_get_bitmap_fmt_zifont(const lv_font_t* font, uint32_t unicode_letter); +HASP_ATTRIBUTE_FAST_MEM bool lv_font_get_glyph_dsc_fmt_zifont(const lv_font_t* font, lv_font_glyph_dsc_t* dsc_out, + uint32_t unicode_letter, uint32_t unicode_letter_next); /********************** * STATIC VARIABLES @@ -83,8 +83,8 @@ static uint8_t* charBitmap_p; * GLOBAL FUNCTIONS **********************/ -static void IRAM_ATTR blackAdd(uint8_t* charBitmap_p, uint16_t pos); -static void IRAM_ATTR colorsAdd(uint8_t* charBitmap_p, uint8_t color1, uint16_t pos); +HASP_ATTRIBUTE_FAST_MEM static void blackAdd(uint8_t* charBitmap_p, uint16_t pos); +HASP_ATTRIBUTE_FAST_MEM static void colorsAdd(uint8_t* charBitmap_p, uint8_t color1, uint16_t pos); // static uint16_t unicode2codepoint(uint32_t unicode, uint8_t codepage); // static void printBuffer(uint8_t * charBitmap_p, uint8_t w, uint8_t h); @@ -282,7 +282,7 @@ int lv_zifont_font_init(lv_font_t** font, const char* font_path, uint16_t size) * @param unicode_letter an unicode letter which bitmap should be get * @return pointer to the bitmap or NULL if not found */ -const uint8_t* IRAM_ATTR lv_font_get_bitmap_fmt_zifont(const lv_font_t* font, uint32_t unicode_letter) +HASP_ATTRIBUTE_FAST_MEM const uint8_t* lv_font_get_bitmap_fmt_zifont(const lv_font_t* font, uint32_t unicode_letter) { /* Bitmap still in buffer */ if(charInBuffer == unicode_letter && charBitmap_p) { @@ -469,8 +469,8 @@ const uint8_t* IRAM_ATTR lv_font_get_bitmap_fmt_zifont(const lv_font_t* font, ui * @return true: descriptor is successfully loaded into `dsc_out`. * false: the letter was not found, no data is loaded to `dsc_out` */ -bool IRAM_ATTR lv_font_get_glyph_dsc_fmt_zifont(const lv_font_t* font, lv_font_glyph_dsc_t* dsc_out, - uint32_t unicode_letter, uint32_t unicode_letter_next) +HASP_ATTRIBUTE_FAST_MEM bool lv_font_get_glyph_dsc_fmt_zifont(const lv_font_t* font, lv_font_glyph_dsc_t* dsc_out, + uint32_t unicode_letter, uint32_t unicode_letter_next) { /* Only ascii characteres supported for now */ // returning true with a box_h of 0 does not display an error @@ -564,7 +564,7 @@ bool IRAM_ATTR lv_font_get_glyph_dsc_fmt_zifont(const lv_font_t* font, lv_font_g return true; } -static void IRAM_ATTR blackAdd(uint8_t* charBitmap_p, uint16_t pos) +HASP_ATTRIBUTE_FAST_MEM static void blackAdd(uint8_t* charBitmap_p, uint16_t pos) { uint8_t col = pos & 0x0001; // remainder uint16_t map_p = pos >> 1; // devide by 2 @@ -576,7 +576,7 @@ static void IRAM_ATTR blackAdd(uint8_t* charBitmap_p, uint16_t pos) } } -static inline void IRAM_ATTR colorsAdd(uint8_t* charBitmap_p, uint8_t color1, uint16_t pos) +HASP_ATTRIBUTE_FAST_MEM static inline void colorsAdd(uint8_t* charBitmap_p, uint8_t color1, uint16_t pos) { uint32_t col = pos & 0x0001; // remainder uint32_t map_p = pos >> 1; // devide by 2 diff --git a/platformio.ini b/platformio.ini index 71863328..c245956f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,6 @@ build_flags = -D LV_LVGL_H_INCLUDE_SIMPLE ; for lv_drivers -D LV_COMP_CONF_INCLUDE_SIMPLE ; for components -D LV_SYMBOL_DEF_H ; don't load default symbol defines - -D LV_MEM_FULL_DEFRAG_CNT=4 ; stability: run lv_mem_defrag more frequently ; -- ESP build options ------------------------------------ -D SPIFFS_TEMPORAL_FD_CACHE ; speedup opening recent files ; -- ArduinoJson build options ---------------------------- @@ -60,23 +59,23 @@ build_flags = ; -- Hasp build options ---------------------------- -D HASP_VER_MAJ=0 - -D HASP_VER_MIN=5 - -D HASP_VER_REV=2 - -D HASP_LOG_LEVEL=LOG_LEVEL_TRACE + -D HASP_VER_MIN=6 + -D HASP_VER_REV=0 + -D HASP_LOG_LEVEL=LOG_LEVEL_OUTPUT ${override.build_flags} ; -- Shared library dependencies in all environments ; Warning : don't put comments after github links => causes infinite download loop lib_deps = - bxparks/AceButton@^1.8.2 ; GPIO button library - bblanchon/ArduinoJson@^6.17.3 ; Json(l) parser + bxparks/AceButton@^1.8.3 ; GPIO button library + bblanchon/ArduinoJson@^6.18.0 ; Json(l) parser bblanchon/StreamUtils@1.6.0 ; for EEPromStream knolleary/PubSubClient@^2.8.0 ; MQTT client git+https://github.com/fvanroie/ConsoleInput.git ;git+https://github.com/andrethomas/TasmotaSlave.git ;git+https://github.com/lvgl/lvgl.git lvgl/lvgl@^7.11.0 ; from PIO library - bodmer/TFT_eSPI@^2.3.62 ; Tft SPI drivers EXACT version 2.3.5 has compile error + bodmer/TFT_eSPI@^2.3.69 ;git+https://github.com/Bodmer/TFT_eSPI.git ; ------ Unused / Test libraries ;https://github.com/netwizeBE/TFT_eSPI.git @@ -91,11 +90,14 @@ src_filter = +<*> -<.git/> - - - - - +#include #include "esp_system.h" - -#include "hasp_conf.h" - -#include "../device.h" -#include "esp32.h" - +#include // needed to get the ResetInfo #include "driver/adc.h" #include "esp_adc_cal.h" +#include "hasp_conf.h" +#include "../device.h" +#include "esp32.h" #include "hasp_debug.h" #define BACKLIGHT_CHANNEL 0 namespace dev { +static String esp32ResetReason(uint8_t cpuid) +{ + if(cpuid > 1) { + return F("Invalid CPU id"); + } + RESET_REASON reason = rtc_get_reset_reason(cpuid); + + String resetReason((char*)0); + resetReason.reserve(128); + + resetReason += F("CPU"); + resetReason += cpuid; + resetReason += F(": "); + + switch(reason) { + case 1: + resetReason += F("POWERON"); + break; /**<1, Vbat power on reset*/ + case 3: + resetReason += F("SW"); + break; /**<3, Software reset digital core*/ + case 4: + resetReason += F("OWDT"); + break; /**<4, Legacy watch dog reset digital core*/ + case 5: + resetReason += F("DEEPSLEEP"); + break; /**<5, Deep Sleep reset digital core*/ + case 6: + resetReason += F("SDIO"); + break; /**<6, Reset by SLC module, reset digital core*/ + case 7: + resetReason += F("TG0WDT_SYS"); + break; /**<7, Timer Group0 Watch dog reset digital core*/ + case 8: + resetReason += F("TG1WDT_SYS"); + break; /**<8, Timer Group1 Watch dog reset digital core*/ + case 9: + resetReason += F("RTCWDT_SYS"); + break; /**<9, RTC Watch dog Reset digital core*/ + case 10: + resetReason += F("INTRUSION"); + break; /**<10, Instrusion tested to reset CPU*/ + case 11: + resetReason += F("TGWDT_CPU"); + break; /**<11, Time Group reset CPU*/ + case 12: + resetReason += F("SW_CPU"); + break; /**<12, Software reset CPU*/ + case 13: + resetReason += F("RTCWDT_CPU"); + break; /**<13, RTC Watch dog Reset CPU*/ + case 14: + resetReason += F("EXT_CPU"); + break; /**<14, for APP CPU, reseted by PRO CPU*/ + case 15: + resetReason += F("RTCWDT_BROWN_OUT"); + break; /**<15, Reset when the vdd voltage is not stable*/ + case 16: + resetReason += F("RTCWDT_RTC"); + break; /**<16, RTC Watch dog reset digital core and rtc module*/ + default: + resetReason += F("NO_MEAN"); + return resetReason; + } + resetReason += F("_RESET"); + return resetReason; +} + +static void halGetResetInfo(String& resetReason) +{ + resetReason = String(esp32ResetReason(0)); + resetReason += F(" / "); + resetReason += String(esp32ResetReason(1)); +} + +Esp32Device::Esp32Device() +{ + BaseDevice::set_hostname(MQTT_NODENAME); + _backlight_invert = (TFT_BACKLIGHT_ON == LOW); + _backlight_power = 1; + _backlight_level = 255; + _backlight_pin = TFT_BCKL; + + /* fill unique identifier with wifi mac */ + byte mac[6]; + WiFi.macAddress(mac); + _hardware_id.reserve(13); + for(int i = 0; i < 6; ++i) { + if(mac[i] < 0x10) _hardware_id += "0"; + _hardware_id += String(mac[i], HEX).c_str(); + } +} + void Esp32Device::reboot() { - ESP.restart(); + esp_sleep_enable_timer_wakeup(50 * 1000); + esp_deep_sleep_start(); + // ESP.restart(); } void Esp32Device::show_info() @@ -31,23 +125,14 @@ void Esp32Device::show_info() LOG_VERBOSE(TAG_DEV, F("Processor : ESP32")); LOG_VERBOSE(TAG_DEV, F("CPU freq. : %i MHz"), get_cpu_frequency()); // LOG_VERBOSE(TAG_DEV, F("Voltage : %2.2f V"), ESP.getVcc() / 918.0); // 918 empirically determined -} -const char* Esp32Device::get_hostname() -{ - return _hostname.c_str(); -} - -void Esp32Device::set_hostname(const char* hostname) -{ - _hostname = hostname; + if(_sketch_size == 0) _sketch_size = ESP.getSketchSize(); // slow: takes ~1 second } const char* Esp32Device::get_core_version() { return esp_get_idf_version(); // == ESP.getSdkVersion(); } - const char* Esp32Device::get_chip_model() { esp_chip_info_t chip_info; @@ -76,6 +161,11 @@ const char* Esp32Device::get_chip_model() // model += chip_info.revision; } +const char* Esp32Device::get_hardware_id() +{ + return _hardware_id.c_str(); +} + void Esp32Device::set_backlight_pin(uint8_t pin) { _backlight_pin = pin; @@ -157,6 +247,35 @@ bool Esp32Device::is_system_pin(uint8_t pin) return false; } +void Esp32Device::get_info(JsonDocument& doc) +{ + char size_buf[64]; + String buffer((char*)0); + buffer.reserve(64); + + JsonObject info = doc.createNestedObject(F(D_INFO_MODULE)); + + /* ESP Stats */ + buffer = String(get_cpu_frequency()); + buffer += F("MHz"); + info[F(D_INFO_MODEL)] = get_chip_model(); // 10ms + info[F(D_INFO_FREQUENCY)] = buffer; + + info[F(D_INFO_CORE_VERSION)] = get_core_version(); + halGetResetInfo(buffer); + info[F(D_INFO_RESET_REASON)] = buffer; + + Parser::format_bytes(ESP.getFlashChipSize(), size_buf, sizeof(size_buf)); // 25ms + info[F(D_INFO_FLASH_SIZE)] = size_buf; + + if(_sketch_size == 0) _sketch_size = ESP.getSketchSize(); // slow: takes ~1 second + Parser::format_bytes(_sketch_size, size_buf, sizeof(size_buf)); + info[F(D_INFO_SKETCH_USED)] = size_buf; + + Parser::format_bytes(ESP.getFreeSketchSpace(), size_buf, sizeof(size_buf)); + info[F(D_INFO_SKETCH_FREE)] = size_buf; +} + } // namespace dev #if defined(LANBONL8) diff --git a/src/dev/esp32/esp32.h b/src/dev/esp32/esp32.h index 4eaff04f..38bf9db2 100644 --- a/src/dev/esp32/esp32.h +++ b/src/dev/esp32/esp32.h @@ -14,21 +14,14 @@ namespace dev { class Esp32Device : public BaseDevice { public: - Esp32Device() - { - _hostname = MQTT_NODENAME; - _backlight_invert = (TFT_BACKLIGHT_ON == LOW); - _backlight_power = 1; - _backlight_level = 255; - _backlight_pin = TFT_BCKL; - } + Esp32Device(); + void reboot() override; void show_info() override; - const char* get_hostname(); - void set_hostname(const char*); const char* get_core_version(); const char* get_chip_model(); + const char* get_hardware_id(); void set_backlight_pin(uint8_t pin) override; void set_backlight_level(uint8_t val) override; @@ -40,11 +33,13 @@ class Esp32Device : public BaseDevice { size_t get_free_heap() override; uint8_t get_heap_fragmentation() override; uint16_t get_cpu_frequency() override; + void get_info(JsonDocument& doc) override; bool is_system_pin(uint8_t pin) override; private: - std::string _hostname; + std::string _hardware_id; + uint32_t _sketch_size; // cached because function is slow uint8_t _backlight_pin; uint8_t _backlight_level; diff --git a/src/dev/esp32/lanbonl8.cpp b/src/dev/esp32/lanbonl8.cpp index f2f50ad9..215779ce 100644 --- a/src/dev/esp32/lanbonl8.cpp +++ b/src/dev/esp32/lanbonl8.cpp @@ -8,13 +8,32 @@ #include "Arduino.h" #include "dev/esp32/esp32.h" +#include "driver/pcnt.h" // Pulse count driver #include "driver/adc.h" #include "esp_adc_cal.h" #include "hasp_conf.h" #include "hasp_debug.h" -#define BACKLIGHT_CHANNEL 0 +#define BACKLIGHT_CHANNEL 15 + +// https://esp32.com/viewtopic.php?t=14660 +#define PCNT_FREQ_UNIT PCNT_UNIT_0 // Pulse Count Unit 0 +#define PCNT_H_LIM_VAL 10000 // upper limit of counting max. 32767, write +1 to overflow counter, when reached +#define PCNT_L_LIM_VAL -10000 // lower limit of counting max. 32767, write +1 to overflow counter, when reached +#define PCNT_INPUT_SIG_IO 35 // Pulse Input GPIO +#define PCNT_INPUT_CTRL_IO 36 // Pulse Control GPIO +#define PCNT_FILTER_VAL 300 // filter (damping, inertia) value for avoiding glitches in the count, max. 1023 +#define MEASURED_WATTS 1580 +#define MEASURED_PULSES_PER_SECOND 281 +// Per second + +int16_t PulseCounter = 0; // pulse counter, max. value is 32535 +int OverflowCounter = 0; // pulse counter overflow counter +uint32_t totalPulses; + +pcnt_isr_handle_t user_isr_handle = NULL; // interrupt handler - not used +hw_timer_t* timer = NULL; // Instancia do timer #define REF_VOLTAGE 1100 esp_adc_cal_characteristics_t* adc_chars = @@ -51,8 +70,49 @@ static void print_char_val_type(esp_adc_cal_value_t val_type) } } +//------------------------------------------------------------------------------------ +void IRAM_ATTR energy_pulse_counter_overflow(void* arg) +{ // Interrupt for overflow of pulse counter + OverflowCounter = OverflowCounter + 1; // increase overflow counter + PCNT.int_clr.val = BIT(PCNT_FREQ_UNIT); // clean overflow flag + pcnt_counter_clear(PCNT_FREQ_UNIT); // zero and reset of pulse counter unit +} + +//------------------------------------------------------------ +void energy_pulse_counter_init() +{ + pcnt_config_t pcntFreqConfig = {}; // initialise pulse counter + pcntFreqConfig.pulse_gpio_num = PCNT_INPUT_SIG_IO; // pin assignment for pulse counter + pcntFreqConfig.ctrl_gpio_num = PCNT_INPUT_CTRL_IO; // pin assignment for control + pcntFreqConfig.channel = PCNT_CHANNEL_0; // select channel 0 of pulse counter unit 0 + pcntFreqConfig.unit = PCNT_FREQ_UNIT; // select ESP32 pulse counter unit 0 + pcntFreqConfig.pos_mode = PCNT_COUNT_INC; // count rising edges (=change from low to high logical level) as pulses + pcntFreqConfig.neg_mode = PCNT_COUNT_DIS; // Conta na subida do pulso + pcntFreqConfig.lctrl_mode = PCNT_MODE_REVERSE; + pcntFreqConfig.hctrl_mode = PCNT_MODE_KEEP, + pcntFreqConfig.counter_h_lim = PCNT_H_LIM_VAL; // set upper limit of counting + pcntFreqConfig.counter_l_lim = PCNT_L_LIM_VAL; // set lower limit of counting + + pcnt_unit_config(&pcntFreqConfig); // configure rigisters of the pulse counter + + pcnt_counter_pause(PCNT_FREQ_UNIT); // pause puls counter unit + pcnt_counter_clear(PCNT_FREQ_UNIT); // zero and reset of pulse counter unit + + pcnt_event_enable(PCNT_FREQ_UNIT, PCNT_EVT_H_LIM); // enable event for interrupt on reaching upper limit of counting + pcnt_isr_register(energy_pulse_counter_overflow, NULL, 0, + &user_isr_handle); // configure register overflow interrupt handler + pcnt_intr_enable(PCNT_FREQ_UNIT); // enable overflow interrupt + + pcnt_set_filter_value(PCNT_FREQ_UNIT, PCNT_FILTER_VAL); // set damping, inertia + pcnt_filter_enable(PCNT_FREQ_UNIT); // enable counter glitch filter (damping) + + pcnt_counter_resume(PCNT_FREQ_UNIT); // resume counting on pulse counter unit +} + void LanbonL8::init() { + energy_pulse_counter_init(); + // Check if Two Point or Vref are burned into eFuse check_efuse(); @@ -64,6 +124,30 @@ void LanbonL8::init() print_char_val_type(val_type); } +void LanbonL8::loop_5s() +{ // function for reading pulse counter (for timer) + pcnt_get_counter_value(PCNT_FREQ_UNIT, &PulseCounter); // get pulse counter value - maximum value is 16 bits + uint32_t newPulses = OverflowCounter * 10000 + PulseCounter; + uint32_t delta = newPulses - totalPulses; + totalPulses = newPulses; + int16_t watt_10 = DEC / 5 * delta * MEASURED_WATTS / MEASURED_PULSES_PER_SECOND; + int16_t kwh_10 = DEC * totalPulses * MEASURED_WATTS / MEASURED_PULSES_PER_SECOND / 3600 / 1000; + LOG_VERBOSE(TAG_DEV, F("Pulse Counter %d.%d W / %d / %d.%d kWh"), watt_10 / DEC, watt_10 % DEC, totalPulses, + kwh_10 / DEC, kwh_10 % DEC); +} + +//------------------------------------------------------------ +void Read_Reset_PCNT() +{ // function for reading pulse counter (for timer) + pcnt_get_counter_value(PCNT_FREQ_UNIT, &PulseCounter); // get pulse counter value - maximum value is 16 bits + + // resetting counter as if example, delet for application in PiedPiperS + OverflowCounter = 0; // set overflow counter to zero + pcnt_counter_clear(PCNT_FREQ_UNIT); // zero and reset of pulse counter unit + // conterOK = true; // not in use, copy from example code + // ######################################## +} + } // namespace dev dev::LanbonL8 haspDevice; diff --git a/src/dev/esp32/lanbonl8.h b/src/dev/esp32/lanbonl8.h index f32e1fa4..b4e205cd 100644 --- a/src/dev/esp32/lanbonl8.h +++ b/src/dev/esp32/lanbonl8.h @@ -8,11 +8,14 @@ #if defined(LANBONL8) +void Read_PCNT(); + namespace dev { class LanbonL8 : public Esp32Device { public: void init(); + void loop_5s(); }; } // namespace dev diff --git a/src/dev/esp8266/esp8266.cpp b/src/dev/esp8266/esp8266.cpp index e685c5c4..0fc52514 100644 --- a/src/dev/esp8266/esp8266.cpp +++ b/src/dev/esp8266/esp8266.cpp @@ -5,6 +5,7 @@ #include "Arduino.h" #include +#include #include "esp8266.h" @@ -15,6 +16,25 @@ namespace dev { +Esp8266Device::Esp8266Device() +{ + _hostname = MQTT_NODENAME; + _backlight_invert = (TFT_BACKLIGHT_ON == LOW); + _backlight_power = 1; + _backlight_level = 255; + _core_version = ESP.getCoreVersion().c_str(); + _backlight_pin = TFT_BCKL; + + /* fill unique identifier with wifi mac */ + byte mac[6]; + WiFi.macAddress(mac); + _hardware_id.reserve(13); + for(int i = 0; i < 6; ++i) { + if(mac[i] < 0x10) _hardware_id += "0"; + _hardware_id += String(mac[i], HEX).c_str(); + } +} + void Esp8266Device::reboot() { ESP.restart(); @@ -46,6 +66,11 @@ const char* Esp8266Device::get_chip_model() return "ESP8266"; } +const char* Esp8266Device::get_hardware_id() +{ + return _hardware_id.c_str(); +} + void Esp8266Device::set_backlight_pin(uint8_t pin) { _backlight_pin = pin; diff --git a/src/dev/esp8266/esp8266.h b/src/dev/esp8266/esp8266.h index 2880ed7e..ee829fd0 100644 --- a/src/dev/esp8266/esp8266.h +++ b/src/dev/esp8266/esp8266.h @@ -14,15 +14,7 @@ namespace dev { class Esp8266Device : public BaseDevice { public: - Esp8266Device() - { - _hostname = MQTT_NODENAME; - _backlight_invert = (TFT_BACKLIGHT_ON == LOW); - _backlight_power = 1; - _backlight_level = 255; - _core_version = ESP.getCoreVersion().c_str(); - _backlight_pin = TFT_BCKL; - } + Esp8266Device(); void reboot() override; void show_info() override; @@ -31,6 +23,7 @@ class Esp8266Device : public BaseDevice { void set_hostname(const char*); const char* get_core_version(); const char* get_chip_model(); + const char* get_hardware_id(); void set_backlight_pin(uint8_t pin) override; void set_backlight_level(uint8_t val) override; @@ -47,6 +40,7 @@ class Esp8266Device : public BaseDevice { private: std::string _hostname; + std::string _hardware_id; std::string _core_version; uint8_t _backlight_pin; diff --git a/src/dev/posix/hasp_posix.cpp b/src/dev/posix/hasp_posix.cpp index 08e2b553..ad994285 100644 --- a/src/dev/posix/hasp_posix.cpp +++ b/src/dev/posix/hasp_posix.cpp @@ -30,7 +30,7 @@ PosixDevice::PosixDevice() // LOG_VERBOSE(0,"Version: %s", uts.version); // LOG_VERBOSE(0,"Machine: %s", uts.machine); - char version[128]; + char version[256]; snprintf(version, sizeof(version), "%s %s", uts.sysname, uts.release); _core_version = version; _chip_model = uts.machine; @@ -66,21 +66,29 @@ const char* PosixDevice::get_hostname() { return _hostname.c_str(); } + void PosixDevice::set_hostname(const char* hostname) { _hostname = hostname; monitor_title(hostname); // SDL_SetWindowTitle(monitor.window, hostname); } + const char* PosixDevice::get_core_version() { return _core_version.c_str(); } + const char* PosixDevice::get_chip_model() { return _chip_model.c_str(); } +const char* PosixDevice::get_hardware_id() +{ + return "223344556677"; +} + void PosixDevice::set_backlight_pin(uint8_t pin) { // PosixDevice::backlight_pin = pin; diff --git a/src/dev/posix/hasp_posix.h b/src/dev/posix/hasp_posix.h index 3a47edc8..589bd16e 100644 --- a/src/dev/posix/hasp_posix.h +++ b/src/dev/posix/hasp_posix.h @@ -38,6 +38,7 @@ class PosixDevice : public BaseDevice { void set_hostname(const char*); const char* get_core_version(); const char* get_chip_model(); + const char* get_hardware_id(); void set_backlight_pin(uint8_t pin); void set_backlight_level(uint8_t val); diff --git a/src/dev/stm32f4/stm32f4.cpp b/src/dev/stm32f4/stm32f4.cpp index ae73b238..8a1a84ac 100644 --- a/src/dev/stm32f4/stm32f4.cpp +++ b/src/dev/stm32f4/stm32f4.cpp @@ -30,6 +30,7 @@ const char* Stm32f4Device::get_hostname() { return _hostname.c_str(); } + void Stm32f4Device::set_hostname(const char* hostname) { _hostname = hostname; @@ -40,6 +41,14 @@ const char* Stm32f4Device::get_core_version() // return ESP.getCoreVersion().c_str(); } +const char* Stm32f4Device::get_hardware_id() +{ + // https://stm32duinoforum.com/forum/viewtopic_f_29_t_2909_start_10.html + // Serial.println("UID [HEX] : "+String(*(uint32_t*)(UID_BASE), HEX)+" "+String(*(uint32_t*)(UID_BASE+0x04), + // HEX)+" "+String(*(uint32_t*)(UID_BASE+0x08), HEX)); + return _hardware_id.c_str(); +} + void Stm32f4Device::set_backlight_pin(uint8_t pin) { _backlight_pin = pin; diff --git a/src/dev/stm32f4/stm32f4.h b/src/dev/stm32f4/stm32f4.h index 35c1bd88..0dc65f45 100644 --- a/src/dev/stm32f4/stm32f4.h +++ b/src/dev/stm32f4/stm32f4.h @@ -30,6 +30,7 @@ class Stm32f4Device : public BaseDevice { void set_hostname(const char*); const char* get_core_version(); const char* get_chip_model(); + const char* get_chip_hardware_id(); void set_backlight_pin(uint8_t pin) override; void set_backlight_level(uint8_t val) override; @@ -46,6 +47,7 @@ class Stm32f4Device : public BaseDevice { private: std::string _hostname; + std::string _hardware_id; uint8_t _backlight_pin; uint8_t _backlight_level; diff --git a/src/dev/win32/hasp_win32.cpp b/src/dev/win32/hasp_win32.cpp index b6917d08..a69fd4a3 100644 --- a/src/dev/win32/hasp_win32.cpp +++ b/src/dev/win32/hasp_win32.cpp @@ -55,11 +55,17 @@ const char* Win32Device::get_core_version() { return _core_version.c_str(); } + const char* Win32Device::get_chip_model() { return "SDL2"; } +const char* Win32Device::get_hardware_id() +{ + return "112233445566"; +} + void Win32Device::set_backlight_pin(uint8_t pin) { // Win32Device::_backlight_pin = pin; diff --git a/src/dev/win32/hasp_win32.h b/src/dev/win32/hasp_win32.h index 67a727b1..a222b39e 100644 --- a/src/dev/win32/hasp_win32.h +++ b/src/dev/win32/hasp_win32.h @@ -59,6 +59,7 @@ class Win32Device : public BaseDevice { void set_hostname(const char*); const char* get_core_version(); const char* get_chip_model(); + const char* get_hardware_id(); void set_backlight_pin(uint8_t pin); void set_backlight_level(uint8_t val); diff --git a/src/drv/hasp_drv_touch.cpp b/src/drv/hasp_drv_touch.cpp index 317d8f18..900fef4c 100644 --- a/src/drv/hasp_drv_touch.cpp +++ b/src/drv/hasp_drv_touch.cpp @@ -1,10 +1,10 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ +#include "hasplib.h" + #include "hasp_drv_touch.h" -#include "hasp/hasp.h" #include "drv/tft_driver.h" -#include "lvgl.h" #if TOUCH_DRIVER == 2046 #if defined(USE_FSMC) @@ -102,13 +102,13 @@ static inline bool drv_touchpad_getXY(int16_t* touchX, int16_t* touchY) touched = Touch_getXY(&normal_x, &normal_y, false); #elif TOUCH_DRIVER == 5206 - touched = FT5206_getXY(&normal_x, &normal_y, false); + touched = FT5206_getXY(&normal_x, &normal_y, false); // no debug #elif TOUCH_DRIVER == 6336 - touched = FT6336U_getXY(&normal_x, &normal_y, true); + touched = FT6336U_getXY(&normal_x, &normal_y, false); // no debug #elif TOUCH_DRIVER == 610 - touched = STMPE610_getXY(&normal_x, &normal_y, drv_touch_rotation, true); + touched = STMPE610_getXY(&normal_x, &normal_y, drv_touch_rotation, false); // no debug #else // xpt2046_alt_drv_read(indev_driver, data); @@ -174,7 +174,7 @@ static inline bool drv_touchpad_getXY(int16_t* touchX, int16_t* touchY) return touched; } -bool drv_touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data) +IRAM_ATTR bool drv_touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data) { #if TOUCH_DRIVER > 0 int16_t touchX = 0; @@ -211,7 +211,7 @@ bool drv_touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data) return false; } -void drv_touch_loop() +IRAM_ATTR void drv_touch_loop() { #if TOUCH_DRIVER == 911 GT911_loop(); diff --git a/src/drv/hasp_drv_touch.h b/src/drv/hasp_drv_touch.h index f1b83f10..d1d3161e 100644 --- a/src/drv/hasp_drv_touch.h +++ b/src/drv/hasp_drv_touch.h @@ -4,14 +4,14 @@ #ifndef HASP_DRV_TOUCH_H #define HASP_DRV_TOUCH_H -#include "lvgl.h" +#include "hasplib.h" #ifndef TOUCH_DRIVER #define TOUCH_DRIVER -1 // No Touch #endif void drv_touch_init(uint8_t rotation); -bool drv_touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data); -void drv_touch_loop(); +IRAM_ATTR bool drv_touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data); +IRAM_ATTR void drv_touch_loop(); #endif \ No newline at end of file diff --git a/src/drv/tft_driver_sdl2.cpp b/src/drv/tft_driver_sdl2.cpp index 0c8801a1..7023ca7c 100644 --- a/src/drv/tft_driver_sdl2.cpp +++ b/src/drv/tft_driver_sdl2.cpp @@ -49,7 +49,7 @@ void TftSdl::init(int w, int h) /* Add a display * Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/ - monitor_init(MONITOR_HOR_RES, MONITOR_VER_RES); + monitor_init(w, h); // (MONITOR_HOR_RES, MONITOR_VER_RES); monitor_title(haspDevice.get_hostname()); /* Add the mouse as input device diff --git a/src/drv/tft_driver_tftespi.cpp b/src/drv/tft_driver_tftespi.cpp index 5713ee85..54052644 100644 --- a/src/drv/tft_driver_tftespi.cpp +++ b/src/drv/tft_driver_tftespi.cpp @@ -27,11 +27,11 @@ void TftEspi::show_info() tft.getSetup(tftSetup); LOG_VERBOSE(TAG_TFT, F("TFT_eSPI : v%s"), tftSetup.version.c_str()); - LOG_VERBOSE(TAG_TFT, F("Transactns : %s"), (tftSetup.trans == 1) ? PSTR("Yes") : PSTR("No")); + LOG_VERBOSE(TAG_TFT, F("Transactns : %s"), (tftSetup.trans == 1) ? PSTR(D_YES) : PSTR(D_NO)); LOG_VERBOSE(TAG_TFT, F("Interface : %s"), (tftSetup.serial == 1) ? PSTR("SPI") : PSTR("Parallel")); #if defined(ARDUINO_ARCH_ESP8266) - LOG_VERBOSE(TAG_TFT, F("SPI overlap: %s"), (tftSetup.overlap == 1) ? PSTR("Yes") : PSTR("No")); + LOG_VERBOSE(TAG_TFT, F("SPI overlap: %s"), (tftSetup.overlap == 1) ? PSTR(D_YES) : PSTR(D_NO)); #endif if(tftSetup.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch @@ -117,25 +117,33 @@ void TftEspi::set_rotation(uint8_t rotation) void TftEspi::set_invert(bool invert) { char buffer[4]; - memcpy_P(buffer, invert ? PSTR("yes") : PSTR("no"), sizeof(buffer)); + memcpy_P(buffer, invert ? PSTR(D_YES) : PSTR(D_NO), sizeof(buffer)); LOG_VERBOSE(TAG_TFT, F("Invert Disp: %s"), buffer); tft.invertDisplay(invert); } -void TftEspi::flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) +/* Update TFT */ +void IRAM_ATTR TftEspi::flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) { - size_t len = lv_area_get_size(area); + uint32_t w = (area->x2 - area->x1 + 1); + uint32_t h = (area->y2 - area->y1 + 1); + // size_t len = lv_area_get_size(area); + uint32_t len = w * h; - /* Update TFT */ - tft.startWrite(); /* Start new TFT transaction */ - tft.setWindow(area->x1, area->y1, area->x2, area->y2); /* set the working window */ #ifdef USE_DMA_TO_TFT - tft.pushPixelsDMA((uint16_t*)color_p, len); /* Write words at once */ + tft.startWrite(); /* Start new TFT transaction */ + // tft.setWindow(area->x1, area->y1, area->x2, area->y2); + tft.setAddrWindow(area->x1, area->y1, w, h); /* set the working window */ + tft.pushPixelsDMA((uint16_t*)color_p, len); /* Write words at once */ + tft.endWrite(); /* terminate TFT transaction */ #else - tft.pushPixels((uint16_t*)color_p, len); /* Write words at once */ + tft.startWrite(); /* Start new TFT transaction */ + // tft.setWindow(area->x1, area->y1, area->x2, area->y2); + tft.setAddrWindow(area->x1, area->y1, w, h); /* set the working window */ + tft.pushPixels((uint16_t*)color_p, len); /* Write words at once */ + tft.endWrite(); /* terminate TFT transaction */ #endif - tft.endWrite(); /* terminate TFT transaction */ /* Tell lvgl that flushing is done */ lv_disp_flush_ready(disp); diff --git a/src/drv/tft_driver_tftespi.h b/src/drv/tft_driver_tftespi.h index dbda686f..53a21574 100644 --- a/src/drv/tft_driver_tftespi.h +++ b/src/drv/tft_driver_tftespi.h @@ -52,7 +52,7 @@ class TftEspi : BaseTft { if(pin != -1) { char buffer[64]; snprintf_P(buffer, sizeof(buffer), PSTR("%-11s: %s (GPIO %02d)"), String(pinfunction).c_str(), - halGpioName(pin).c_str(), pin); + haspDevice.gpio_name(pin).c_str(), pin); LOG_VERBOSE(TAG_TFT, buffer); } } diff --git a/src/drv/touch/hasp_drv_ft6336u.cpp b/src/drv/touch/hasp_drv_ft6336u.cpp index 15cc921d..07bcd14a 100644 --- a/src/drv/touch/hasp_drv_ft6336u.cpp +++ b/src/drv/touch/hasp_drv_ft6336u.cpp @@ -1,17 +1,17 @@ #if TOUCH_DRIVER == 6336 - #include - #include "FT6336U.h" - #include "ArduinoLog.h" +#include +#include "FT6336U.h" +#include "ArduinoLog.h" - #include "hasp_drv_ft6336u.h" +#include "hasp_drv_ft6336u.h" - #define RST_PIN (TOUCH_RST) // -1 if pin is connected to VCC else set pin number +#define RST_PIN (TOUCH_RST) // -1 if pin is connected to VCC else set pin number -FT6336U * touchpanel; +FT6336U* touchpanel; // Read touch points -bool FT6336U_getXY(int16_t * touchX, int16_t * touchY, bool debug) +HASP_ATTRIBUTE_FAST_MEM bool FT6336U_getXY(int16_t* touchX, int16_t* touchY, bool debug) { if(touchpanel->read_touch_number() != 1) return false; @@ -20,7 +20,7 @@ bool FT6336U_getXY(int16_t * touchX, int16_t * touchY, bool debug) return true; } -void scan(TwoWire & i2c) +void scan(TwoWire& i2c) { byte error, address; int nDevices; diff --git a/src/drv/touch/hasp_drv_ft6336u.h b/src/drv/touch/hasp_drv_ft6336u.h index a52100e1..afae44c0 100644 --- a/src/drv/touch/hasp_drv_ft6336u.h +++ b/src/drv/touch/hasp_drv_ft6336u.h @@ -8,7 +8,7 @@ #include "hasp_debug.h" // for TAG_DRVR -bool FT6336U_getXY(int16_t* touchX, int16_t* touchY, bool debug); +HASP_ATTRIBUTE_FAST_MEM bool FT6336U_getXY(int16_t* touchX, int16_t* touchY, bool debug); void FT6336U_init(); #endif diff --git a/src/drv/touch/hasp_drv_stmpe610.cpp b/src/drv/touch/hasp_drv_stmpe610.cpp index f1a6df4a..3815135b 100644 --- a/src/drv/touch/hasp_drv_stmpe610.cpp +++ b/src/drv/touch/hasp_drv_stmpe610.cpp @@ -1,69 +1,71 @@ #if TOUCH_DRIVER == 610 - #include - #include "Adafruit_STMPE610.h" - #include "ArduinoLog.h" +#include "hasp_conf.h" - #include "hasp_drv_stmpe610.h" +#include +#include "Adafruit_STMPE610.h" +#include "ArduinoLog.h" + +#include "hasp_drv_stmpe610.h" // This is calibration data for the raw touch data to the screen coordinates - #define TS_MINX 3800 - #define TS_MAXX 100 - #define TS_MINY 100 - #define TS_MAXY 3750 +#define TS_MINX 3800 +#define TS_MAXX 100 +#define TS_MINY 100 +#define TS_MAXY 3750 static Adafruit_STMPE610 touch = Adafruit_STMPE610(STMPE_CS); // Read touch points from global variable -bool IRAM_ATTR STMPE610_getXY(int16_t * touchX, int16_t * touchY, uint8_t touchRotation, bool debug) +HASP_ATTRIBUTE_FAST_MEM bool STMPE610_getXY(int16_t* touchX, int16_t* touchY, uint8_t touchRotation, bool debug) { uint16_t x, y; uint8_t z; - if(! touch.touched()) return false; + if(!touch.touched()) return false; - while (! touch.bufferEmpty()) { + while(!touch.bufferEmpty()) { touch.readData(&x, &y, &z); if(debug) Log.trace(TAG_DRVR, F("STMPE610: x=%i y=%i z=%i r=%i"), x, y, z, touchRotation); } touch.writeRegister8(STMPE_INT_STA, 0xFF); - if (1 == touchRotation) { - #if HX8357D_DRIVER == 1 + if(1 == touchRotation) { +#if HX8357D_DRIVER == 1 y = map(y, TS_MINX, TS_MAXX, 0, TFT_HEIGHT); x = map(x, TS_MINY, TS_MAXY, TFT_WIDTH, 0); - #else +#else x = map(x, TS_MAXX, TS_MINX, 0, TFT_WIDTH); y = map(y, TS_MAXY, TS_MINY, 0, TFT_HEIGHT); - #endif - } else if (2 == touchRotation) { - #if HX8357D_DRIVER == 1 +#endif + } else if(2 == touchRotation) { +#if HX8357D_DRIVER == 1 x = map(x, TS_MAXX, TS_MINX, TFT_WIDTH, 0); y = map(y, TS_MAXY, TS_MINY, 0, TFT_HEIGHT); - #else +#else x = map(x, TS_MAXX, TS_MINX, 0, TFT_WIDTH); y = map(y, TS_MAXY, TS_MINY, 0, TFT_HEIGHT); - #endif +#endif } else { - #if HX8357D_DRIVER == 1 +#if HX8357D_DRIVER == 1 x = map(x, TS_MINX, TS_MAXX, TFT_WIDTH, 0); y = map(y, TS_MINY, TS_MAXY, 0, TFT_HEIGHT); - #else +#else x = map(x, TS_MINX, TS_MAXX, 0, TFT_WIDTH); y = map(y, TS_MINY, TS_MAXY, 0, TFT_HEIGHT); - #endif +#endif } *touchX = x; *touchY = y; return true; - } void STMPE610_init() { - if (! touch.begin()) { - Log.trace(TAG_DRVR, F("STMPE610 not found!")); + LOG_INFO(TAG_DRVR, F("STMPE610 " D_SERVICE_STARTING)); + if(!touch.begin()) { + LOG_ERROR(TAG_DRVR, F("STMPE610 " D_SERVICE_START_FAILED)); } else { - Log.trace(TAG_DRVR, F("STMPE610 touch driver started")); + LOG_INFO(TAG_DRVR, F("STMPE610 " D_SERVICE_STARTED)); } } #endif \ No newline at end of file diff --git a/src/drv/touch/hasp_drv_stmpe610.h b/src/drv/touch/hasp_drv_stmpe610.h index 2eb07f99..fe0c3c81 100644 --- a/src/drv/touch/hasp_drv_stmpe610.h +++ b/src/drv/touch/hasp_drv_stmpe610.h @@ -6,9 +6,9 @@ #if TOUCH_DRIVER == 610 - #include "hasp_debug.h" // for TAG_DRVR +#include "hasp_debug.h" // for TAG_DRVR -bool IRAM_ATTR STMPE610_getXY(int16_t * touchX, int16_t * touchY, uint8_t touchRotation, bool debug); +HASP_ATTRIBUTE_FAST_MEM bool STMPE610_getXY(int16_t* touchX, int16_t* touchY, uint8_t touchRotation, bool debug); void STMPE610_init(); #endif diff --git a/src/drv/touch/hasp_drv_tft_espi.cpp b/src/drv/touch/hasp_drv_tft_espi.cpp index 552828e4..a15721ee 100644 --- a/src/drv/touch/hasp_drv_tft_espi.cpp +++ b/src/drv/touch/hasp_drv_tft_espi.cpp @@ -8,7 +8,6 @@ #if defined(TOUCH_CS) -#include "hal/hasp_hal.h" // for halGpioName() #include "dev/device.h" #include "drv/tft_driver.h" diff --git a/src/hal/hasp_hal.cpp b/src/hal/hasp_hal.cpp index 0b2bc579..967a1d70 100644 --- a/src/hal/hasp_hal.cpp +++ b/src/hal/hasp_hal.cpp @@ -99,22 +99,22 @@ void halRestartMcu(void) } // halt } -String halGetResetInfo() -{ -#if defined(ARDUINO_ARCH_ESP32) - String resetReason((char*)0); - resetReason.reserve(128); +// String halGetResetInfo() +// { +// #if defined(ARDUINO_ARCH_ESP32) +// String resetReason((char*)0); +// resetReason.reserve(128); - resetReason += String(esp32ResetReason(0)); - resetReason += F(" / "); - resetReason += String(esp32ResetReason(1)); - return resetReason; -#elif defined(ARDUINO_ARCH_ESP8266) - return ESP.getResetInfo(); -#else - return ""; -#endif -} +// resetReason += String(esp32ResetReason(0)); +// resetReason += F(" / "); +// resetReason += String(esp32ResetReason(1)); +// return resetReason; +// #elif defined(ARDUINO_ARCH_ESP8266) +// return ESP.getResetInfo(); +// #else +// return ""; +// #endif +// } // String halGetCoreVersion() // { @@ -127,48 +127,48 @@ String halGetResetInfo() // #endif // } -String halGetChipModel() -{ - String model((char*)0); - model.reserve(128); +// String halGetChipModel() +// { +// String model((char*)0); +// model.reserve(128); -#if defined(STM32F4xx) - model = F("STM32"); -#endif +// #if defined(STM32F4xx) +// model = F("STM32"); +// #endif -#if defined(STM32F4xx) - model = F("STM32F4xx"); +// #if defined(STM32F4xx) +// model = F("STM32F4xx"); -#elif defined(ARDUINO_ARCH_ESP8266) - model = F("ESP8266"); +// #elif defined(ARDUINO_ARCH_ESP8266) +// model = F("ESP8266"); -#elif defined(ARDUINO_ARCH_ESP32) - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); +// #elif defined(ARDUINO_ARCH_ESP32) +// esp_chip_info_t chip_info; +// esp_chip_info(&chip_info); - model = chip_info.cores; - model += F("core "); - switch(chip_info.model) { - case CHIP_ESP32: - model += F("ESP32"); - break; -#ifdef CHIP_ESP32S2 - case CHIP_ESP32S2: - model += F("ESP32-S2"); - break; -#endif - default: - model = F("Unknown ESP32"); - } - model += F(" rev"); - model += chip_info.revision; +// model = chip_info.cores; +// model += F("core "); +// switch(chip_info.model) { +// case CHIP_ESP32: +// model += F("ESP32"); +// break; +// #ifdef CHIP_ESP32S2 +// case CHIP_ESP32S2: +// model += F("ESP32-S2"); +// break; +// #endif +// default: +// model = F("Unknown ESP32"); +// } +// model += F(" rev"); +// model += chip_info.revision; -#else - model = F("Unknown"); -#endif +// #else +// model = F("Unknown"); +// #endif - return model; -} +// return model; +// } /*******************************/ /* Memory Management Functions */ @@ -216,43 +216,43 @@ int getMemFree() { // returns the amount of free memory in bytes return mi.fordblks + freeHighMemory(); } */ -size_t halGetMaxFreeBlock() -{ -#if defined(ARDUINO_ARCH_ESP32) - return ESP.getMaxAllocHeap(); -#elif defined(ARDUINO_ARCH_ESP8266) - return ESP.getMaxFreeBlockSize(); -#else - return freeHighMemory(); -#endif -} +// size_t halGetMaxFreeBlock() +// { +// #if defined(ARDUINO_ARCH_ESP32) +// return ESP.getMaxAllocHeap(); +// #elif defined(ARDUINO_ARCH_ESP8266) +// return ESP.getMaxFreeBlockSize(); +// #else +// return freeHighMemory(); +// #endif +// } -size_t halGetFreeHeap(void) -{ -#if defined(ARDUINO_ARCH_ESP32) - return ESP.getFreeHeap(); -#elif defined(ARDUINO_ARCH_ESP8266) - return ESP.getFreeHeap(); -#else - struct mallinfo chuncks = mallinfo(); +// size_t halGetFreeHeap(void) +// { +// #if defined(ARDUINO_ARCH_ESP32) +// return ESP.getFreeHeap(); +// #elif defined(ARDUINO_ARCH_ESP8266) +// return ESP.getFreeHeap(); +// #else +// struct mallinfo chuncks = mallinfo(); - // fordblks - // This is the total size of memory occupied by free (not in use) chunks. +// // fordblks +// // This is the total size of memory occupied by free (not in use) chunks. - return chuncks.fordblks + freeHighMemory(); -#endif -} +// return chuncks.fordblks + freeHighMemory(); +// #endif +// } -uint8_t halGetHeapFragmentation() -{ -#if defined(ARDUINO_ARCH_ESP32) - return (int8_t)(100.00f - (float)ESP.getMaxAllocHeap() * 100.00f / (float)ESP.getFreeHeap()); -#elif defined(ARDUINO_ARCH_ESP8266) - return ESP.getHeapFragmentation(); -#else - return (int8_t)(100.00f - (float)freeHighMemory() * 100.00f / (float)halGetFreeHeap()); -#endif -} +// uint8_t halGetHeapFragmentation() +// { +// #if defined(ARDUINO_ARCH_ESP32) +// return (int8_t)(100.00f - (float)ESP.getMaxAllocHeap() * 100.00f / (float)ESP.getFreeHeap()); +// #elif defined(ARDUINO_ARCH_ESP8266) +// return ESP.getHeapFragmentation(); +// #else +// return (int8_t)(100.00f - (float)freeHighMemory() * 100.00f / (float)halGetFreeHeap()); +// #endif +// } String halGetMacAddress(int start, const char* seperator) { @@ -284,14 +284,14 @@ String halGetMacAddress(int start, const char* seperator) return cMac; } -uint16_t halGetCpuFreqMHz() -{ -#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_32) - return ESP.getCpuFreqMHz(); -#else - return (F_CPU / 1000 / 1000); -#endif -} +// uint16_t halGetCpuFreqMHz() +// { +// #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_32) +// return ESP.getCpuFreqMHz(); +// #else +// return (F_CPU / 1000 / 1000); +// #endif +// } String halDisplayDriverName() { diff --git a/src/hal/hasp_hal.h b/src/hal/hasp_hal.h index 61cfa2a5..4d397f7d 100644 --- a/src/hal/hasp_hal.h +++ b/src/hal/hasp_hal.h @@ -7,7 +7,7 @@ #include // void halRestartMcu(void); -String halGetResetInfo(void); +// String halGetResetInfo(void); // uint8_t halGetHeapFragmentation(void); // size_t halGetMaxFreeBlock(void); // size_t halGetFreeHeap(void); @@ -16,6 +16,6 @@ String halGetResetInfo(void); String halGetMacAddress(int start, const char* seperator); // uint16_t halGetCpuFreqMHz(void); // String halDisplayDriverName(void); -String halGpioName(uint8_t gpio); +// String halGpioName(uint8_t gpio); #endif \ No newline at end of file diff --git a/src/hasp/hasp.cpp b/src/hasp/hasp.cpp index e94dd47e..848d9dd6 100644 --- a/src/hasp/hasp.cpp +++ b/src/hasp/hasp.cpp @@ -1,6 +1,9 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ +#include "hasplib.h" +#include "dev/device.h" + #ifdef ARDUINO #include "ArduinoLog.h" #endif @@ -11,15 +14,10 @@ #include #endif -#include "ArduinoJson.h" - #if HASP_USE_EEPROM > 0 #include "StreamUtils.h" // For EEPromStream #endif -#include "lvgl.h" -#include "lv_conf.h" - #if HASP_USE_DEBUG > 0 #include "../hasp_debug.h" #endif @@ -31,12 +29,6 @@ //#include "hasp_filesystem.h" included in hasp_conf.h #endif -#include "hasplib.h" -#include "hasp.h" -#include "lv_theme_hasp.h" - -#include "dev/device.h" - #if HASP_USE_EEPROM > 0 #include "EEPROM.h" #endif @@ -110,30 +102,44 @@ lv_font_t* hasp_get_font(uint8_t fontid) /** * Check if sleep state needs to be updated */ -bool hasp_update_sleep_state() +HASP_ATTRIBUTE_FAST_MEM bool hasp_update_sleep_state() { uint32_t idle = lv_disp_get_inactive_time(NULL); if(sleepTimeLong > 0 && idle >= (sleepTimeShort + sleepTimeLong) * 1000U) { if(hasp_sleep_state != HASP_SLEEP_LONG) { - dispatch_output_idle_state(HASP_SLEEP_LONG); hasp_sleep_state = HASP_SLEEP_LONG; + dispatch_idle(NULL, NULL); } } else if(sleepTimeShort > 0 && idle >= sleepTimeShort * 1000U) { if(hasp_sleep_state != HASP_SLEEP_SHORT) { - dispatch_output_idle_state(HASP_SLEEP_SHORT); hasp_sleep_state = HASP_SLEEP_SHORT; + dispatch_idle(NULL, NULL); } } else { if(hasp_sleep_state != HASP_SLEEP_OFF) { - dispatch_output_idle_state(HASP_SLEEP_OFF); hasp_sleep_state = HASP_SLEEP_OFF; + dispatch_idle(NULL, NULL); } } return (hasp_sleep_state != HASP_SLEEP_OFF); } +void hasp_get_sleep_state(char* payload) +{ + switch(hasp_sleep_state) { + case HASP_SLEEP_LONG: + memcpy_P(payload, PSTR("long"), 5); + break; + case HASP_SLEEP_SHORT: + memcpy_P(payload, PSTR("short"), 6); + break; + default: + memcpy_P(payload, PSTR("off"), 4); + } +} + void hasp_enable_wakeup_touch() { lv_obj_set_click(lv_disp_get_layer_sys(NULL), true); // enable first touch @@ -219,7 +225,7 @@ void haspReconnect() void haspProgressVal(uint8_t val) { lv_obj_t* layer = lv_disp_get_layer_sys(NULL); - lv_obj_t* bar = hasp_find_obj_from_parent_id(haspPages.get_obj(255), (uint8_t)10); + lv_obj_t* bar = hasp_find_obj_from_page_id(255U, 10U); if(layer && bar) { if(val == 255) { if(!lv_obj_get_hidden(bar)) { @@ -227,8 +233,8 @@ void haspProgressVal(uint8_t val) lv_obj_set_style_local_bg_opa(layer, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); // lv_obj_set_style_local_value_str(bar, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, ""); - // lv_obj_set_value_str_txt(bar, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, NULL); //TODO: call our custom - // function to free the memory + // lv_obj_set_value_str_txt(bar, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, NULL); + // TODO: call our custom function to free the memory #if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) // progress_str.clear(); @@ -248,9 +254,7 @@ void haspProgressVal(uint8_t val) // Sets the value string of the global progress bar void haspProgressMsg(const char* msg) { - lv_obj_t* bar = hasp_find_obj_from_parent_id(haspPages.get_obj(255), (uint8_t)10); - - if(bar) { + if(lv_obj_t* bar = hasp_find_obj_from_page_id(255U, 10U)) { char value_str[10]; snprintf_P(value_str, sizeof(value_str), PSTR("value_str")); hasp_process_obj_attribute(bar, value_str, msg, true); @@ -278,14 +282,17 @@ void haspProgressMsg(const __FlashStringHelper* msg) /*Add a custom apply callback*/ static void custom_font_apply_cb(lv_theme_t* th, lv_obj_t* obj, lv_theme_style_t name) { - lv_style_list_t* list; + /* lv_style_list_t* list; - switch(name) { - case LV_THEME_BTN: - list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN); - // _lv_style_list_add_style(list, &my_style); - break; - } + switch(name) { + case LV_THEME_BTN: + list = lv_obj_get_style_list(obj, LV_BTN_PART_MAIN); + // _lv_style_list_add_style(list, &my_style); + break; + default: + // nothing + ; + } */ } /** @@ -298,7 +305,7 @@ void haspSetup(void) /******* File System Test ********************************************************************/ // lv_fs_file_t f; // lv_fs_res_t res; - // res = lv_fs_open(&f, "E:/config.json", LV_FS_MODE_RD); + // res = lv_fs_open(&f, "L:/config.json", LV_FS_MODE_RD); // if(res == LV_FS_RES_OK) // LOG_VERBOSE(TAG_HASP, F("Opening config.json OK")); // else @@ -337,7 +344,7 @@ void haspSetup(void) // WARNING: hasp_font needs to be null ! if(lv_zifont_font_init(&hasp_font, haspZiFontPath, 32) != 0) { - LOG_ERROR(TAG_HASP, F("Failed to set font to %s"), haspZiFontPath); + if(strlen(haspZiFontPath) > 0) LOG_WARNING(TAG_HASP, F("Failed to set font to %s"), haspZiFontPath); haspFonts[0] = LV_THEME_DEFAULT_FONT_SMALL; } else { // defaultFont = haspFonts[0]; @@ -356,8 +363,8 @@ void haspSetup(void) if(haspFonts[3] == nullptr) haspFonts[3] = LV_THEME_DEFAULT_FONT_TITLE; // haspFonts[0] = lv_font_load("E:/font_1.fnt"); - // haspFonts[2] = lv_font_load("E:/font_2.fnt"); - // haspFonts[3] = lv_font_load("E:/font_3.fnt"); + // haspFonts[2] = lv_font_load("E:/font_2.fnt"); + // haspFonts[3] = lv_font_load("E:/font_3.fnt"); /* ********** Theme Initializations ********** */ if(haspThemeId == 8) haspThemeId = 1; // update old HASP id @@ -452,20 +459,30 @@ void haspSetup(void) } #endif - haspPages.init(haspStartPage); - haspPages.load_jsonl(haspPagesPath); - haspPages.set(haspStartPage, LV_SCR_LOAD_ANIM_NONE); + hasp_init(); + hasp_load_json(); + haspPages.set(haspStartPage, LV_SCR_LOAD_ANIM_FADE_ON); } /********************** * STATIC FUNCTIONS **********************/ -void haspLoop(void) +IRAM_ATTR void haspLoop(void) { dispatchLoop(); } +void hasp_init(void) +{ + haspPages.init(haspStartPage); +} + +void hasp_load_json(void) +{ + haspPages.load_jsonl(haspPagesPath); +} + /* void hasp_background(uint16_t pageid, uint16_t imageid) { @@ -517,10 +534,10 @@ void hasp_background(uint16_t pageid, uint16_t imageid) /////////////////////////////////////////////////////////////////////////////////////////////////////////// -void haspGetVersion(char* version, size_t len) -{ - snprintf_P(version, len, PSTR("%u.%u.%u"), HASP_VER_MAJ, HASP_VER_MIN, HASP_VER_REV); -} +// void haspGetVersion(char* version, size_t len) +// { +// snprintf_P(version, len, PSTR("%u.%u.%u"), HASP_VER_MAJ, HASP_VER_MIN, HASP_VER_REV); +// } void haspClearPage(uint16_t pageid) { @@ -553,47 +570,80 @@ void haspSetPage(uint8_t pageid) } } -void haspLoadPage(const char* pagesfile) +void hasp_get_info(JsonDocument& doc) { -#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 - if(pagesfile[0] == '\0') return; + std::string buffer; + buffer.reserve(64); + char size_buf[32]; + JsonObject info = doc.createNestedObject(F(D_MANUFACTURER)); - if(!filesystemSetup()) { - LOG_ERROR(TAG_HASP, F("FS not mounted. Failed to load %s"), pagesfile); - return; + info[F(D_INFO_VERSION)] = haspDevice.get_version(); + + buffer = __DATE__; + buffer += (" "); + buffer += __TIME__; + buffer += (" UTC"); // Github buildservers are in UTC + info[F(D_INFO_BUILD_DATETIME)] = buffer; + + unsigned long time = millis() / 1000; + uint16_t day = time / 86400; + time = time % 86400; + uint8_t hour = time / 3600; + time = time % 3600; + uint8_t min = time / 60; + time = time % 60; + uint8_t sec = time; + + buffer.clear(); + if(day > 0) { + itoa(day, size_buf, DEC); + buffer += size_buf; + buffer += "d "; } - - if(!HASP_FS.exists(pagesfile)) { - LOG_ERROR(TAG_HASP, F("Non existing file %s"), pagesfile); - return; + if(day > 0 || hour > 0) { + itoa(hour, size_buf, DEC); + buffer += size_buf; + buffer += "h "; } + if(day > 0 || hour > 0 || min > 0) { + itoa(min, size_buf, DEC); + buffer += size_buf; + buffer += "m "; + } + itoa(sec, size_buf, DEC); + buffer += size_buf; + buffer += "s"; + info[F(D_INFO_UPTIME)] = buffer; - LOG_TRACE(TAG_HASP, F("Loading file %s"), pagesfile); + info = doc.createNestedObject(F(D_INFO_DEVICE_MEMORY)); + Parser::format_bytes(haspDevice.get_free_heap(), size_buf, sizeof(size_buf)); + info[F(D_INFO_FREE_HEAP)] = size_buf; + Parser::format_bytes(haspDevice.get_free_max_block(), size_buf, sizeof(size_buf)); + info[F(D_INFO_FREE_BLOCK)] = size_buf; + info[F(D_INFO_FRAGMENTATION)] = haspDevice.get_heap_fragmentation(); - File file = HASP_FS.open(pagesfile, "r"); - dispatch_parse_jsonl(file); - file.close(); - - LOG_INFO(TAG_HASP, F("File %s loaded"), pagesfile); -#else - -#if HASP_USE_EEPROM > 0 - LOG_TRACE(TAG_HASP, F("Loading jsonl from EEPROM...")); - EepromStream eepromStream(4096, 1024); - dispatch_parse_jsonl(eepromStream); - LOG_INFO(TAG_HASP, F("Loaded jsonl from EEPROM")); +#if ARDUINO_ARCH_ESP32 + if(psramFound()) { + Parser::format_bytes(ESP.getFreePsram(), size_buf, sizeof(size_buf)); + info[F(D_INFO_PSRAM_FREE)] = size_buf; + Parser::format_bytes(ESP.getPsramSize(), size_buf, sizeof(size_buf)); + info[F(D_INFO_PSRAM_SIZE)] = size_buf; + } #endif - std::ifstream ifs("pages.json", std::ifstream::in); - if(ifs) { - LOG_TRACE(TAG_HASP, F("Loading file %s"), pagesfile); - dispatch_parse_jsonl(ifs); - LOG_INFO(TAG_HASP, F("File %s loaded"), pagesfile); - } else { - LOG_ERROR(TAG_HASP, F("Non existing file %s"), pagesfile); - } + info = doc.createNestedObject(F(D_INFO_LVGL_MEMORY)); + lv_mem_monitor_t mem_mon; + lv_mem_monitor(&mem_mon); + Parser::format_bytes(mem_mon.total_size, size_buf, sizeof(size_buf)); + info[F(D_INFO_TOTAL_MEMORY)] = size_buf; + Parser::format_bytes(mem_mon.free_size, size_buf, sizeof(size_buf)); + info[F(D_INFO_FREE_MEMORY)] = size_buf; + info[F(D_INFO_FRAGMENTATION)] = mem_mon.frag_pct; -#endif + info = doc.createNestedObject(F("HASP State")); + hasp_get_sleep_state(size_buf); + info[F("Idle")] = size_buf; + info[F("Active Page")] = haspPages.get(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/hasp/hasp.h b/src/hasp/hasp.h index 76b57448..1a76532f 100644 --- a/src/hasp/hasp.h +++ b/src/hasp/hasp.h @@ -8,10 +8,7 @@ #include "Arduino.h" #endif -#include "lvgl.h" -#include "hasp_conf.h" - -#include "hasp_conf.h" +#include "hasplib.h" #if HASP_USE_DEBUG > 0 #include "../hasp_debug.h" @@ -47,13 +44,12 @@ extern "C" { * Create a hasp application */ void haspSetup(void); -void haspLoop(void); +IRAM_ATTR void haspLoop(void); void haspEverySecond(void); void haspReconnect(void); void haspDisconnect(void); -void haspGetVersion(char* version, size_t len); // void haspBackground(uint16_t pageid, uint16_t imageid); // void haspNewObject(const JsonObject & config, uint8_t & saved_page_id); @@ -67,12 +63,18 @@ bool haspSetConfig(const JsonObject& settings); lv_font_t* hasp_get_font(uint8_t fontid); -bool hasp_update_sleep_state(); +HASP_ATTRIBUTE_FAST_MEM bool hasp_update_sleep_state(); +void hasp_get_sleep_state(char* payload); void hasp_get_sleep_time(uint16_t& short_time, uint16_t& long_time); void hasp_set_sleep_time(uint16_t short_time, uint16_t long_time); void hasp_enable_wakeup_touch(); void hasp_disable_wakeup_touch(); +void hasp_init(void); +void hasp_load_json(void); + +void hasp_get_info(JsonDocument& info); + /********************** * MACROS **********************/ diff --git a/src/hasp/hasp_attribute.cpp b/src/hasp/hasp_attribute.cpp index 674a743a..4010eff6 100644 --- a/src/hasp/hasp_attribute.cpp +++ b/src/hasp/hasp_attribute.cpp @@ -5,281 +5,12 @@ #include "ArduinoLog.h" #endif -#include "lvgl.h" -#if LVGL_VERSION_MAJOR != 7 -#include "../lv_components.h" -#endif - #include "hasplib.h" +#include "hasp_attribute_helper.h" LV_FONT_DECLARE(unscii_8_icon); extern const char** btnmatrix_default_map; // memory pointer to lvgl default btnmatrix map -#if 0 -static bool attribute_lookup_lv_property(uint16_t hash, uint8_t * prop) -{ - struct prop_hash_map - { - uint16_t hash; - uint8_t prop; - }; - - /* in order of prevalence */ - prop_hash_map props[] = { - {ATTR_PAD_TOP, LV_STYLE_PAD_TOP & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_WIDTH, LV_STYLE_BORDER_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_OUTLINE_WIDTH, LV_STYLE_OUTLINE_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_LETTER_SPACE, LV_STYLE_VALUE_LETTER_SPACE & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_LETTER_SPACE, LV_STYLE_TEXT_LETTER_SPACE & LV_STYLE_PROP_ALL}, - {ATTR_LINE_WIDTH, LV_STYLE_LINE_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_TIME, LV_STYLE_TRANSITION_TIME & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_WIDTH, LV_STYLE_SCALE_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_RADIUS, LV_STYLE_RADIUS & LV_STYLE_PROP_ALL}, - {ATTR_PAD_BOTTOM, LV_STYLE_PAD_BOTTOM & LV_STYLE_PROP_ALL}, - {ATTR_BG_MAIN_STOP, LV_STYLE_BG_MAIN_STOP & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_SIDE, LV_STYLE_BORDER_SIDE & LV_STYLE_PROP_ALL}, - {ATTR_OUTLINE_PAD, LV_STYLE_OUTLINE_PAD & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_REPEAT, LV_STYLE_PATTERN_REPEAT & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_LINE_SPACE, LV_STYLE_VALUE_LINE_SPACE & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_LINE_SPACE, LV_STYLE_TEXT_LINE_SPACE & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_DELAY, LV_STYLE_TRANSITION_DELAY & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_BORDER_WIDTH, LV_STYLE_SCALE_BORDER_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_CLIP_CORNER, LV_STYLE_CLIP_CORNER & LV_STYLE_PROP_ALL}, - {ATTR_PAD_LEFT, LV_STYLE_PAD_LEFT & LV_STYLE_PROP_ALL}, - {ATTR_BG_GRAD_STOP, LV_STYLE_BG_GRAD_STOP & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_DECOR, LV_STYLE_TEXT_DECOR & LV_STYLE_PROP_ALL}, - {ATTR_LINE_DASH_WIDTH, LV_STYLE_LINE_DASH_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_1, LV_STYLE_TRANSITION_PROP_1 & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_END_BORDER_WIDTH, LV_STYLE_SCALE_END_BORDER_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_SIZE, LV_STYLE_SIZE & LV_STYLE_PROP_ALL}, - {ATTR_PAD_RIGHT, LV_STYLE_PAD_RIGHT & LV_STYLE_PROP_ALL}, - {ATTR_BG_GRAD_DIR, LV_STYLE_BG_GRAD_DIR & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_POST, LV_STYLE_BORDER_POST & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_OFS_X, LV_STYLE_VALUE_OFS_X & LV_STYLE_PROP_ALL}, - {ATTR_LINE_DASH_GAP, LV_STYLE_LINE_DASH_GAP & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_2, LV_STYLE_TRANSITION_PROP_2 & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_END_LINE_WIDTH, LV_STYLE_SCALE_END_LINE_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_PAD_INNER, LV_STYLE_PAD_INNER & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_OFS_Y, LV_STYLE_VALUE_OFS_Y & LV_STYLE_PROP_ALL}, - {ATTR_LINE_ROUNDED, LV_STYLE_LINE_ROUNDED & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_3, LV_STYLE_TRANSITION_PROP_3 & LV_STYLE_PROP_ALL}, - {ATTR_TRANSFORM_HEIGHT, LV_STYLE_TRANSFORM_HEIGHT & LV_STYLE_PROP_ALL}, - // {ATTR_MARGIN_TOP, LV_STYLE_MARGIN_TOP & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_ALIGN, LV_STYLE_VALUE_ALIGN & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_4, LV_STYLE_TRANSITION_PROP_4 & LV_STYLE_PROP_ALL}, - // {ATTR_TRANSFORM_ANGLE, LV_STYLE_TRANSFORM_ANGLE & LV_STYLE_PROP_ALL}, - // {ATTR_MARGIN_BOTTOM, LV_STYLE_MARGIN_BOTTOM & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_5, LV_STYLE_TRANSITION_PROP_5 & LV_STYLE_PROP_ALL}, - // {ATTR_TRANSFORM_ZOOM, LV_STYLE_TRANSFORM_ZOOM & LV_STYLE_PROP_ALL}, - // {ATTR_MARGIN_LEFT, LV_STYLE_MARGIN_LEFT & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PROP_6, LV_STYLE_TRANSITION_PROP_6 & LV_STYLE_PROP_ALL}, - // {ATTR_MARGIN_RIGHT, LV_STYLE_MARGIN_RIGHT & LV_STYLE_PROP_ALL}, - {ATTR_BG_COLOR, LV_STYLE_BG_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_COLOR, LV_STYLE_BORDER_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_OUTLINE_COLOR, LV_STYLE_OUTLINE_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_RECOLOR, LV_STYLE_PATTERN_RECOLOR & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_COLOR, LV_STYLE_VALUE_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_COLOR, LV_STYLE_TEXT_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_LINE_COLOR, LV_STYLE_LINE_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_IMAGE_RECOLOR, LV_STYLE_IMAGE_RECOLOR & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_GRAD_COLOR, LV_STYLE_SCALE_GRAD_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_BG_GRAD_COLOR, LV_STYLE_BG_GRAD_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_SEL_COLOR, LV_STYLE_TEXT_SEL_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_SCALE_END_COLOR, LV_STYLE_SCALE_END_COLOR & LV_STYLE_PROP_ALL}, - // {ATTR_TEXT_SEL_BG_COLOR, LV_STYLE_TEXT_SEL_BG_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_OPA_SCALE, LV_STYLE_OPA_SCALE & LV_STYLE_PROP_ALL}, - {ATTR_BG_OPA, LV_STYLE_BG_OPA & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_OPA, LV_STYLE_BORDER_OPA & LV_STYLE_PROP_ALL}, - {ATTR_OUTLINE_OPA, LV_STYLE_OUTLINE_OPA & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_OPA, LV_STYLE_PATTERN_OPA & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_OPA, LV_STYLE_VALUE_OPA & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_OPA, LV_STYLE_TEXT_OPA & LV_STYLE_PROP_ALL}, - {ATTR_LINE_OPA, LV_STYLE_LINE_OPA & LV_STYLE_PROP_ALL}, - {ATTR_IMAGE_OPA, LV_STYLE_IMAGE_OPA & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_RECOLOR_OPA, LV_STYLE_PATTERN_RECOLOR_OPA & LV_STYLE_PROP_ALL}, - {ATTR_IMAGE_RECOLOR_OPA, LV_STYLE_IMAGE_RECOLOR_OPA & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_IMAGE, LV_STYLE_PATTERN_IMAGE & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_FONT, LV_STYLE_VALUE_FONT & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_FONT, LV_STYLE_TEXT_FONT & LV_STYLE_PROP_ALL}, - {ATTR_TRANSITION_PATH, LV_STYLE_TRANSITION_PATH & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_STR, LV_STYLE_VALUE_STR & LV_STYLE_PROP_ALL}, - -#if LV_USE_SHADOW - {ATTR_SHADOW_WIDTH, LV_STYLE_SHADOW_WIDTH & LV_STYLE_PROP_ALL}, - {ATTR_SHADOW_OFS_X, LV_STYLE_SHADOW_OFS_X & LV_STYLE_PROP_ALL}, - {ATTR_SHADOW_OFS_Y, LV_STYLE_SHADOW_OFS_Y & LV_STYLE_PROP_ALL}, - {ATTR_SHADOW_SPREAD, LV_STYLE_SHADOW_SPREAD & LV_STYLE_PROP_ALL}, - {ATTR_SHADOW_COLOR, LV_STYLE_SHADOW_COLOR & LV_STYLE_PROP_ALL}, - {ATTR_SHADOW_OPA, LV_STYLE_SHADOW_OPA & LV_STYLE_PROP_ALL}, -#endif - -#if LV_USE_BLEND_MODES && LV_USE_SHADOW - {ATTR_SHADOW_BLEND_MODE, LV_STYLE_SHADOW_BLEND_MODE & LV_STYLE_PROP_ALL}, -#endif - -#if LV_USE_BLEND_MODES - {ATTR_BG_BLEND_MODE, LV_STYLE_BG_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_PATTERN_BLEND_MODE, LV_STYLE_PATTERN_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_IMAGE_BLEND_MODE, LV_STYLE_IMAGE_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_LINE_BLEND_MODE, LV_STYLE_LINE_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_BORDER_BLEND_MODE, LV_STYLE_BORDER_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_OUTLINE_BLEND_MODE, LV_STYLE_OUTLINE_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_VALUE_BLEND_MODE, LV_STYLE_VALUE_BLEND_MODE & LV_STYLE_PROP_ALL}, - {ATTR_TEXT_BLEND_MODE, LV_STYLE_TEXT_BLEND_MODE & LV_STYLE_PROP_ALL}, -#endif - }; - - for(uint32_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { - if(props[i].hash == hash) { - *prop = props[1].prop; - LOG_WARNING(TAG_ATTR, F("%d found and has propery %d"), hash, props[i].prop); - return true; - } - } - LOG_ERROR(TAG_ATTR, F("%d has no property id"), hash); - return false; -} - -static bool attribute_get_lv_property() -{ - lv_res_t res _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, void * res); - return res == LV_RES_OK -} - -static bool attribute_set_lv_property() -{ - lv_res_t res _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, void * res); - return res == LV_RES_OK -} - -static bool attribute_update_lv_property(lv_obj_t * obj, const char * attr_p, uint16_t attr_hash, const char * payload, - bool update) -{ - uint8_t prop; - uint8_t prop_type; - - // convert sdbm hash to lv property number - if(!attribute_lookup_lv_property(attr_hash, &prop)) return false; - - // find the parameter type for this property - prop_type = prop & 0xF; - - if(prop_type < LV_STYLE_ID_COLOR) { - if(update) { - _lv_obj_set_style_local_int(obj, part, prop | (state << LV_STYLE_STATE_POS), atoi(payload)) - } else { - attr_out_str(obj, attr_p, lv_obj_get_style_value_str(obj, part)); - } - } else if(prop_type < LV_STYLE_ID_OPA) { - } else if(prop_type < LV_STYLE_ID_PTR) { - } else { - } -} -#endif - -// OK - this function is missing in lvgl -static uint8_t my_roller_get_visible_row_count(lv_obj_t* roller) -{ - const lv_font_t* font = lv_obj_get_style_text_font(roller, LV_ROLLER_PART_BG); - lv_style_int_t line_space = lv_obj_get_style_text_line_space(roller, LV_ROLLER_PART_BG); - lv_coord_t h = lv_obj_get_height(roller); - - if((lv_font_get_line_height(font) + line_space) != 0) - return (uint8_t)(h / (lv_font_get_line_height(font) + line_space)); - else - return 0; -} - -// OK - this function is missing in lvgl -static inline int16_t my_arc_get_rotation(lv_obj_t* arc) -{ - lv_arc_ext_t* ext = (lv_arc_ext_t*)lv_obj_get_ext_attr(arc); - return ext->rotation_angle; -} - -// OK - this function is missing in lvgl -static inline int16_t my_chart_get_min_value(lv_obj_t* chart) -{ - lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); - return ext->ymin[LV_CHART_AXIS_PRIMARY_Y]; -} - -// OK - this function is missing in lvgl -static inline int16_t my_chart_get_max_value(lv_obj_t* chart) -{ - lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); - return ext->ymax[LV_CHART_AXIS_PRIMARY_Y]; -} - -lv_chart_series_t* my_chart_get_series(lv_obj_t* chart, uint8_t ser_num) -{ - lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); - lv_chart_series_t* ser = (lv_chart_series_t*)_lv_ll_get_tail(&ext->series_ll); - while(ser_num > 0 && ser) { - ser = (lv_chart_series_t*)_lv_ll_get_prev(&ext->series_ll, ser); - ser_num--; - } - return ser; -} - -/** - * Set a new value_str for an object. Memory will be allocated to store the text by the object. - * @param obj pointer to a object - * @param text '\0' terminated character string. NULL to refresh with the current text. - */ -void my_obj_set_value_str_txt(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text) -{ - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - - void* value_str_p = (void*)lv_obj_get_style_value_str(obj, part); - lv_obj_invalidate(obj); - - if(text == NULL || text[0] == 0) { - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - lv_obj_set_style_local_value_str(obj, part, state, NULL); - lv_mem_free(value_str_p); - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - return; - } - - LV_ASSERT_STR(text); - - if(value_str_p == NULL) { - /*Get the size of the text*/ - size_t len = strlen(text) + 1; - - /*Allocate space for the new text*/ - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - value_str_p = (char*)lv_mem_alloc(len); - LV_ASSERT_MEM(value_str_p); - if(value_str_p == NULL) return; - - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - strncpy((char*)value_str_p, text, len); - lv_obj_set_style_local_value_str(obj, part, state, (char*)value_str_p); - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - return; - } - - if(value_str_p == text) { - /*If set its own text then reallocate it (maybe its size changed)*/ - LOG_DEBUG(TAG_ATTR, "%s %d", __FILE__, __LINE__); - return; // don't touch the data - } - - /*Get the size of the text*/ - size_t len = strlen(text) + 1; - - /*Allocate space for the new text*/ - value_str_p = lv_mem_realloc(value_str_p, len); - LV_ASSERT_MEM(value_str_p); - if(value_str_p != NULL) strcpy((char*)value_str_p, text); - lv_obj_set_style_local_value_str(obj, part, state, (char*)value_str_p); - - // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); -} - void my_btnmatrix_map_clear(lv_obj_t* obj) { lv_btnmatrix_ext_t* ext = (lv_btnmatrix_ext_t*)lv_obj_get_ext_attr(obj); @@ -305,11 +36,15 @@ void my_btnmatrix_map_clear(lv_obj_t* obj) } } -static void my_btnmatrix_map_create(lv_obj_t* obj, const char* payload) +void my_msgbox_map_clear(lv_obj_t* obj) { - const char** map_p = lv_btnmatrix_get_map_array(obj); + lv_msgbox_ext_t* ext = (lv_msgbox_ext_t*)lv_obj_get_ext_attr(obj); + if(ext && ext->btnm) my_btnmatrix_map_clear(ext->btnm); // Clear the button map if it exists yet +} - // Create new map +// Create new btnmatrix button map from json array +const char** my_map_create(const char* payload) +{ // Reserve memory for JsonDocument size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 256; DynamicJsonDocument map_doc(maxsize); @@ -317,7 +52,7 @@ static void my_btnmatrix_map_create(lv_obj_t* obj, const char* payload) if(jsonError) { // Couldn't parse incoming JSON payload dispatch_json_error(TAG_ATTR, jsonError); - return; + return NULL; } JsonArray arr = map_doc.as(); // Parse payload @@ -326,14 +61,13 @@ static void my_btnmatrix_map_create(lv_obj_t* obj, const char* payload) const char** map_data_str = (const char**)lv_mem_alloc(tot_len); if(map_data_str == NULL) { LOG_ERROR(TAG_ATTR, F("Out of memory while creating button map")); - return; + return NULL; } memset(map_data_str, 0, tot_len); // Create buffer tot_len = 0; for(JsonVariant btn : arr) { - // tot_len += btn.as().length() + 1; tot_len += strlen(btn.as()) + 1; } tot_len++; // trailing '\0' @@ -343,13 +77,13 @@ static void my_btnmatrix_map_create(lv_obj_t* obj, const char* payload) if(buffer_addr == NULL) { lv_mem_free(map_data_str); LOG_ERROR(TAG_ATTR, F("Out of memory while creating button map")); - return; + return NULL; } memset(buffer_addr, 0, tot_len); // Important, last index needs to be 0 => empty string "" /* Point of no return, destroy & free the previous map */ LOG_VERBOSE(TAG_ATTR, F("%s %d map addr: %x"), __FILE__, __LINE__, map_data_str); - my_btnmatrix_map_clear(obj); // Free previous map + // my_btnmatrix_map_clear(obj); // Free previous map // Fill buffer size_t index = 0; @@ -370,7 +104,32 @@ static void my_btnmatrix_map_create(lv_obj_t* obj, const char* payload) map_data_str[index] = buffer_addr + pos; // save pointer to the last \0 byte LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); - lv_btnmatrix_set_map(obj, map_data_str); + return map_data_str; +} + +static void my_btnmatrix_set_map(lv_obj_t* obj, const char* payload) +{ + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + const char** map = my_map_create(payload); + if(!map) return; + + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + my_btnmatrix_map_clear(obj); // Free previous map + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + lv_btnmatrix_set_map(obj, map); + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); +} + +static void my_msgbox_set_map(lv_obj_t* obj, const char* payload) +{ + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + const char** map = my_map_create(payload); + if(!map) return; + + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + my_msgbox_map_clear(obj); // Free previous map + LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + lv_msgbox_add_btns(obj, map); LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); } @@ -423,16 +182,6 @@ static void line_set_points(lv_obj_t* obj, const char* payload) // TO DO : free & destroy previous pointlist! } -// OK -static inline lv_color_t haspLogColor(lv_color_t color) -{ - // uint8_t r = (LV_COLOR_GET_R(color) * 263 + 7) >> 5; - // uint8_t g = (LV_COLOR_GET_G(color) * 259 + 3) >> 6; - // uint8_t b = (LV_COLOR_GET_B(color) * 263 + 7) >> 5; - // LOG_VERBOSE(TAG_ATTR,F("Color: R%u G%u B%u"), r, g, b); - return color; -} - static lv_font_t* haspPayloadToFont(const char* payload) { uint8_t var = atoi(payload); @@ -477,26 +226,6 @@ static lv_font_t* haspPayloadToFont(const char* payload) } } -static void gauge_format_10(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) -{ - snprintf(buf, bufsize, PSTR("%d"), value / 10); -} - -static void gauge_format_100(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) -{ - snprintf(buf, bufsize, PSTR("%d"), value / 100); -} - -static void gauge_format_1k(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) -{ - snprintf(buf, bufsize, PSTR("%d"), value / 1000); -} - -static void gauge_format_10k(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) -{ - snprintf(buf, bufsize, PSTR("%d"), value / 10000); -} - static void hasp_process_label_long_mode(lv_obj_t* obj, const char* payload, bool update) { if(update) { @@ -524,77 +253,6 @@ static void hasp_process_label_long_mode(lv_obj_t* obj, const char* payload, boo } } -// OK -lv_obj_t* FindButtonLabel(lv_obj_t* btn) -{ - if(btn) { - lv_obj_t* label = lv_obj_get_child_back(btn, NULL); -#if 1 - if(label) { - if(check_obj_type(label, LV_HASP_LABEL)) { - return label; - } -#else - if(label) { - lv_obj_type_t list; - lv_obj_get_type(label, &list); - const char* objtype = list.type[0]; - - if(check_obj_type(objtype, LV_HASP_LABEL)) { - return label; - } -#endif - - } else { - LOG_ERROR(TAG_ATTR, F("FindButtonLabel NULL Pointer encountered")); - } - } else { - LOG_WARNING(TAG_ATTR, F("Button not defined")); - } - return NULL; -} - -// OK -static inline void haspSetLabelText(lv_obj_t* obj, const char* value) -{ - lv_obj_t* label = FindButtonLabel(obj); - if(label) { - lv_label_set_text(label, value); - } -} - -// OK -static bool haspGetLabelText(lv_obj_t* obj, char** text) -{ - if(!obj) { - LOG_WARNING(TAG_ATTR, F("Button not defined")); - return false; - } - - lv_obj_t* label = lv_obj_get_child_back(obj, NULL); - if(label) { -#if 1 - if(check_obj_type(label, LV_HASP_LABEL)) { - *text = lv_label_get_text(label); - return true; - } -#else - lv_obj_type_t list; - lv_obj_get_type(label, &list); - - if(check_obj_type(list.type[0], LV_HASP_LABEL)) { - text = lv_label_get_text(label); - return true; - } -#endif - - } else { - LOG_WARNING(TAG_ATTR, F("haspGetLabelText NULL Pointer encountered")); - } - - return false; -} - static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, char* attr_out, uint8_t& part, uint8_t& state) { @@ -620,7 +278,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch // lv_obj_get_type(obj, &list); // const char * objtype = list.type[0]; - if(check_obj_type(obj, LV_HASP_BUTTON)) { + if(obj_check_type(obj, LV_HASP_BUTTON)) { switch(index) { case 1: state = LV_BTN_STATE_PRESSED; @@ -647,16 +305,17 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch #if(LV_SLIDER_PART_INDIC != LV_SWITCH_PART_INDIC) || (LV_SLIDER_PART_KNOB != LV_SWITCH_PART_KNOB) || \ (LV_SLIDER_PART_BG != LV_SWITCH_PART_BG) || (LV_SLIDER_PART_INDIC != LV_ARC_PART_INDIC) || \ (LV_SLIDER_PART_KNOB != LV_ARC_PART_KNOB) || (LV_SLIDER_PART_BG != LV_ARC_PART_BG) || \ + (LV_SLIDER_PART_INDIC != LV_SPINNER_PART_INDIC) || (LV_SLIDER_PART_BG != LV_SPINNER_PART_BG) || \ (LV_SLIDER_PART_INDIC != LV_BAR_PART_INDIC) || (LV_SLIDER_PART_BG != LV_BAR_PART_BG) -#error "LV_SLIDER, LV_BAR, LV_ARC, LV_SWITCH parts should match!" +#error "LV_SLIDER, LV_BAR, LV_ARC, LV_SPINNER, LV_SWITCH parts should match!" #endif - if(check_obj_type(obj, LV_HASP_SLIDER) || check_obj_type(obj, LV_HASP_SWITCH) || check_obj_type(obj, LV_HASP_ARC) || - check_obj_type(obj, LV_HASP_BAR)) { + if(obj_check_type(obj, LV_HASP_SLIDER) || obj_check_type(obj, LV_HASP_SWITCH) || obj_check_type(obj, LV_HASP_ARC) || + obj_check_type(obj, LV_HASP_BAR) || obj_check_type(obj, LV_HASP_SPINNER)) { if(index == 1) { part = LV_SLIDER_PART_INDIC; } else if(index == 2) { - if(!check_obj_type(obj, LV_HASP_BAR)) part = LV_SLIDER_PART_KNOB; + if(!obj_check_type(obj, LV_HASP_BAR) && !obj_check_type(obj, LV_HASP_SPINNER)) part = LV_SLIDER_PART_KNOB; } else { part = LV_SLIDER_PART_BG; } @@ -664,7 +323,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch return; } - if(check_obj_type(obj, LV_HASP_CHECKBOX)) { + if(obj_check_type(obj, LV_HASP_CHECKBOX)) { if(index == 1) { part = LV_CHECKBOX_PART_BULLET; } else { @@ -674,7 +333,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch return; } - if(check_obj_type(obj, LV_HASP_CPICKER)) { + if(obj_check_type(obj, LV_HASP_CPICKER)) { if(index == 1) { part = LV_CPICKER_PART_KNOB; } else { @@ -684,7 +343,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch return; } - if(check_obj_type(obj, LV_HASP_ROLLER)) { + if(obj_check_type(obj, LV_HASP_ROLLER)) { if(index == 1) { part = LV_ROLLER_PART_SELECTED; } else { @@ -694,7 +353,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch return; } - // if(check_obj_type(obj, LV_HASP_LMETER)) { + // if(obj_check_type(obj, LV_HASP_LINEMETER)) { // state = LV_STATE_DEFAULT; // return; // } @@ -710,7 +369,7 @@ static void hasp_attribute_get_part_state(lv_obj_t* obj, const char* attr_in, ch * @note setting a value won't return anything, getting will dispatch the value */ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, const char* payload, - bool update) + bool update, bool& result) { char attr[32]; uint8_t part = LV_OBJ_PART_MAIN; @@ -726,6 +385,8 @@ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t at * when using hasp_out use attr_p for the original attribute name * *************************************************************** */ + result = true; // default return + switch(attr_hash) { /* 1: Use other blend modes than normal (`LV_BLEND_MODE_...`)*/ @@ -882,12 +543,12 @@ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t at if(font) { LOG_WARNING(TAG_ATTR, "%s %d %x", __FILE__, __LINE__, *font); uint8_t count = 3; - if(check_obj_type(obj, LV_HASP_ROLLER)) count = my_roller_get_visible_row_count(obj); + if(obj_check_type(obj, LV_HASP_ROLLER)) count = my_roller_get_visible_row_count(obj); lv_obj_set_style_local_text_font(obj, part, state, font); - if(check_obj_type(obj, LV_HASP_ROLLER)) lv_roller_set_visible_row_count(obj, count); + if(obj_check_type(obj, LV_HASP_ROLLER)) lv_roller_set_visible_row_count(obj, count); lv_obj_set_style_local_text_font(obj, part, state, font); // again, for roller - if(check_obj_type(obj, LV_HASP_DROPDOWN)) { // issue #43 + if(obj_check_type(obj, LV_HASP_DROPDOWN)) { // issue #43 lv_obj_set_style_local_text_font(obj, LV_DROPDOWN_PART_MAIN, state, font); lv_obj_set_style_local_text_font(obj, LV_DROPDOWN_PART_LIST, state, font); lv_obj_set_style_local_text_font(obj, LV_DROPDOWN_PART_SELECTED, state, font); @@ -1001,7 +662,7 @@ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t at return attribute_value_opa(obj, part, state, update, attr_p, (lv_opa_t)var); case ATTR_VALUE_STR: { if(update) { - my_obj_set_value_str_txt(obj, part, state, payload); + my_obj_set_value_str_text(obj, part, state, payload); } else { attr_out_str(obj, attr, lv_obj_get_style_value_str(obj, part)); } @@ -1035,9 +696,6 @@ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t at return attribute_pattern_opa(obj, part, state, update, attr_p, (lv_opa_t)var); case ATTR_PATTERN_RECOLOR_OPA: return attribute_pattern_recolor_opa(obj, part, state, update, attr_p, (lv_opa_t)var); - case ATTR_PATTERN_IMAGE: - // return lv_obj_set_style_local_pattern_image(obj, part, state, (constvoid *)var); - break; case ATTR_PATTERN_RECOLOR: { if(update) { lv_color32_t c; @@ -1050,161 +708,156 @@ static void hasp_local_style_attr(lv_obj_t* obj, const char* attr_p, uint16_t at return; } + case ATTR_PATTERN_IMAGE: + // return lv_obj_set_style_local_pattern_image(obj, part, state, (constvoid *)var); + // return; + /* Image attributes */ - // Todo + case ATTR_IMAGE_RECOLOR_OPA: + return attribute_image_recolor_opa(obj, part, state, update, attr_p, (lv_opa_t)var); + case ATTR_IMAGE_OPA: + return attribute_image_opa(obj, part, state, update, attr_p, (lv_opa_t)var); + case ATTR_IMAGE_RECOLOR: { + if(update) { + lv_color32_t c; + if(Parser::haspPayloadToColor(payload, c)) + lv_obj_set_style_local_image_recolor(obj, part, state, + lv_color_make(c.ch.red, c.ch.green, c.ch.blue)); + } else { + attr_out_color(obj, attr, lv_obj_get_style_image_recolor(obj, part)); + } + return; + } /* Transition attributes */ // Todo + + default: + break; } + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN " (%d)"), attr_p, attr_hash); + result = false; } -static void hasp_process_arc_attribute(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, const char* payload, - bool update) +static hasp_attribute_type_t hasp_process_arc_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { // We already know it's a arc object - int16_t intval = atoi(payload); - uint16_t val = atoi(payload); - - char* attr = (char*)attr_p; - if(*attr == '.') attr++; // strip leading '.' - switch(attr_hash) { case ATTR_TYPE: - return (update) ? lv_arc_set_type(obj, val % 3) : attr_out_int(obj, attr, lv_arc_get_type(obj)); + if(update) + lv_arc_set_type(obj, val % 3); + else + val = lv_arc_get_type(obj); + break; - case ATTR_ROTATION: - return (update) ? lv_arc_set_rotation(obj, val) : attr_out_int(obj, attr, my_arc_get_rotation(obj)); - - case ATTR_ADJUSTABLE: - if(update) { - bool toggle = Parser::is_true(payload); - lv_arc_set_adjustable(obj, toggle); - lv_obj_set_event_cb(obj, toggle ? slider_event_handler : generic_event_handler); - } else { - attr_out_int(obj, attr, lv_arc_get_adjustable(obj)); - } - return; - - case ATTR_START_ANGLE: - return (update) ? lv_arc_set_bg_start_angle(obj, val) - : attr_out_int(obj, attr, lv_arc_get_bg_angle_start(obj)); - - case ATTR_END_ANGLE: - return (update) ? lv_arc_set_bg_end_angle(obj, val) : attr_out_int(obj, attr, lv_arc_get_bg_angle_end(obj)); - - case ATTR_START_ANGLE1: - return (update) ? lv_arc_set_start_angle(obj, val) : attr_out_int(obj, attr, lv_arc_get_angle_start(obj)); - - case ATTR_END_ANGLE1: - return (update) ? lv_arc_set_end_angle(obj, val) : attr_out_int(obj, attr, lv_arc_get_angle_end(obj)); + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; } - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr_p); + return hasp_attribute_type_t::ATTR_TYPE_INT; } -static void hasp_process_lmeter_attribute(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, const char* payload, - bool update) +static hasp_attribute_type_t hasp_process_lmeter_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { // We already know it's a linemeter object - int16_t intval = atoi(payload); - uint16_t val = atoi(payload); - uint16_t line_count = lv_linemeter_get_line_count(obj); uint16_t angle = lv_linemeter_get_scale_angle(obj); - char* attr = (char*)attr_p; - if(*attr == '.') attr++; // strip leading '.' - switch(attr_hash) { case ATTR_TYPE: - return (update) ? lv_linemeter_set_mirror(obj, val != 0) - : attr_out_int(obj, attr, lv_linemeter_get_mirror(obj)); - - case ATTR_ROTATION: - return (update) ? lv_linemeter_set_angle_offset(obj, val) - : attr_out_int(obj, attr, lv_linemeter_get_angle_offset(obj)); + if(update) + lv_linemeter_set_mirror(obj, !!val); + else + val = lv_linemeter_get_mirror(obj); + break; case ATTR_LINE_COUNT: - return (update) ? lv_linemeter_set_scale(obj, angle, val) : attr_out_int(obj, attr, line_count); + if(update) + lv_linemeter_set_scale(obj, angle, val); + else + val = line_count; + break; case ATTR_ANGLE: - return (update) ? lv_linemeter_set_scale(obj, val, line_count) : attr_out_int(obj, attr, angle); + if(update) + lv_linemeter_set_scale(obj, val, line_count); + else + val = angle; + break; + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; } - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr_p); + return hasp_attribute_type_t::ATTR_TYPE_INT; } -static void hasp_process_dropdown_attribute(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, const char* payload, - bool update) +static hasp_attribute_type_t special_attribute_direction(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { - // We already know it's a gauge object - int16_t intval = atoi(payload); - uint16_t val = atoi(payload); + switch(obj_get_type(obj)) { - char* attr = (char*)attr_p; - if(*attr == '.') attr++; // strip leading '.' + case LV_HASP_DROPDOWN: + if(update) + lv_dropdown_set_dir(obj, (lv_dropdown_dir_t)val); + else + val = lv_dropdown_get_dir(obj); + break; - switch(attr_hash) { - case ATTR_DIRECTION: - return (update) ? lv_dropdown_set_dir(obj, intval) : attr_out_int(obj, attr, lv_dropdown_get_dir(obj)); + case LV_HASP_SPINNER: + if(update) + lv_spinner_set_dir(obj, (lv_spinner_dir_t)val); + else + val = lv_spinner_get_dir(obj); + break; - case ATTR_SYMBOL: - return (update) ? lv_dropdown_set_symbol(obj, payload) - : attr_out_str(obj, attr, lv_dropdown_get_symbol(obj)); - - case ATTR_OPEN: - return lv_dropdown_open(obj); - - case ATTR_CLOSE: - return lv_dropdown_close(obj); - - case ATTR_MAX_HEIGHT: - return (update) ? lv_dropdown_set_max_height(obj, intval) - : attr_out_int(obj, attr, lv_dropdown_get_max_height(obj)); - - case ATTR_SHOW_SELECTED: - return (update) ? lv_dropdown_set_show_selected(obj, val) - : attr_out_int(obj, attr, lv_dropdown_get_show_selected(obj)); + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; } - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr_p); + return hasp_attribute_type_t::ATTR_TYPE_INT; } -static void hasp_process_gauge_attribute(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, const char* payload, - bool update) +static hasp_attribute_type_t hasp_process_gauge_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { // We already know it's a gauge object - int16_t intval = atoi(payload); - uint16_t val = atoi(payload); uint8_t label_count = lv_gauge_get_label_count(obj); uint16_t line_count = lv_gauge_get_line_count(obj); uint16_t angle = lv_gauge_get_scale_angle(obj); - char* attr = (char*)attr_p; - if(*attr == '.') attr++; // strip leading '.' - switch(attr_hash) { case ATTR_CRITICAL_VALUE: - return (update) ? lv_gauge_set_critical_value(obj, intval) - : attr_out_int(obj, attr, lv_gauge_get_critical_value(obj)); + if(update) + lv_gauge_set_critical_value(obj, val); + else + val = lv_gauge_get_critical_value(obj); + break; case ATTR_ANGLE: - return (update) ? lv_gauge_set_scale(obj, val, line_count, label_count) : attr_out_int(obj, attr, angle); + if(update) + lv_gauge_set_scale(obj, val, line_count, label_count); + else + val = angle; + break; case ATTR_LINE_COUNT: - return (update) ? lv_gauge_set_scale(obj, angle, val, label_count) : attr_out_int(obj, attr, line_count); + if(update) + lv_gauge_set_scale(obj, angle, val, label_count); + else + val = line_count; + break; case ATTR_LABEL_COUNT: - return (update) ? lv_gauge_set_scale(obj, angle, line_count, val) : attr_out_int(obj, attr, label_count); - - case ATTR_ROTATION: - return (update) ? lv_gauge_set_angle_offset(obj, val) - : attr_out_int(obj, attr, lv_gauge_get_angle_offset(obj)); + if(update) + lv_gauge_set_scale(obj, angle, line_count, val); + else + val = label_count; + break; case ATTR_FORMAT: if(update) { + // TODO : getter needed switch(val) { case 1: lv_gauge_set_formatter_cb(obj, gauge_format_10); @@ -1222,186 +875,796 @@ static void hasp_process_gauge_attribute(lv_obj_t* obj, const char* attr_p, uint lv_gauge_set_formatter_cb(obj, NULL); } } - return; + break; + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; } - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr_p); + return hasp_attribute_type_t::ATTR_TYPE_INT; } // ##################### Common Attributes ######################################################## -static void hasp_process_page_attributes(lv_obj_t* obj, const char* attr_p, uint16_t attr_hash, uint8_t val, - bool update) +static hasp_attribute_type_t specific_page_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { + if(!obj_check_type(obj, LV_HASP_PAGE)) return hasp_attribute_type_t::ATTR_NOT_FOUND; + uint8_t pageid; if(haspPages.get_id(obj, &pageid)) { switch(attr_hash) { case ATTR_NEXT: - update ? haspPages.set_next(pageid, val) : attr_out_int(obj, attr_p, haspPages.get_next(pageid)); + if(update) + haspPages.set_next(pageid, (uint8_t)val); + else + val = haspPages.get_next(pageid); break; case ATTR_PREV: - update ? haspPages.set_prev(pageid, val) : attr_out_int(obj, attr_p, haspPages.get_prev(pageid)); + if(update) + haspPages.set_prev(pageid, (uint8_t)val); + else + val = haspPages.get_prev(pageid); + break; + + case ATTR_BACK: + if(update) + haspPages.set_back(pageid, (uint8_t)val); + else + val = haspPages.get_back(pageid); break; - // case ATTR_BACK: default: - update ? haspPages.set_back(pageid, val) : attr_out_int(obj, attr_p, haspPages.get_back(pageid)); - break; + return hasp_attribute_type_t::ATTR_NOT_FOUND; } } + + return hasp_attribute_type_t::ATTR_TYPE_INT; } -static void hasp_process_obj_attribute_txt(lv_obj_t* obj, const char* attr, const char* payload, bool update) +template +static inline bool do_attribute(T& list, lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) { - /* Attributes depending on objecttype */ - // lv_obj_type_t list; - // lv_obj_get_type(obj, &list); - // const char * objtype = list.type[0]; + uint8_t obj_type = obj_get_type(obj); + int count = sizeof(list) / sizeof(list[0]); - if(check_obj_type(obj, LV_HASP_BUTTON)) { - if(update) { - haspSetLabelText(obj, payload); + for(int i = 0; i < count; i++) { + if(obj_type == list[i].obj_type && attr_hash == list[i].hash) { + if(update) + list[i].set(obj, val); + else + val = list[i].get(obj); + return true; + } + } + + return false; +} + +static hasp_attribute_type_t special_attribute_src(lv_obj_t* obj, const char* payload, char** text, bool update) +{ + if(!obj_check_type(obj, LV_HASP_IMAGE)) return ATTR_NOT_FOUND; + + if(update) { + lv_img_cache_invalidate_src(lv_img_get_src(obj)); + lv_img_set_src(obj, payload); + } else { + switch(lv_img_src_get_type(obj)) { + case LV_IMG_SRC_FILE: + *text = (char*)lv_img_get_file_name(obj); + case LV_IMG_SRC_SYMBOL: + *text = (char*)lv_img_get_src(obj); + } + } + return ATTR_TYPE_STR; +} + +static hasp_attribute_type_t attribute_common_align(lv_obj_t* obj, const char* attr, const char* payload, char** text, + bool update) +{ + lv_label_align_t val; + + if(update) { + if(!strcasecmp_P(payload, PSTR("left"))) { + val = LV_LABEL_ALIGN_LEFT; + } else if(!strcasecmp_P(payload, PSTR("right"))) { + val = LV_LABEL_ALIGN_RIGHT; + } else if(!strcasecmp_P(payload, PSTR("center"))) { + val = LV_LABEL_ALIGN_CENTER; + } else if(!strcasecmp_P(payload, PSTR("auto"))) { + val = LV_LABEL_ALIGN_AUTO; } else { - char* text = NULL; - if(haspGetLabelText(obj, &text) && text) attr_out_str(obj, attr, text); + return ATTR_TYPE_ALIGN_INVALID; } - return; } - if(check_obj_type(obj, LV_HASP_LABEL)) { - return update ? lv_label_set_text(obj, payload) : attr_out_str(obj, attr, lv_label_get_text(obj)); - } - if(check_obj_type(obj, LV_HASP_CHECKBOX)) { - return update ? lv_checkbox_set_text(obj, payload) : attr_out_str(obj, attr, lv_checkbox_get_text(obj)); - } - if(check_obj_type(obj, LV_HASP_DROPDOWN)) { - char buffer[128]; - lv_dropdown_get_selected_str(obj, buffer, sizeof(buffer)); - return attr_out_str(obj, attr, buffer); - } - if(check_obj_type(obj, LV_HASP_ROLLER)) { - char buffer[128]; - lv_roller_get_selected_str(obj, buffer, sizeof(buffer)); - return attr_out_str(obj, attr, buffer); - } -#if LV_USE_WIN != 0 - if(check_obj_type(obj, LV_HASP_WINDOW)) { - // return update ? lv_win_set_title(obj, (const char *)payload) : attr_out_str(obj, attr, - // lv_win_get_title(obj)); - } -#endif - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr); + switch(obj_get_type(obj)) { + case LV_HASP_BUTTON: { + lv_obj_t* label = FindButtonLabel(obj); + if(label) { + if(update) + lv_label_set_align(label, val); + else + val = lv_label_get_align(label); + } else { + return ATTR_NOT_FOUND; // not found + } + break; + } + case LV_HASP_BTNMATRIX: + if(update) + lv_btnmatrix_set_align(obj, val); + else + val = lv_btnmatrix_get_align(obj); + break; + + case LV_HASP_LABEL: + if(update) + lv_label_set_align(obj, val); + else + val = lv_label_get_align(obj); + break; + + case LV_HASP_ROLLER: + if(update) + lv_roller_set_align(obj, val); + else + val = lv_roller_get_align(obj); + break; + + default: + return ATTR_NOT_FOUND; // not found + } + + // return values + switch(val) { + case LV_LABEL_ALIGN_AUTO: + strcpy_P(*text, PSTR("auto")); + break; + case LV_LABEL_ALIGN_CENTER: + strcpy_P(*text, PSTR("center")); + break; + case LV_LABEL_ALIGN_RIGHT: + strcpy_P(*text, PSTR("right")); + break; + default: + strcpy_P(*text, PSTR("left")); + } + + return ATTR_TYPE_STR; } -bool hasp_process_obj_attribute_val(lv_obj_t* obj, const char* attr, int16_t intval, bool boolval, bool update) +static hasp_attribute_type_t attribute_common_text(lv_obj_t* obj, const char* attr, const char* payload, char** text, + bool update) { - // int16_t intval = atoi(payload); + uint8_t obj_type = obj_get_type(obj); - if(check_obj_type(obj, LV_HASP_BUTTON)) { + hasp_attr_update_char_const_t list[] = { + {LV_HASP_BUTTON, ATTR_TEXT, my_btn_set_text, my_btn_get_text}, + {LV_HASP_LABEL, ATTR_TEXT, my_label_set_text, my_label_get_text}, + {LV_HASP_CHECKBOX, ATTR_TEXT, lv_checkbox_set_text, lv_checkbox_get_text}, + {LV_HASP_TABVIEW, ATTR_TEXT, my_tabview_set_text, my_tabview_get_text}, + {LV_HASP_TAB, ATTR_TEXT, my_tab_set_text, my_tab_get_text}, +#if LV_USE_WIN != 0 + {LV_HASP_WINDOW, ATTR_TEXT, lv_win_set_title, lv_win_get_title}, +#endif + {LV_HASP_MSGBOX, ATTR_TEXT, lv_msgbox_set_text, lv_msgbox_get_text} + }; + + for(int i = 0; i < sizeof(list) / sizeof(list[0]); i++) { + if(obj_type == list[i].obj_type) { + if(update) + list[i].set(obj, payload); + else + *text = (char*)list[i].get(obj); + + return hasp_attribute_type_t::ATTR_TYPE_STR; + } + } + + // Special cases + // {LV_HASP_DROPDOWN, set_text_not_implemented, my_dropdown_get_text}, + // {LV_HASP_ROLLER, set_text_not_implemented, my_roller_get_text}, + switch(obj_get_type(obj)) { + case LV_HASP_DROPDOWN: { + lv_dropdown_get_selected_str(obj, *text, 128); + if(update) return hasp_attribute_type_t::ATTR_TYPE_STR_READONLY; + break; + } + + case LV_HASP_ROLLER: { + lv_roller_get_selected_str(obj, *text, 128); + if(update) return hasp_attribute_type_t::ATTR_TYPE_STR_READONLY; + break; + } + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; + } + + return hasp_attribute_type_t::ATTR_TYPE_STR; +} + +static hasp_attribute_type_t specific_bool_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) +{ + { // bool + hasp_attr_update_bool_const_t list[] = { + {LV_HASP_ARC, ATTR_ADJUSTABLE, my_arc_set_adjustable, my_arc_get_adjustable}, + {LV_HASP_BTNMATRIX, ATTR_ONE_CHECK, lv_btnmatrix_set_one_check, lv_btnmatrix_get_one_check}, + {LV_HASP_IMAGE, ATTR_AUTO_SIZE, lv_img_set_auto_size, lv_img_get_auto_size}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_BOOL; + } + + { // bool + hasp_attr_update_bool_t list[] = { + {LV_HASP_DROPDOWN, ATTR_SHOW_SELECTED, lv_dropdown_set_show_selected, lv_dropdown_get_show_selected}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_BOOL; + } + + return ATTR_NOT_FOUND; +} + +static hasp_attribute_type_t specific_coord_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) +{ + { // lv_coord_t + hasp_attr_update_lv_coord_t list[] = { + {LV_HASP_IMAGE, ATTR_OFFSET_X, lv_img_set_offset_x, lv_img_get_offset_x}, + {LV_HASP_IMAGE, ATTR_OFFSET_Y, lv_img_set_offset_y, lv_img_get_offset_y}, + {LV_HASP_DROPDOWN, ATTR_MAX_HEIGHT, lv_dropdown_set_max_height, my_dropdown_get_max_height}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + return ATTR_NOT_FOUND; +} + +static hasp_attribute_type_t specific_int_attribute(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) +{ + { // uint16_t + hasp_attr_update_uint16_const_t list[] = { + {LV_HASP_MSGBOX, ATTR_AUTO_CLOSE, lv_msgbox_start_auto_close, my_msgbox_stop_auto_close}, + {LV_HASP_SPINNER, ATTR_SPEED, lv_spinner_set_spin_time, lv_spinner_get_spin_time}, + {LV_HASP_BAR, ATTR_ANIM_TIME, lv_bar_set_anim_time, lv_bar_get_anim_time}, + {LV_HASP_SWITCH, ATTR_ANIM_TIME, lv_switch_set_anim_time, lv_switch_get_anim_time}, + {LV_HASP_LIST, ATTR_ANIM_TIME, lv_list_set_anim_time, lv_list_get_anim_time}, + {LV_HASP_MSGBOX, ATTR_ANIM_TIME, lv_msgbox_set_anim_time, lv_msgbox_get_anim_time}, + {LV_HASP_PAGE, ATTR_ANIM_TIME, lv_page_set_anim_time, lv_page_get_anim_time}, + {LV_HASP_ROLLER, ATTR_ANIM_TIME, lv_roller_set_anim_time, lv_roller_get_anim_time}, + {LV_HASP_TABVIEW, ATTR_ANIM_TIME, lv_tabview_set_anim_time, lv_tabview_get_anim_time}, + {LV_HASP_WINDOW, ATTR_ANIM_TIME, lv_win_set_anim_time, lv_win_get_anim_time}, + {LV_HASP_LABEL, ATTR_ANIM_SPEED, lv_label_set_anim_speed, lv_label_get_anim_speed}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + { // uint16_t, but getter is not const + hasp_attr_update_uint16_t list[] = { + {LV_HASP_ARC, ATTR_ROTATION, lv_arc_set_rotation, my_arc_get_rotation}, + {LV_HASP_ARC, ATTR_START_ANGLE, lv_arc_set_bg_start_angle, lv_arc_get_bg_angle_start}, + {LV_HASP_ARC, ATTR_END_ANGLE, lv_arc_set_bg_end_angle, lv_arc_get_bg_angle_end}, + {LV_HASP_ARC, ATTR_START_ANGLE1, lv_arc_set_start_angle, lv_arc_get_angle_start}, + {LV_HASP_ARC, ATTR_END_ANGLE1, lv_arc_set_end_angle, lv_arc_get_angle_end}, + {LV_HASP_LINEMETER, ATTR_ROTATION, lv_linemeter_set_angle_offset, lv_linemeter_get_angle_offset}, + {LV_HASP_GAUGE, ATTR_ROTATION, lv_gauge_set_angle_offset, lv_gauge_get_angle_offset}, + {LV_HASP_SLIDER, ATTR_ANIM_TIME, lv_slider_set_anim_time, lv_slider_get_anim_time}, + {LV_HASP_TABLE, ATTR_COLS, lv_table_set_col_cnt, lv_table_get_col_cnt}, + {LV_HASP_TABLE, ATTR_ROWS, lv_table_set_row_cnt, lv_table_get_row_cnt}, + {LV_HASP_TILEVIEW, ATTR_ANIM_TIME, lv_tileview_set_anim_time, lv_tileview_get_anim_time}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + { // lv_anim_value_t + hasp_attr_update_lv_anim_value_const_t list[] = { + {LV_HASP_SPINNER, ATTR_ANGLE, lv_spinner_set_arc_length, lv_spinner_get_arc_length}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + { // int16_t + hasp_attr_update_int16_const_t list[] = { + {LV_HASP_BAR, ATTR_START_VALUE, my_bar_set_start_value, lv_bar_get_start_value}, + {LV_HASP_SLIDER, ATTR_START_VALUE, my_slider_set_left_value, lv_slider_get_left_value}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + { // uint8_t + hasp_attr_update_uint8_const_t list[] = { + {LV_HASP_ROLLER, ATTR_ROWS, lv_roller_set_visible_row_count, my_roller_get_visible_row_count}}; + if(do_attribute(list, obj, attr_hash, val, update)) return ATTR_TYPE_INT; + } + + if(obj_check_type(obj, LV_HASP_TABVIEW)) { + switch(attr_hash) { + case ATTR_COUNT: + val = lv_tabview_get_tab_count(obj); + if(update) + return ATTR_TYPE_INT_READONLY; + else + return ATTR_TYPE_INT; + + case ATTR_BTN_POS: + if(update) { + lv_tabview_set_btns_pos(obj, val); + } else { + val = lv_tabview_get_btns_pos(obj); + } + return ATTR_TYPE_INT; + } + } + + return ATTR_NOT_FOUND; +} + +static bool my_obj_get_range(lv_obj_t* obj, int32_t& min, int32_t& max) +{ + min = 0; + max = 1; + + switch(obj_get_type(obj)) { + case LV_HASP_BUTTON: + if(!lv_btn_get_checkable(obj)) { + return false; // not checkable + } + case LV_HASP_CHECKBOX: + case LV_HASP_SWITCH: + // default min=0 and max=1 + break; + + case LV_HASP_LED: + min = 0; + max = 255; + break; + + case LV_HASP_LINEMETER: + min = lv_linemeter_get_min_value(obj); + max = lv_linemeter_get_max_value(obj); + break; + + case LV_HASP_SLIDER: + min = lv_slider_get_min_value(obj); + max = lv_slider_get_max_value(obj); + break; + + case LV_HASP_GAUGE: + min = lv_gauge_get_min_value(obj); + max = lv_gauge_get_max_value(obj); + break; + + case LV_HASP_ARC: + min = lv_arc_get_min_value(obj); + max = lv_arc_get_max_value(obj); + break; + + case LV_HASP_BAR: + min = lv_bar_get_min_value(obj); + max = lv_bar_get_max_value(obj); + break; + + case LV_HASP_TABVIEW: + min = 0; + max = lv_tabview_get_tab_count(obj) - 1; + if(max == 0) return false; // only one tab available + break; + + case LV_HASP_CHART: + min = my_chart_get_min_value(obj); + max = my_chart_get_max_value(obj); + break; + + case LV_HASP_DROPDOWN: + case LV_HASP_ROLLER: + return false; // not supported yet + + default: + return false; + } + return true; +} + +static hasp_attribute_type_t attribute_common_val(lv_obj_t* obj, int32_t& val, bool update) +{ + if(obj_check_type(obj, LV_HASP_BUTTON)) { if(lv_btn_get_checkable(obj)) { if(update) { - if(intval) + if(val) lv_obj_add_state(obj, LV_STATE_CHECKED); else lv_obj_clear_state(obj, LV_STATE_CHECKED); } else { - attr_out_int(obj, attr, lv_obj_get_state(obj, LV_BTN_PART_MAIN) & LV_STATE_CHECKED); + val = lv_obj_get_state(obj, LV_BTN_PART_MAIN) & LV_STATE_CHECKED; } } else { - return false; // not checkable + return hasp_attribute_type_t::ATTR_NOT_FOUND; // not checkable } - } else if(check_obj_type(obj, LV_HASP_CHECKBOX)) { - update ? lv_checkbox_set_checked(obj, boolval) : attr_out_int(obj, attr, lv_checkbox_is_checked(obj)); - } else if(check_obj_type(obj, LV_HASP_SWITCH)) { + } else if(obj_check_type(obj, LV_HASP_CHECKBOX)) { if(update) - boolval ? lv_switch_on(obj, LV_ANIM_ON) : lv_switch_off(obj, LV_ANIM_ON); + lv_checkbox_set_checked(obj, !!val); else - attr_out_int(obj, attr, lv_switch_get_state(obj)); - } else if(check_obj_type(obj, LV_HASP_DROPDOWN)) { - lv_dropdown_set_selected(obj, (uint16_t)intval); - } else if(check_obj_type(obj, LV_HASP_LMETER)) { - update ? lv_linemeter_set_value(obj, intval) : attr_out_int(obj, attr, lv_linemeter_get_value(obj)); - } else if(check_obj_type(obj, LV_HASP_SLIDER)) { - update ? lv_slider_set_value(obj, intval, LV_ANIM_ON) : attr_out_int(obj, attr, lv_slider_get_value(obj)); - } else if(check_obj_type(obj, LV_HASP_LED)) { - update ? lv_led_set_bright(obj, (uint8_t)intval) : attr_out_int(obj, attr, lv_led_get_bright(obj)); - } else if(check_obj_type(obj, LV_HASP_ARC)) { - update ? lv_arc_set_value(obj, intval) : attr_out_int(obj, attr, lv_arc_get_value(obj)); - } else if(check_obj_type(obj, LV_HASP_GAUGE)) { - update ? lv_gauge_set_value(obj, 0, intval) : attr_out_int(obj, attr, lv_gauge_get_value(obj, 0)); - } else if(check_obj_type(obj, LV_HASP_ROLLER)) { - lv_roller_set_selected(obj, (uint16_t)intval, LV_ANIM_ON); - } else if(check_obj_type(obj, LV_HASP_BAR)) { - update ? lv_bar_set_value(obj, intval, LV_ANIM_ON) : attr_out_int(obj, attr, lv_bar_get_value(obj)); + val = lv_checkbox_is_checked(obj); + } else if(obj_check_type(obj, LV_HASP_SWITCH)) { + if(update) + !val ? lv_switch_off(obj, LV_ANIM_ON) : lv_switch_on(obj, LV_ANIM_ON); + else + val = lv_switch_get_state(obj); + } else if(obj_check_type(obj, LV_HASP_DROPDOWN)) { + lv_dropdown_set_selected(obj, (uint16_t)val); + } else if(obj_check_type(obj, LV_HASP_LINEMETER)) { + if(update) + lv_linemeter_set_value(obj, val); + else + val = lv_linemeter_get_value(obj); + } else if(obj_check_type(obj, LV_HASP_SLIDER)) { + if(update) + lv_slider_set_value(obj, val, LV_ANIM_ON); + else + val = lv_slider_get_value(obj); + } else if(obj_check_type(obj, LV_HASP_LED)) { + if(update) + lv_led_set_bright(obj, (uint8_t)val); + else + val = lv_led_get_bright(obj); + } else if(obj_check_type(obj, LV_HASP_ARC)) { + if(update) + lv_arc_set_value(obj, val); + else + val = lv_arc_get_value(obj); + } else if(obj_check_type(obj, LV_HASP_GAUGE)) { + if(update) + lv_gauge_set_value(obj, 0, val); + else + val = lv_gauge_get_value(obj, 0); + } else if(obj_check_type(obj, LV_HASP_ROLLER)) { + lv_roller_set_selected(obj, (uint16_t)val, LV_ANIM_ON); + } else if(obj_check_type(obj, LV_HASP_BAR)) { + if(update) + lv_bar_set_value(obj, val, LV_ANIM_ON); + else + val = lv_bar_get_value(obj); + } else if(obj_check_type(obj, LV_HASP_TABVIEW)) { + if(update) + lv_tabview_set_tab_act(obj, val, LV_ANIM_ON); + else + val = lv_tabview_get_tab_act(obj); } else { - return false; + return hasp_attribute_type_t::ATTR_NOT_FOUND; // not found } + return hasp_attribute_type_t::ATTR_TYPE_INT; // found +} + +bool attribute_set_normalized_value(lv_obj_t* obj, hasp_update_value_t& value) +{ + if(value.min == value.max) return false; // would cause divide by zero error + + int32_t val; + int32_t min; + int32_t max; + if(!my_obj_get_range(obj, min, max)) return false; // range could not be determined + + // Limit the value between min and max, adjust if power = 0 + if(value.power == 0 || value.val <= value.min) { + val = value.min; + } else if(value.val >= value.max) { + val = value.max; + } else { + val = value.val; + } + + if(min == 0 && max == 1) { + val = val != value.min; // Toggles are set to 0 when val = min, otherwise 1 + } else { + val = map(val, value.min, value.max, min, max); + } + + attribute_common_val(obj, val, true); return true; } -static void hasp_process_obj_attribute_range(lv_obj_t* obj, const char* attr, const char* payload, bool update, - bool set_min, bool set_max) +static hasp_attribute_type_t attribute_common_range(lv_obj_t* obj, int32_t& val, bool update, bool set_min, + bool set_max) { - int16_t val = atoi(payload); - int32_t val32 = strtol(payload, nullptr, DEC); + int32_t min; + int32_t max; + if(!my_obj_get_range(obj, min, max)) return hasp_attribute_type_t::ATTR_RANGE_ERROR; - /* Attributes depending on objecttype */ - // lv_obj_type_t list; - // lv_obj_get_type(obj, &list); - // const char * objtype = list.type[0]; + switch(obj_get_type(obj)) { + case LV_HASP_SLIDER: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_slider_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; - if(check_obj_type(obj, LV_HASP_SLIDER)) { - int16_t min = lv_slider_get_min_value(obj); - int16_t max = lv_slider_get_max_value(obj); - if(update && (set_min ? val : min) == (set_max ? val : max)) return; // prevent setting min=max - return update ? lv_slider_set_range(obj, set_min ? val : min, set_max ? val : max) - : attr_out_int(obj, attr, set_min ? min : max); + case LV_HASP_GAUGE: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_gauge_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; + + case LV_HASP_ARC: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_arc_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; + + case LV_HASP_BAR: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_bar_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; + + case LV_HASP_LINEMETER: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_linemeter_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; + + case LV_HASP_CHART: + if(update && (set_min ? val : min) == (set_max ? val : max)) + return hasp_attribute_type_t::ATTR_RANGE_ERROR; // prevent setting min=max + if(update) + lv_chart_set_range(obj, set_min ? val : min, set_max ? val : max); + else + val = set_min ? min : max; + break; + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; } - if(check_obj_type(obj, LV_HASP_GAUGE)) { - int32_t min = lv_gauge_get_min_value(obj); - int32_t max = lv_gauge_get_max_value(obj); - if(update && (set_min ? val32 : min) == (set_max ? val32 : max)) return; // prevent setting min=max - return update ? lv_gauge_set_range(obj, set_min ? val32 : min, set_max ? val32 : max) - : attr_out_int(obj, attr, set_min ? min : max); + return hasp_attribute_type_t::ATTR_TYPE_INT; +} + +// ##################### Default Attributes ######################################################## + +/** + * Execute the method of an object + * @param obj lv_obj_t*: the object to get/set the attribute + * @param attr_hash uint16_t: the hashed attribute value + * @param attr char*: the attribute name (without leading ".") + * @param payload char*: the parameters for the method + * @note setting a value won't return anything, getting will dispatch the value + */ +static hasp_attribute_type_t attribute_common_method(lv_obj_t* obj, uint16_t attr_hash, const char* attr, + const char* payload) +{ + switch(attr_hash) { + case ATTR_DELETE: + if(!lv_obj_get_parent(obj)) return hasp_attribute_type_t::ATTR_TYPE_METHOD_INVALID_FOR_PAGE; + lv_obj_del_async(obj); + break; + + case ATTR_CLEAR: + lv_obj_clean(obj); + break; + + case ATTR_TO_FRONT: + if(!lv_obj_get_parent(obj)) return hasp_attribute_type_t::ATTR_TYPE_METHOD_INVALID_FOR_PAGE; + lv_obj_move_foreground(obj); + break; + + case ATTR_TO_BACK: + if(!lv_obj_get_parent(obj)) return hasp_attribute_type_t::ATTR_TYPE_METHOD_INVALID_FOR_PAGE; + lv_obj_move_background(obj); + break; + + case ATTR_OPEN: + case ATTR_CLOSE: + if(!obj_check_type(obj, LV_HASP_DROPDOWN)) return hasp_attribute_type_t::ATTR_NOT_FOUND; + if(attr_hash == ATTR_OPEN) + lv_dropdown_open(obj); + else + lv_dropdown_open(obj); + break; + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; // attribute_not found } - if(check_obj_type(obj, LV_HASP_ARC)) { - int16_t min = lv_arc_get_min_value(obj); - int16_t max = lv_arc_get_max_value(obj); - if(update && (set_min ? val : min) == (set_max ? val : max)) return; // prevent setting min=max - return update ? lv_arc_set_range(obj, set_min ? val : min, set_max ? val : max) - : attr_out_int(obj, attr, set_min ? min : max); + return hasp_attribute_type_t::ATTR_TYPE_METHOD_OK; +} + +/** + * Change or Retrieve the value of the attribute of an object + * @param obj lv_obj_t*: the object to get/set the attribute + * @param attr_hash uint16_t: the hashed attribute to get/set + * @param val uint32_t: the new value of the attribute + * @param update bool: change/set the value if true, dispatch/get value if false + * @note setting a value won't return anything, getting will dispatch the value + */ +static hasp_attribute_type_t attribute_common_int(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) +{ + switch(attr_hash) { + case ATTR_ID: + if(update) + obj->user_data.id = (uint8_t)val; + else + val = obj->user_data.id; + break; // attribute_found + + case ATTR_GROUPID: + if(update) + obj->user_data.groupid = (uint8_t)val; + else + val = obj->user_data.groupid; + break; // attribute_found + + case ATTR_TRANSITION: + if(update) + obj->user_data.transitionid = (uint8_t)val; + else + val = obj->user_data.transitionid; + break; // attribute_found + + case ATTR_OBJID: + val = obj->user_data.objid; + if(update && val != obj->user_data.objid) hasp_attribute_type_t::ATTR_TYPE_INT_READONLY; + break; // attribute_found + + case ATTR_X: + if(update) + lv_obj_set_x(obj, val); + else + val = lv_obj_get_x(obj); + break; // attribute_found + + case ATTR_Y: + if(update) + lv_obj_set_y(obj, val); + else + val = lv_obj_get_y(obj); + break; // attribute_found + + case ATTR_W: + if(update) { + lv_obj_set_width(obj, val); + if(obj_check_type(obj, LV_HASP_CPICKER)) { +#if LVGL_VERSION_MAJOR == 7 + lv_cpicker_set_type(obj, lv_obj_get_width(obj) == lv_obj_get_height(obj) ? LV_CPICKER_TYPE_DISC + : LV_CPICKER_TYPE_RECT); +#endif + } + } else { + val = lv_obj_get_width(obj); + } + break; // attribute_found + + case ATTR_H: + if(update) { + lv_obj_set_height(obj, val); + if(obj_check_type(obj, LV_HASP_CPICKER)) { +#if LVGL_VERSION_MAJOR == 7 + lv_cpicker_set_type(obj, lv_obj_get_width(obj) == lv_obj_get_height(obj) ? LV_CPICKER_TYPE_DISC + : LV_CPICKER_TYPE_RECT); +#endif + } + } else { + val = lv_obj_get_height(obj); + } + break; // attribute_found + + case ATTR_OPACITY: + if(update) + lv_obj_set_style_local_opa_scale(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, val); + else + val = lv_obj_get_style_opa_scale(obj, LV_OBJ_PART_MAIN); + break; // attribute_found + + case ATTR_EXT_CLICK_H: + if(update) + lv_obj_set_ext_click_area(obj, val, val, lv_obj_get_ext_click_pad_top(obj), + lv_obj_get_ext_click_pad_bottom(obj)); + else + val = lv_obj_get_ext_click_pad_left(obj); + break; // attribute_found + + case ATTR_EXT_CLICK_V: + if(update) + lv_obj_set_ext_click_area(obj, lv_obj_get_ext_click_pad_left(obj), lv_obj_get_ext_click_pad_right(obj), + val, val); + else + val = lv_obj_get_ext_click_pad_top(obj); + break; // attribute_found + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; // attribute_not found } - if(check_obj_type(obj, LV_HASP_BAR)) { - int16_t min = lv_bar_get_min_value(obj); - int16_t max = lv_bar_get_max_value(obj); - if(update && (set_min ? val : min) == (set_max ? val : max)) return; // prevent setting min=max - return update ? lv_bar_set_range(obj, set_min ? val : min, set_max ? val : max) - : attr_out_int(obj, attr, set_min ? min : max); + return hasp_attribute_type_t::ATTR_TYPE_INT; +} + +/** + * Change or Retrieve the value of the attribute of an object + * @param obj lv_obj_t*: the object to get/set the attribute + * @param attr_hash uint16_t: the hashed attribute to get/set + * @param val uint32_t: the new value of the attribute + * @param update bool: change/set the value if true, dispatch/get value if false + * @note setting a value won't return anything, getting will dispatch the value + */ +static hasp_attribute_type_t attribute_common_bool(lv_obj_t* obj, uint16_t attr_hash, int32_t& val, bool update) +{ + switch(attr_hash) { + case ATTR_VIS: + if(update) + lv_obj_set_hidden(obj, !val); + else + val = !lv_obj_get_hidden(obj); + break; // attribute_found + + case ATTR_HIDDEN: + if(update) + lv_obj_set_hidden(obj, !!val); + else + val = lv_obj_get_hidden(obj); + break; // attribute_found + + case ATTR_CLICK: + if(update) + lv_obj_set_click(obj, !!val); + else + val = lv_obj_get_click(obj); + break; // attribute_found + + case ATTR_ENABLED: + if(update) + if(!!val) + lv_obj_clear_state(obj, LV_STATE_DISABLED); + else + lv_obj_add_state(obj, LV_STATE_DISABLED); + else + val = !(lv_obj_get_state(obj, LV_BTN_PART_MAIN) & LV_STATE_DISABLED); + break; // attribute_found + + case ATTR_SWIPE: + if(update) + obj->user_data.swipeid = (!!val) % 16; + else + val = obj->user_data.swipeid; + break; // attribute_found + + case ATTR_TOGGLE: + switch(obj_get_type(obj)) { + case LV_HASP_BUTTON: + if(update) { + lv_btn_set_checkable(obj, !!val); + lv_obj_set_event_cb(obj, !!val ? toggle_event_handler : generic_event_handler); + } else { + val = lv_btn_get_checkable(obj); + } + break; // attribute_found + case LV_HASP_BTNMATRIX: + if(update) { + if(val) { + lv_btnmatrix_set_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKABLE); + } else { + lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKABLE); + lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECK_STATE); + } + } else { + val = lv_btn_get_checkable(obj); + } + break; // attribute_found + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; // attribute_not found + } + + default: + return hasp_attribute_type_t::ATTR_NOT_FOUND; // attribute_not found } - if(check_obj_type(obj, LV_HASP_LMETER)) { - int32_t min = lv_linemeter_get_min_value(obj); - int32_t max = lv_linemeter_get_max_value(obj); - if(update && (set_min ? val32 : min) == (set_max ? val32 : max)) return; // prevent setting min=max - return update ? lv_linemeter_set_range(obj, set_min ? val32 : min, set_max ? val32 : max) - : attr_out_int(obj, attr, set_min ? min : max); - } - - if(check_obj_type(obj, LV_HASP_CHART)) { - int16_t min = my_chart_get_min_value(obj); - int16_t max = my_chart_get_max_value(obj); - if(update && (set_min ? val : min) == (set_max ? val : max)) return; // prevent setting min=max - return update ? lv_chart_set_range(obj, set_min ? val : min, set_max ? val : max) - : attr_out_int(obj, attr, set_min ? min : max); - } - - LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN), attr); + return hasp_attribute_type_t::ATTR_TYPE_BOOL; } // ##################### Default Attributes ######################################################## @@ -1414,392 +1677,79 @@ static void hasp_process_obj_attribute_range(lv_obj_t* obj, const char* attr, co * @param update bool: change/set the value if true, dispatch/get value if false * @note setting a value won't return anything, getting will dispatch the value */ -void hasp_process_obj_attribute(lv_obj_t* obj, const char* attr_p, const char* payload, bool update) +void old_process_obj_attribute(lv_obj_t* obj, const char* attr_p, const char* payload, bool update) { - // unsigned long start = millis(); - if(!obj) { - LOG_WARNING(TAG_ATTR, F(D_OBJECT_UNKNOWN)); - return; - } - - char* attr = (char*)attr_p; - if(*attr == '.') attr++; // strip leading '.' - - uint16_t attr_hash = Parser::get_sdbm(attr); - int16_t val = atoi(payload); + uint16_t attr_hash; /* 16-bit Hash Lookup Table */ switch(attr_hash) { - case ATTR_ID: - update ? (void)(obj->user_data.id = (uint8_t)val) : attr_out_int(obj, attr, obj->user_data.id); - break; // attribute_found - - case ATTR_GROUPID: - update ? (void)(obj->user_data.groupid = (uint8_t)val) : attr_out_int(obj, attr, obj->user_data.groupid); - break; // attribute_found - - case ATTR_ACTION: - update ? (void)(obj->user_data.actionid = Parser::get_action_id(payload)) - : attr_out_int(obj, attr, obj->user_data.actionid); - break; // attribute_found - - case ATTR_TRANSITION: - update ? (void)(obj->user_data.transitionid = (uint8_t)val) - : attr_out_int(obj, attr, obj->user_data.transitionid); - break; // attribute_found - - case ATTR_OBJ: - if(update) LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_READ_ONLY), attr_p); - attr_out_str(obj, attr, get_obj_type_name(obj)); - break; // attribute_found - - case ATTR_OBJID: - if(update) LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_READ_ONLY), attr_p); - attr_out_int(obj, attr, obj->user_data.objid); - break; // attribute_found - - case ATTR_X: - update ? lv_obj_set_x(obj, val) : attr_out_int(obj, attr, lv_obj_get_x(obj)); - break; // attribute_found - - case ATTR_Y: - update ? lv_obj_set_y(obj, val) : attr_out_int(obj, attr, lv_obj_get_y(obj)); - break; // attribute_found - - case ATTR_W: - if(update) { - lv_obj_set_width(obj, val); - if(check_obj_type(obj, LV_HASP_CPICKER)) { -#if LVGL_VERSION_MAJOR == 7 - lv_cpicker_set_type(obj, lv_obj_get_width(obj) == lv_obj_get_height(obj) ? LV_CPICKER_TYPE_DISC - : LV_CPICKER_TYPE_RECT); -#endif - } - } else { - attr_out_int(obj, attr, lv_obj_get_width(obj)); - } - break; // attribute_found - - case ATTR_H: - if(update) { - lv_obj_set_height(obj, val); - if(check_obj_type(obj, LV_HASP_CPICKER)) { -#if LVGL_VERSION_MAJOR == 7 - lv_cpicker_set_type(obj, lv_obj_get_width(obj) == lv_obj_get_height(obj) ? LV_CPICKER_TYPE_DISC - : LV_CPICKER_TYPE_RECT); -#endif - } - } else { - attr_out_int(obj, attr, lv_obj_get_height(obj)); - } - break; // attribute_found - - case ATTR_VIS: - update ? lv_obj_set_hidden(obj, !Parser::is_true(payload)) - : attr_out_int(obj, attr, !lv_obj_get_hidden(obj)); - break; // attribute_found - - case ATTR_HIDDEN: - update ? lv_obj_set_hidden(obj, Parser::is_true(payload)) : attr_out_int(obj, attr, lv_obj_get_hidden(obj)); - break; // attribute_found - - case ATTR_TXT: // TODO: remove - LOG_WARNING(TAG_HASP, F("txt property is obsolete, use text instead")); - case ATTR_TEXT: - hasp_process_obj_attribute_txt(obj, attr, payload, update); - break; // attribute_found - - case ATTR_COLOR: - if(check_obj_type(obj, LV_HASP_CPICKER)) { - if(update) { - lv_color32_t c; - if(Parser::haspPayloadToColor(payload, c)) - lv_cpicker_set_color(obj, lv_color_make(c.ch.red, c.ch.green, c.ch.blue)); - } else { - attr_out_color(obj, attr, lv_cpicker_get_color(obj)); - } - } else { - goto attribute_not_found; - } - break; // attribute_found - - case ATTR_VAL: - if(!hasp_process_obj_attribute_val(obj, attr, atoi(payload), Parser::is_true(payload), update)) - goto attribute_not_found; - break; // attribute_found - - case ATTR_MIN: - hasp_process_obj_attribute_range(obj, attr, payload, update, true, false); - break; // attribute_found - - case ATTR_MAX: - hasp_process_obj_attribute_range(obj, attr, payload, update, false, true); - break; // attribute_found - - case ATTR_OPACITY: - update ? lv_obj_set_style_local_opa_scale(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, val) - : attr_out_int(obj, attr, lv_obj_get_style_opa_scale(obj, LV_OBJ_PART_MAIN)); - break; // attribute_found - - case ATTR_CLICK: - update ? lv_obj_set_click(obj, Parser::is_true(payload)) : attr_out_int(obj, attr, lv_obj_get_click(obj)); - break; // attribute_found - - case ATTR_ENABLED: - if(update) - if(Parser::is_true(payload)) - lv_obj_clear_state(obj, LV_STATE_DISABLED); - else - lv_obj_add_state(obj, LV_STATE_DISABLED); - else - attr_out_int(obj, attr, !(lv_obj_get_state(obj, LV_BTN_PART_MAIN) & LV_STATE_DISABLED)); - break; // attribute_found - - case ATTR_SWIPE: - update ? (void)(obj->user_data.swipeid = Parser::is_true(payload) % 16) - : attr_out_int(obj, attr, obj->user_data.swipeid); - break; // attribute_found - - case ATTR_SRC: - if(check_obj_type(obj, LV_HASP_IMAGE)) { - if(update) { - return lv_img_set_src(obj, payload); - } else { - switch(lv_img_src_get_type(obj)) { - case LV_IMG_SRC_FILE: - return attr_out_str(obj, attr, lv_img_get_file_name(obj)); - case LV_IMG_SRC_SYMBOL: - return attr_out_str(obj, attr, (char*)lv_img_get_src(obj)); - default: - return; - } - } - } else { - goto attribute_not_found; - } - break; // attribute_found - - /* case ATTR_ANIM_TIME: - return (update) ? lv_anim_time(obj, val) - : attr_out_int(obj, attr, lv_gauge_get_angle_offset(obj)); */ - - case ATTR_ROWS: - if(check_obj_type(obj, LV_HASP_ROLLER)) { - update ? lv_roller_set_visible_row_count(obj, (uint8_t)val) - : attr_out_int(obj, attr, my_roller_get_visible_row_count(obj)); - } else if(check_obj_type(obj, LV_HASP_TABLE)) { - update ? lv_table_set_row_cnt(obj, (uint8_t)val) : attr_out_int(obj, attr, lv_table_get_row_cnt(obj)); - } else { - goto attribute_not_found; - } - break; // attribute_found - - case ATTR_COLS: - if(check_obj_type(obj, LV_HASP_TABLE)) { - update ? lv_table_set_col_cnt(obj, (uint8_t)val) : attr_out_int(obj, attr, lv_table_get_col_cnt(obj)); - } else { - goto attribute_not_found; - } - break; // attribute_found - - // case ATTR_RECT: - // if(check_obj_type(obj, LV_HASP_CPICKER)) { - // lv_cpicker_set_type(obj, is_true(payload) ? LV_CPICKER_TYPE_RECT : LV_CPICKER_TYPE_DISC); - // return; - // } - // break; - - case ATTR_ALIGN: - if(check_obj_type(obj, LV_HASP_BUTTON)) { - lv_obj_t* label = FindButtonLabel(obj); - if(label == NULL) - goto attribute_not_found; - else - update ? lv_label_set_align(label, val) : attr_out_int(obj, attr, lv_label_get_align(label)); - - } else if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { - update ? lv_btnmatrix_set_align(obj, val) : attr_out_int(obj, attr, lv_btnmatrix_get_align(obj)); - } else if(check_obj_type(obj, LV_HASP_LABEL)) { - update ? lv_label_set_align(obj, val) : attr_out_int(obj, attr, lv_label_get_align(obj)); - } else if(check_obj_type(obj, LV_HASP_ROLLER)) { - update ? lv_roller_set_align(obj, val) : attr_out_int(obj, attr, lv_roller_get_align(obj)); - } else { - goto attribute_not_found; - } - break; // attribute_found case ATTR_MODE: - if(check_obj_type(obj, LV_HASP_BUTTON)) { - lv_obj_t* label = FindButtonLabel(obj); - if(label) { - hasp_process_label_long_mode(label, payload, update); - lv_obj_set_width(label, lv_obj_get_width(obj)); - } - } else if(check_obj_type(obj, LV_HASP_LABEL)) { - hasp_process_label_long_mode(obj, payload, update); - } else if(check_obj_type(obj, LV_HASP_ROLLER)) { - if(update) { - lv_roller_set_options(obj, lv_roller_get_options(obj), (lv_roller_mode_t)Parser::is_true(payload)); - } else { - lv_roller_ext_t* ext = (lv_roller_ext_t*)lv_obj_get_ext_attr(obj); - attr_out_int(obj, attr, ext->mode); - } - } else { - goto attribute_not_found; - } - break; // attribute_found - - case ATTR_TOGGLE: - if(check_obj_type(obj, LV_HASP_BUTTON)) { - if(update) { - bool toggle = Parser::is_true(payload); - lv_btn_set_checkable(obj, toggle); - lv_obj_set_event_cb(obj, toggle ? toggle_event_handler : generic_event_handler); - } else { - attr_out_int(obj, attr, lv_btn_get_checkable(obj)); - } - } else if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { - if(update) { - bool toggle = Parser::is_true(payload); - if(toggle) { - lv_btnmatrix_set_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKABLE); - } else { - lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKABLE); - lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECK_STATE); + switch(obj_get_type(obj)) { + case LV_HASP_BUTTON: { + lv_obj_t* label = FindButtonLabel(obj); + if(label) { + hasp_process_label_long_mode(label, payload, update); + lv_obj_set_width(label, lv_obj_get_width(obj)); } - } else { - attr_out_int(obj, attr, lv_btn_get_checkable(obj)); + return; // attribute_found } - } else { - goto attribute_not_found; + case LV_HASP_LABEL: + hasp_process_label_long_mode(obj, payload, update); + return; // attribute_found + case LV_HASP_ROLLER: + if(update) { + lv_roller_set_options(obj, lv_roller_get_options(obj), + (lv_roller_mode_t)Parser::is_true(payload)); + } else { + lv_roller_ext_t* ext = (lv_roller_ext_t*)lv_obj_get_ext_attr(obj); + attr_out_int(obj, attr_p, ext->mode); + } + return; // attribute_found + default: + break; // not found } - break; // attribute_found + break; // not found case ATTR_OPTIONS: - if(check_obj_type(obj, LV_HASP_DROPDOWN)) { - if(update) { - lv_dropdown_set_options(obj, payload); - } else { - attr_out_str(obj, attr, lv_dropdown_get_options(obj)); - } - } else if(check_obj_type(obj, LV_HASP_ROLLER)) { - if(update) { - lv_roller_ext_t* ext = (lv_roller_ext_t*)lv_obj_get_ext_attr(obj); - lv_roller_set_options(obj, payload, ext->mode); - } else { - attr_out_str(obj, attr, lv_roller_get_options(obj)); - } - } else if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { - if(update) { - my_btnmatrix_map_create(obj, payload); - } else { - attr_out_str(obj, attr_p, "Not implemented"); // TODO : Literal String - } - } else { - goto attribute_not_found; + switch(obj_get_type(obj)) { + case LV_HASP_DROPDOWN: + if(update) { + lv_dropdown_set_options(obj, payload); + } else { + attr_out_str(obj, attr_p, lv_dropdown_get_options(obj)); + } + return; // attribute_found + case LV_HASP_ROLLER: + if(update) { + lv_roller_ext_t* ext = (lv_roller_ext_t*)lv_obj_get_ext_attr(obj); + lv_roller_set_options(obj, payload, ext->mode); + } else { + attr_out_str(obj, attr_p, lv_roller_get_options(obj)); + } + return; // attribute_found + case LV_HASP_BTNMATRIX: + if(update) { + my_btnmatrix_set_map(obj, payload); + } else { + attr_out_str(obj, attr_p, "Not implemented"); // TODO : Literal String + } + return; // attribute_found + case LV_HASP_MSGBOX: + if(update) { + my_msgbox_set_map(obj, payload); + } else { + attr_out_str(obj, attr_p, "Not implemented"); // TODO : Literal String + } + return; // attribute_found + default: + break; // not found } - break; // attribute_found + break; // not found - case ATTR_ONE_CHECK: - if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { - if(update) { - lv_btnmatrix_set_one_check(obj, Parser::is_true(payload)); - } else { - attr_out_int(obj, attr_p, lv_btnmatrix_get_one_check(obj)); - } - } - break; - - case ATTR_CRITICAL_VALUE: - case ATTR_ANGLE: - case ATTR_LABEL_COUNT: - case ATTR_LINE_COUNT: - case ATTR_FORMAT: - case ATTR_TYPE: - case ATTR_ROTATION: - case ATTR_ADJUSTABLE: - case ATTR_START_ANGLE: - case ATTR_END_ANGLE: - case ATTR_START_ANGLE1: - case ATTR_END_ANGLE1: - if(check_obj_type(obj, LV_HASP_ARC)) { - hasp_process_arc_attribute(obj, attr_p, attr_hash, payload, update); - } else if(check_obj_type(obj, LV_HASP_GAUGE)) { - hasp_process_gauge_attribute(obj, attr_p, attr_hash, payload, update); - } else if(check_obj_type(obj, LV_HASP_LMETER)) { - hasp_process_lmeter_attribute(obj, attr_p, attr_hash, payload, update); - } else { - goto attribute_not_found; - } - break; // attribute_found - - case ATTR_DIRECTION: - case ATTR_SYMBOL: - case ATTR_OPEN: - case ATTR_CLOSE: - case ATTR_MAX_HEIGHT: - case ATTR_SHOW_SELECTED: - if(check_obj_type(obj, LV_HASP_DROPDOWN)) { - hasp_process_dropdown_attribute(obj, attr_p, attr_hash, payload, update); - } else { - goto attribute_not_found; - } - break; // attribute_found - - case ATTR_RED: // TODO: remove temp RED - if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { - my_btnmatrix_map_clear(obj); // TODO : remove this test property - } else { - goto attribute_not_found; - } - break; - - case ATTR_DELETE: - if(!lv_obj_get_parent(obj)) { - LOG_ERROR(TAG_ATTR, F(D_ATTRIBUTE_PAGE_METHOD_INVALID), attr_p); - return; - } - lv_obj_del_async(obj); - break; // attribute_found - - case ATTR_TO_FRONT: - if(!lv_obj_get_parent(obj)) { - LOG_ERROR(TAG_ATTR, F(D_ATTRIBUTE_PAGE_METHOD_INVALID), attr_p); - return; - } - lv_obj_move_foreground(obj); - break; // attribute_found - - case ATTR_TO_BACK: - if(!lv_obj_get_parent(obj)) { - LOG_ERROR(TAG_ATTR, F(D_ATTRIBUTE_PAGE_METHOD_INVALID), attr_p); - return; - } - lv_obj_move_background(obj); - break; // attribute_found - - case ATTR_NEXT: - case ATTR_PREV: - case ATTR_BACK: - if(check_obj_type(obj, LV_HASP_SCREEN)) { - hasp_process_page_attributes(obj, attr_p, attr_hash, val, update); - break; // attribute_found - } - goto attribute_not_found; - - case ATTR_COMMENT: - break; // attribute_found - - default: - hasp_local_style_attr(obj, attr, attr_hash, payload, update); + // case ATTR_MODAL: } -attribute_found: - // LOG_VERBOSE(TAG_ATTR, F("%s (%d)"), attr_p, attr_hash); - // LOG_VERBOSE(TAG_ATTR, F("%s (%d) took %d ms."), attr_p, attr_hash, millis() - start); - return; - -attribute_not_found: LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN " (%d)"), attr_p, attr_hash); } @@ -1808,22 +1758,17 @@ void attr_out_str(lv_obj_t* obj, const char* attribute, const char* data) uint8_t pageid; uint8_t objid; - if(hasp_find_id_from_obj(obj, &pageid, &objid)) { - if(!attribute || !data) return; + if(!attribute || !hasp_find_id_from_obj(obj, &pageid, &objid)) return; - StaticJsonDocument<32> doc; // Total (recommended) size + StaticJsonDocument<32> doc; // Total (recommended) size + if(data) doc[attribute].set(data); + else + doc[attribute].set(nullptr); - size_t size = measureJson(doc); // strlen(data) + strlen(attribute); - if(size < MQTT_MAX_PACKET_SIZE) { - char payload[MQTT_MAX_PACKET_SIZE]; - serializeJson(doc, payload); - object_dispatch_state(pageid, objid, payload); - - } else { - LOG_ERROR(TAG_ATTR, F(D_MQTT_PAYLOAD_TOO_LONG), size); - } - } + char payload[MQTT_MAX_PACKET_SIZE]; + serializeJson(doc, payload, MQTT_MAX_PACKET_SIZE); + object_dispatch_state(pageid, objid, payload); } void attr_out_int(lv_obj_t* obj, const char* attribute, int32_t val) @@ -1831,14 +1776,28 @@ void attr_out_int(lv_obj_t* obj, const char* attribute, int32_t val) uint8_t pageid; uint8_t objid; - if(hasp_find_id_from_obj(obj, &pageid, &objid)) { - if(!attribute) return; + if(!attribute || !hasp_find_id_from_obj(obj, &pageid, &objid)) return; - char payload[64 + strlen(attribute)]; - snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":%d}"), attribute, val); + char payload[64 + strlen(attribute)]; + snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":%d}"), attribute, val); - object_dispatch_state(pageid, objid, payload); - } + object_dispatch_state(pageid, objid, payload); +} + +void attr_out_bool(lv_obj_t* obj, const char* attribute, bool val) +{ + uint8_t pageid; + uint8_t objid; + + if(!attribute || !hasp_find_id_from_obj(obj, &pageid, &objid)) return; + + char payload[16 + strlen(attribute)]; + if(val) + snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":true}"), attribute); + else + snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":false}"), attribute); + + object_dispatch_state(pageid, objid, payload); } void attr_out_color(lv_obj_t* obj, const char* attribute, lv_color_t color) @@ -1846,15 +1805,275 @@ void attr_out_color(lv_obj_t* obj, const char* attribute, lv_color_t color) uint8_t pageid; uint8_t objid; - if(hasp_find_id_from_obj(obj, &pageid, &objid)) { - if(!attribute) return; + if(!attribute || !hasp_find_id_from_obj(obj, &pageid, &objid)) return; - char payload[64 + strlen(attribute)]; - lv_color32_t c32; - c32.full = lv_color_to32(color); + char payload[64 + strlen(attribute)]; + lv_color32_t c32; + c32.full = lv_color_to32(color); - snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d}"), attribute, - c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue); - object_dispatch_state(pageid, objid, payload); + snprintf_P(payload, sizeof(payload), PSTR("{\"%s\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d}"), attribute, + c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue); + object_dispatch_state(pageid, objid, payload); +} + +/** + * Change or Retrieve the value of the attribute of an object + * @param obj lv_obj_t*: the object to get/set the attribute + * @param attribute char*: the attribute name (with or without leading ".") + * @param payload char*: the new value of the attribute + * @param update bool: change/set the value if true, dispatch/get value if false + * @note setting a value won't return anything, getting will dispatch the value + */ +void hasp_process_obj_attribute(lv_obj_t* obj, const char* attribute, const char* payload, bool update) +{ + // unsigned long start = millis(); + if(!obj) return; + + lv_color_t color; + int32_t val; + char temp_buffer[128] = ""; // buffer to hold return strings + char* text = &temp_buffer[0]; // pointer to temp_buffer + hasp_attribute_type_t ret = + hasp_attribute_type_t::ATTR_NOT_FOUND; // the return code determines the attribute return value type + uint16_t attr_hash = Parser::get_sdbm(attribute); + + switch(attr_hash) { + case ATTR_GROUPID: + case ATTR_ID: + case ATTR_TRANSITION: + case ATTR_OBJID: + case ATTR_X: + case ATTR_Y: + case ATTR_H: + case ATTR_W: + case ATTR_OPACITY: + case ATTR_EXT_CLICK_H: + case ATTR_EXT_CLICK_V: + val = strtol(payload, nullptr, DEC); + ret = attribute_common_int(obj, attr_hash, val, update); + break; + + case ATTR_VIS: + case ATTR_HIDDEN: + case ATTR_TOGGLE: + case ATTR_CLICK: + case ATTR_SWIPE: + case ATTR_ENABLED: + val = Parser::is_true(payload); + ret = attribute_common_bool(obj, attr_hash, val, update); + break; + + case ATTR_MIN: + val = strtol(payload, nullptr, DEC); + ret = attribute_common_range(obj, val, update, true, false); + break; + + case ATTR_MAX: + val = strtol(payload, nullptr, DEC); + ret = attribute_common_range(obj, val, update, false, true); + break; + + case ATTR_VAL: + val = strtol(payload, nullptr, DEC); + ret = attribute_common_val(obj, val, update); + break; + + case ATTR_TXT: // TODO: remove + LOG_WARNING(TAG_HASP, F("txt property is obsolete, use text instead")); + case ATTR_TEXT: + ret = attribute_common_text(obj, attribute, payload, &text, update); + break; + + case ATTR_ALIGN: + ret = attribute_common_align(obj, attribute, payload, &text, update); + break; + + case ATTR_OBJ: + text = (char*)obj_get_type_name(obj); + if(update) + ret = ATTR_TYPE_STR_READONLY; + else + ret = ATTR_TYPE_STR; + break; + + // case ATTR_MODE: + // case ATTR_OPTIONS: + // case ATTR_BTN_POS: + + case ATTR_ACTION: + if(update) + obj->user_data.actionid = Parser::get_action_id(payload); + else + val = obj->user_data.actionid; + ret = ATTR_TYPE_INT; + break; + + // case ATTR_SYMBOL: + // (update) ? lv_dropdown_set_symbol(obj, payload) : attr_out_str(obj, attr, + // lv_dropdown_get_symbol(obj)); return true; + + case ATTR_DELETE: + case ATTR_CLEAR: + case ATTR_TO_FRONT: + case ATTR_TO_BACK: + case ATTR_OPEN: + case ATTR_CLOSE: + ret = attribute_common_method(obj, attr_hash, attribute, payload); + break; + + case ATTR_COLS: + case ATTR_ROWS: + case ATTR_AUTO_CLOSE: + case ATTR_SPEED: + case ATTR_ANIM_TIME: + case ATTR_ANIM_SPEED: + case ATTR_ANGLE: + case ATTR_ROTATION: + case ATTR_START_VALUE: + case ATTR_START_ANGLE: + case ATTR_END_ANGLE: + case ATTR_START_ANGLE1: + case ATTR_END_ANGLE1: + case ATTR_COUNT: + case ATTR_BTN_POS: + val = strtol(payload, nullptr, DEC); + ret = specific_int_attribute(obj, attr_hash, val, update); + break; + + case ATTR_OFFSET_X: + case ATTR_OFFSET_Y: + case ATTR_MAX_HEIGHT: + val = strtol(payload, nullptr, DEC); + ret = specific_coord_attribute(obj, attr_hash, val, update); + break; + + case ATTR_ADJUSTABLE: + case ATTR_ONE_CHECK: + case ATTR_AUTO_SIZE: + case ATTR_SHOW_SELECTED: + val = Parser::is_true(payload); + ret = specific_bool_attribute(obj, attr_hash, val, update); + break; + + case ATTR_NEXT: + case ATTR_PREV: + case ATTR_BACK: + val = strtol(payload, nullptr, DEC); + ret = specific_page_attribute(obj, attr_hash, val, update); + break; + + case ATTR_DIRECTION: + val = strtol(payload, nullptr, DEC); + ret = special_attribute_direction(obj, attr_hash, val, update); + break; + + case ATTR_SRC: + ret = special_attribute_src(obj, payload, &text, update); + break; + + default: { + break; // not found + } } -} \ No newline at end of file + + if(ret == ATTR_NOT_FOUND) { + switch(obj_get_type(obj)) { // Properties by object type + + case LV_HASP_ARC: + val = strtol(payload, nullptr, DEC); + ret = hasp_process_arc_attribute(obj, attr_hash, val, update); + break; + + case LV_HASP_GAUGE: + val = strtol(payload, nullptr, DEC); + ret = hasp_process_gauge_attribute(obj, attr_hash, val, update); + break; + + case LV_HASP_LINEMETER: + val = strtol(payload, nullptr, DEC); + ret = hasp_process_lmeter_attribute(obj, attr_hash, val, update); + break; + + case LV_HASP_CPICKER: + if(attr_hash == ATTR_COLOR) { + if(update) { + lv_color32_t c; + if(Parser::haspPayloadToColor(payload, c)) + lv_cpicker_set_color(obj, lv_color_make(c.ch.red, c.ch.green, c.ch.blue)); + } else { + color = lv_cpicker_get_color(obj); + } + ret = ATTR_TYPE_COLOR; + } + break; + + default: + break; // not found + } + } + + if(ret == ATTR_NOT_FOUND) { + bool result; + hasp_local_style_attr(obj, attribute, attr_hash, payload, update, result); + if(result) { + ret = ATTR_TYPE_METHOD_OK; + } else { + ret = ATTR_NOT_FOUND; + } + LOG_VERBOSE(TAG_ATTR, "%s %d ret:%d", __FILE__, __LINE__, ret); + } + + // Positive return codes have returned a value, negative are warnings + if(update && ret > 0) return; // done + + // Output the returned value or warning + switch(ret) { + case ATTR_NOT_FOUND: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_UNKNOWN " (%d)"), attribute, attr_hash); + break; + + case ATTR_TYPE_INT_READONLY: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_READ_ONLY), attribute); + case ATTR_TYPE_INT: + attr_out_int(obj, attribute, val); + break; + + case ATTR_TYPE_BOOL_READONLY: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_READ_ONLY), attribute); + case ATTR_TYPE_BOOL: + attr_out_bool(obj, attribute, val); + break; + + case ATTR_TYPE_STR_READONLY: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_READ_ONLY), attribute); + case ATTR_TYPE_STR: + attr_out_str(obj, attribute, text); + break; + + case ATTR_TYPE_COLOR_INVALID: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_COLOR_INVALID), payload); + break; + + case ATTR_TYPE_COLOR: + attr_out_color(obj, attribute, color); + break; + + case ATTR_TYPE_ALIGN_INVALID: + LOG_WARNING(TAG_ATTR, F(D_ATTRIBUTE_ALIGN_INVALID), payload); + break; + + case ATTR_TYPE_ALIGN: + case ATTR_TYPE_DIRECTION_CLOCK: + case ATTR_TYPE_DIRECTION_XY: + attr_out_int(obj, attribute, val); + break; + + case ATTR_TYPE_METHOD_INVALID_FOR_PAGE: + LOG_ERROR(TAG_ATTR, F(D_ATTRIBUTE_PAGE_METHOD_INVALID), attribute); + case ATTR_TYPE_METHOD_OK: + break; + + default: + LOG_ERROR(TAG_ATTR, F(D_ERROR_UNKNOWN " (%d)"), ret); + } +} diff --git a/src/hasp/hasp_attribute.h b/src/hasp/hasp_attribute.h index f08b1a2b..f35cac27 100644 --- a/src/hasp/hasp_attribute.h +++ b/src/hasp/hasp_attribute.h @@ -4,27 +4,22 @@ #ifndef HASP_ATTR_SET_H #define HASP_ATTR_SET_H -#include "lvgl.h" -#if LVGL_VERSION_MAJOR != 7 -#include "../lv_components.h" -#endif - -#include "hasp_conf.h" -#include "hasp.h" -#include "hasp_object.h" +#include "hasplib.h" #ifdef __cplusplus extern "C" { #endif lv_chart_series_t* my_chart_get_series(lv_obj_t* chart, uint8_t ser_num); -void my_obj_set_value_str_txt(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text); +void my_obj_set_value_str_text(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text); void my_btnmatrix_map_clear(lv_obj_t* obj); +void my_msgbox_map_clear(lv_obj_t* obj); void line_clear_points(lv_obj_t* obj); void hasp_process_obj_attribute(lv_obj_t* obj, const char* attr_p, const char* payload, bool update); -bool hasp_process_obj_attribute_val(lv_obj_t* obj, const char* attr, int16_t intval, bool booval, bool update); + +bool attribute_set_normalized_value(lv_obj_t* obj, hasp_update_value_t& value); void attr_out_str(lv_obj_t* obj, const char* attribute, const char* data); void attr_out_int(lv_obj_t* obj, const char* attribute, int32_t val); @@ -34,6 +29,106 @@ void attr_out_color(lv_obj_t* obj, const char* attribute, lv_color_t color); } /* extern "C" */ #endif +typedef enum { + ATTR_RANGE_ERROR = -9, + ATTR_TYPE_METHOD_INVALID_FOR_PAGE = -8, + ATTR_TYPE_ALIGN_INVALID = -5, + ATTR_TYPE_COLOR_INVALID = -4, + ATTR_TYPE_STR_READONLY = -3, + ATTR_TYPE_BOOL_READONLY = -2, + ATTR_TYPE_INT_READONLY = -1, + ATTR_NOT_FOUND = 0, + ATTR_TYPE_INT, + ATTR_TYPE_BOOL, + ATTR_TYPE_STR, + ATTR_TYPE_COLOR, + ATTR_TYPE_ALIGN, + ATTR_TYPE_DIRECTION_XY, + ATTR_TYPE_DIRECTION_CLOCK, + ATTR_TYPE_METHOD_OK, + +} hasp_attribute_type_t; + +struct hasp_attr_update_bool_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, bool); + bool (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update_uint16_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, uint16_t); + uint16_t (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update_lv_anim_value_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, lv_anim_value_t); + lv_anim_value_t (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update_int16_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, int16_t); + int16_t (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update_uint8_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, uint8_t); + uint8_t (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update8_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, uint8_t); + uint8_t (*get)(const lv_obj_t*); +}; + +struct hasp_attr_update_uint16_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, uint16_t); + uint16_t (*get)(lv_obj_t*); +}; + +struct hasp_attr_update_bool_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, bool); + bool (*get)(lv_obj_t*); +}; + +struct hasp_attr_update_lv_coord_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, lv_coord_t); + lv_coord_t (*get)(lv_obj_t*); +}; + +struct hasp_attr_update_char_const_t +{ + lv_hasp_obj_type_t obj_type; + uint16_t hash; + void (*set)(lv_obj_t*, const char*); + const char* (*get)(const lv_obj_t*); +}; + #define _HASP_ATTRIBUTE(prop_name, func_name, value_type) \ static inline void attribute_##func_name(lv_obj_t* obj, uint8_t part, lv_state_t state, bool update, \ const char* attr, value_type val) \ @@ -266,7 +361,6 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) #define ATTR_BACK 57799 /* Object Attributes */ -#define ATTR_COMMENT 62559 #define ATTR_X 120 #define ATTR_Y 121 #define ATTR_W 119 @@ -292,10 +386,15 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) #define ATTR_TEXT 53869 #define ATTR_SRC 4964 #define ATTR_ID 6715 +#define ATTR_EXT_CLICK_H 46643 +#define ATTR_EXT_CLICK_V 46657 #define ATTR_ANIM_TIME 59451 +#define ATTR_ANIM_SPEED 281 +#define ATTR_START_VALUE 11828 // methods #define ATTR_DELETE 50027 +#define ATTR_CLEAR 1069 #define ATTR_TO_FRONT 44741 #define ATTR_TO_BACK 24555 @@ -326,6 +425,25 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) // Buttonmatrix #define ATTR_ONE_CHECK 45935 +// Tabview +#define ATTR_BTN_POS 35697 +#define ATTR_COUNT 29103 + +// Msgbox +#define ATTR_MODAL 7405 +#define ATTR_AUTO_CLOSE 7880 + +// Image +#define ATTR_OFFSET_X 65388 +#define ATTR_OFFSET_Y 65389 +#define ATTR_AUTO_SIZE 63729 + +// Spinner +#define ATTR_SPEED 14375 +#define ATTR_THICKNESS 24180 +//#define ATTR_ARC_LENGTH 755 - use ATTR_ANGLE +// #define ATTR_DIRECTION 32415 - see Dropdown + /* hasp user data */ #define ATTR_ACTION 42102 #define ATTR_TRANSITION 10933 @@ -333,4 +451,11 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) #define ATTR_OBJID 41010 #define ATTR_OBJ 53623 +#define ATTR_TEXT_MAC 38107 +#define ATTR_TEXT_IP 41785 +#define ATTR_TEXT_HOSTNAME 10125 +#define ATTR_TEXT_MODEL 54561 +#define ATTR_TEXT_VERSION 60178 +#define ATTR_TEXT_SSID 62981 + #endif diff --git a/src/hasp/hasp_attribute_helper.h b/src/hasp/hasp_attribute_helper.h new file mode 100644 index 00000000..0cc2cc15 --- /dev/null +++ b/src/hasp/hasp_attribute_helper.h @@ -0,0 +1,532 @@ +/* MIT License - Copyright (c) 2019-2021 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#include "hasplib.h" + +const char* my_tabview_get_tab_name(const lv_obj_t* tabview, uint16_t id) +{ + if(id >= lv_tabview_get_tab_count(tabview)) return NULL; + + lv_tabview_ext_t* ext = (lv_tabview_ext_t*)lv_obj_get_ext_attr(tabview); + return ext->tab_name_ptr[id]; +} + +// OK - this function is missing in lvgl +static uint8_t my_roller_get_visible_row_count(const lv_obj_t* roller) +{ + const lv_font_t* font = lv_obj_get_style_text_font(roller, LV_ROLLER_PART_BG); + lv_style_int_t line_space = lv_obj_get_style_text_line_space(roller, LV_ROLLER_PART_BG); + lv_coord_t h = lv_obj_get_height(roller); + + if((lv_font_get_line_height(font) + line_space) != 0) + return (uint8_t)(h / (lv_font_get_line_height(font) + line_space)); + else + return 0; +} + +// OK - this function is not const in lvgl and doesn't return 0 +static uint16_t my_msgbox_stop_auto_close(const lv_obj_t* obj) +{ + lv_msgbox_stop_auto_close((lv_obj_t*)obj); + return 0; +} + +// OK - this function is not const in lvgl +static bool my_arc_get_adjustable(const lv_obj_t* arc) +{ + return lv_arc_get_adjustable((lv_obj_t*)arc); +} + +// OK - we need to change the event handler too +static void my_arc_set_adjustable(lv_obj_t* arc, bool toggle) +{ + lv_arc_set_adjustable(arc, toggle); + lv_obj_set_event_cb(arc, toggle ? slider_event_handler : generic_event_handler); +} + +// OK - this function is missing in lvgl +static inline uint16_t my_arc_get_rotation(lv_obj_t* arc) +{ + lv_arc_ext_t* ext = (lv_arc_ext_t*)lv_obj_get_ext_attr(arc); + return ext->rotation_angle; +} + +// OK - this function is missing in lvgl +static inline int16_t my_chart_get_min_value(lv_obj_t* chart) +{ + lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); + return ext->ymin[LV_CHART_AXIS_PRIMARY_Y]; +} + +// OK - this function is missing in lvgl +static inline int16_t my_chart_get_max_value(lv_obj_t* chart) +{ + lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); + return ext->ymax[LV_CHART_AXIS_PRIMARY_Y]; +} + +lv_chart_series_t* my_chart_get_series(lv_obj_t* chart, uint8_t ser_num) +{ + lv_chart_ext_t* ext = (lv_chart_ext_t*)lv_obj_get_ext_attr(chart); + lv_chart_series_t* ser = (lv_chart_series_t*)_lv_ll_get_tail(&ext->series_ll); + while(ser_num > 0 && ser) { + ser = (lv_chart_series_t*)_lv_ll_get_prev(&ext->series_ll, ser); + ser_num--; + } + return ser; +} + +// OK +static inline lv_color_t haspLogColor(lv_color_t color) +{ + // uint8_t r = (LV_COLOR_GET_R(color) * 263 + 7) >> 5; + // uint8_t g = (LV_COLOR_GET_G(color) * 259 + 3) >> 6; + // uint8_t b = (LV_COLOR_GET_B(color) * 263 + 7) >> 5; + // LOG_VERBOSE(TAG_ATTR,F("Color: R%u G%u B%u"), r, g, b); + return color; +} + +void my_bar_set_start_value(lv_obj_t* bar, int16_t start_value) +{ + lv_bar_set_start_value(bar, start_value, true); +} + +void my_slider_set_left_value(lv_obj_t* bar, int16_t start_value) +{ + lv_slider_set_left_value(bar, start_value, true); +} + +lv_coord_t my_dropdown_get_max_height(lv_obj_t* obj) +{ + return lv_dropdown_get_max_height(obj); +} + +// OK +lv_obj_t* FindButtonLabel(lv_obj_t* btn) +{ + if(btn) { + lv_obj_t* label = lv_obj_get_child_back(btn, NULL); + if(label) { + if(obj_check_type(label, LV_HASP_LABEL)) { + return label; + } + + } else { + LOG_ERROR(TAG_ATTR, F("FindButtonLabel NULL Pointer encountered")); + } + } else { + LOG_WARNING(TAG_ATTR, F("Button not defined")); + } + return NULL; +} + +// OK - lvgl does not return a const char * +static const char* my_label_get_text(const lv_obj_t* label) +{ + return lv_label_get_text(label); // library does not return const +} + +static void my_label_set_text(lv_obj_t* label, const char* text) +{ + if(text[0] == '%') { + uint16_t hash = Parser::get_sdbm(text); + size_t len = strlen(text); + const char* static_text = NULL; + + switch(hash) { + case ATTR_TEXT_MAC: + if(len == 4) break; + break; + +#if HASP_USE_WIFI > 0 + case ATTR_TEXT_SSID: + if(len == 6) static_text = wifi_get_ssid(); + break; + + case ATTR_TEXT_IP: + if(len == 4) static_text = wifi_get_ip_address(); + break; +#endif + case ATTR_TEXT_HOSTNAME: + if(len == 10) static_text = haspDevice.get_hostname(); + break; + + case ATTR_TEXT_MODEL: + if(len == 7) static_text = haspDevice.get_model(); + break; + + case ATTR_TEXT_VERSION: + if(len == 9) static_text = haspDevice.get_version(); + break; + } + + if(static_text) { + lv_label_set_text_static(label, static_text); + return; + } + } + + lv_label_set_text(label, text); +} + +// OK +static const char* my_btn_get_text(const lv_obj_t* obj) +{ + if(!obj) { + LOG_WARNING(TAG_ATTR, F("Button not defined")); + return NULL; + } + + lv_obj_t* label = lv_obj_get_child_back(obj, NULL); + if(label) { +#if 1 + if(obj_check_type(label, LV_HASP_LABEL)) return lv_label_get_text(label); + +#else + lv_obj_type_t list; + lv_obj_get_type(label, &list); + + if(obj_check_type(list.type[0], LV_HASP_LABEL)) { + text = lv_label_get_text(label); + return true; + } +#endif + + } else { + LOG_WARNING(TAG_ATTR, F("my_btn_get_text NULL Pointer encountered")); + } + + return NULL; +} + +// OK +static inline void my_btn_set_text(lv_obj_t* obj, const char* value) +{ + lv_obj_t* label = FindButtonLabel(obj); + if(label) { + my_label_set_text(label, value); + } +} + +/** + * Set a new value_str for an object. Memory will be allocated to store the text by the object. + * @param obj pointer to a object + * @param text '\0' terminated character string. NULL to refresh with the current text. + */ +void my_obj_set_value_str_text(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text) +{ + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + + const void* value_str_p = lv_obj_get_style_value_str(obj, part); + lv_obj_invalidate(obj); + + if(text == NULL || text[0] == 0) { + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + lv_obj_set_style_local_value_str(obj, part, state, NULL); + lv_mem_free(value_str_p); + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + return; + } + + LV_ASSERT_STR(text); + + if(value_str_p == NULL) { + /*Get the size of the text*/ + size_t len = strlen(text) + 1; + + /*Allocate space for the new text*/ + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + value_str_p = (char*)lv_mem_alloc(len); + LV_ASSERT_MEM(value_str_p); + if(value_str_p == NULL) return; + + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + strncpy((char*)value_str_p, text, len); + lv_obj_set_style_local_value_str(obj, part, state, (char*)value_str_p); + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + return; + } + + // lv_obj_set_style_local_value_str(obj, part, state, str_p); + + if(value_str_p == text) { + /*If set its own text then reallocate it (maybe its size changed)*/ + LOG_DEBUG(TAG_ATTR, "%s %d", __FILE__, __LINE__); + return; // don't touch the data + + // value_str_p = lv_mem_realloc(value_str_p, strlen(text) + 1); + + // LV_ASSERT_MEM(value_str_p); + // if(value_str_p == NULL) return; + } else { + /*Free the old text*/ + if(value_str_p != NULL) { + // LOG_DEBUG(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + lv_mem_free(value_str_p); + value_str_p = NULL; + // LOG_DEBUG(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); + } + + /*Get the size of the text*/ + size_t len = strlen(text) + 1; + + /*Allocate space for the new text*/ + value_str_p = lv_mem_alloc(len); + LV_ASSERT_MEM(value_str_p); + if(value_str_p != NULL) strcpy((char*)value_str_p, text); + lv_obj_set_style_local_value_str(obj, part, state, (char*)value_str_p); + } + + // LOG_VERBOSE(TAG_ATTR, F("%s %d"), __FILE__, __LINE__); +} + +void my_tabview_set_text(lv_obj_t* obj, const char* payload) +{ + uint16_t id = lv_tabview_get_tab_act(obj); + + if(id < lv_tabview_get_tab_count(obj)) { + lv_tabview_set_tab_name(obj, id, (char*)payload); + } +} + +const char* my_tabview_get_text(const lv_obj_t* obj) +{ + uint16_t id = lv_tabview_get_tab_act(obj); + + if(id < lv_tabview_get_tab_count(obj)) { + return my_tabview_get_tab_name(obj, id); + } else { + return NULL; + } +} + +void my_tab_set_text(lv_obj_t* obj, const char* payload) +{ + lv_obj_t* content = lv_obj_get_parent(obj->parent); // 2 levels up + if(!content) return LOG_WARNING(TAG_ATTR, F("content not found")); + + lv_obj_t* tabview = lv_obj_get_parent(content); // 3rd level up + if(!tabview) return LOG_WARNING(TAG_ATTR, F("Tabview not found")); + + if(!obj_check_type(tabview, LV_HASP_TABVIEW)) + return LOG_WARNING(TAG_ATTR, F("LV_HASP_TABVIEW not found %d"), obj_get_type(tabview)); + + for(uint16_t id = 0; id < lv_tabview_get_tab_count(tabview); id++) { + if(obj == lv_tabview_get_tab(tabview, id)) { + lv_tabview_set_tab_name(tabview, id, (char*)payload); + return; + } + } + LOG_WARNING(TAG_ATTR, F("Tab not found")); +} + +const char* my_tab_get_text(const lv_obj_t* obj) +{ + lv_obj_t* content = lv_obj_get_parent(obj->parent); // 2 levels up + if(!content) { + LOG_WARNING(TAG_ATTR, F("content not found")); + return NULL; + } + + lv_obj_t* tabview = lv_obj_get_parent(content); // 3rd level up + if(!tabview) { + LOG_WARNING(TAG_ATTR, F("Tabview not found")); + return NULL; + } + + if(!obj_check_type(tabview, LV_HASP_TABVIEW)) { + LOG_WARNING(TAG_ATTR, F("LV_HASP_TABVIEW not found %d"), obj_get_type(tabview)); + return NULL; + } + + for(uint16_t id = 0; id < lv_tabview_get_tab_count(tabview); id++) { + if(obj == lv_tabview_get_tab(tabview, id)) { + return my_tabview_get_tab_name(tabview, id); + } + } + LOG_WARNING(TAG_ATTR, F("Tab not found")); + return NULL; +} + +static void gauge_format_10(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) +{ + snprintf(buf, bufsize, PSTR("%d"), value / 10); +} + +static void gauge_format_100(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) +{ + snprintf(buf, bufsize, PSTR("%d"), value / 100); +} + +static void gauge_format_1k(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) +{ + snprintf(buf, bufsize, PSTR("%d"), value / 1000); +} + +static void gauge_format_10k(lv_obj_t* gauge, char* buf, int bufsize, int32_t value) +{ + snprintf(buf, bufsize, PSTR("%d"), value / 10000); +} + +#if 0 +static bool attribute_lookup_lv_property(uint16_t hash, uint8_t * prop) +{ + struct prop_hash_map + { + uint16_t hash; + uint8_t prop; + }; + + /* in order of prevalence */ + prop_hash_map props[] = { + {ATTR_PAD_TOP, LV_STYLE_PAD_TOP & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_WIDTH, LV_STYLE_BORDER_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_OUTLINE_WIDTH, LV_STYLE_OUTLINE_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_LETTER_SPACE, LV_STYLE_VALUE_LETTER_SPACE & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_LETTER_SPACE, LV_STYLE_TEXT_LETTER_SPACE & LV_STYLE_PROP_ALL}, + {ATTR_LINE_WIDTH, LV_STYLE_LINE_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_TIME, LV_STYLE_TRANSITION_TIME & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_WIDTH, LV_STYLE_SCALE_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_RADIUS, LV_STYLE_RADIUS & LV_STYLE_PROP_ALL}, + {ATTR_PAD_BOTTOM, LV_STYLE_PAD_BOTTOM & LV_STYLE_PROP_ALL}, + {ATTR_BG_MAIN_STOP, LV_STYLE_BG_MAIN_STOP & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_SIDE, LV_STYLE_BORDER_SIDE & LV_STYLE_PROP_ALL}, + {ATTR_OUTLINE_PAD, LV_STYLE_OUTLINE_PAD & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_REPEAT, LV_STYLE_PATTERN_REPEAT & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_LINE_SPACE, LV_STYLE_VALUE_LINE_SPACE & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_LINE_SPACE, LV_STYLE_TEXT_LINE_SPACE & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_DELAY, LV_STYLE_TRANSITION_DELAY & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_BORDER_WIDTH, LV_STYLE_SCALE_BORDER_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_CLIP_CORNER, LV_STYLE_CLIP_CORNER & LV_STYLE_PROP_ALL}, + {ATTR_PAD_LEFT, LV_STYLE_PAD_LEFT & LV_STYLE_PROP_ALL}, + {ATTR_BG_GRAD_STOP, LV_STYLE_BG_GRAD_STOP & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_DECOR, LV_STYLE_TEXT_DECOR & LV_STYLE_PROP_ALL}, + {ATTR_LINE_DASH_WIDTH, LV_STYLE_LINE_DASH_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_1, LV_STYLE_TRANSITION_PROP_1 & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_END_BORDER_WIDTH, LV_STYLE_SCALE_END_BORDER_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_SIZE, LV_STYLE_SIZE & LV_STYLE_PROP_ALL}, + {ATTR_PAD_RIGHT, LV_STYLE_PAD_RIGHT & LV_STYLE_PROP_ALL}, + {ATTR_BG_GRAD_DIR, LV_STYLE_BG_GRAD_DIR & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_POST, LV_STYLE_BORDER_POST & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_OFS_X, LV_STYLE_VALUE_OFS_X & LV_STYLE_PROP_ALL}, + {ATTR_LINE_DASH_GAP, LV_STYLE_LINE_DASH_GAP & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_2, LV_STYLE_TRANSITION_PROP_2 & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_END_LINE_WIDTH, LV_STYLE_SCALE_END_LINE_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_PAD_INNER, LV_STYLE_PAD_INNER & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_OFS_Y, LV_STYLE_VALUE_OFS_Y & LV_STYLE_PROP_ALL}, + {ATTR_LINE_ROUNDED, LV_STYLE_LINE_ROUNDED & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_3, LV_STYLE_TRANSITION_PROP_3 & LV_STYLE_PROP_ALL}, + {ATTR_TRANSFORM_HEIGHT, LV_STYLE_TRANSFORM_HEIGHT & LV_STYLE_PROP_ALL}, + // {ATTR_MARGIN_TOP, LV_STYLE_MARGIN_TOP & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_ALIGN, LV_STYLE_VALUE_ALIGN & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_4, LV_STYLE_TRANSITION_PROP_4 & LV_STYLE_PROP_ALL}, + // {ATTR_TRANSFORM_ANGLE, LV_STYLE_TRANSFORM_ANGLE & LV_STYLE_PROP_ALL}, + // {ATTR_MARGIN_BOTTOM, LV_STYLE_MARGIN_BOTTOM & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_5, LV_STYLE_TRANSITION_PROP_5 & LV_STYLE_PROP_ALL}, + // {ATTR_TRANSFORM_ZOOM, LV_STYLE_TRANSFORM_ZOOM & LV_STYLE_PROP_ALL}, + // {ATTR_MARGIN_LEFT, LV_STYLE_MARGIN_LEFT & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PROP_6, LV_STYLE_TRANSITION_PROP_6 & LV_STYLE_PROP_ALL}, + // {ATTR_MARGIN_RIGHT, LV_STYLE_MARGIN_RIGHT & LV_STYLE_PROP_ALL}, + {ATTR_BG_COLOR, LV_STYLE_BG_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_COLOR, LV_STYLE_BORDER_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_OUTLINE_COLOR, LV_STYLE_OUTLINE_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_RECOLOR, LV_STYLE_PATTERN_RECOLOR & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_COLOR, LV_STYLE_VALUE_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_COLOR, LV_STYLE_TEXT_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_LINE_COLOR, LV_STYLE_LINE_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_IMAGE_RECOLOR, LV_STYLE_IMAGE_RECOLOR & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_GRAD_COLOR, LV_STYLE_SCALE_GRAD_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_BG_GRAD_COLOR, LV_STYLE_BG_GRAD_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_SEL_COLOR, LV_STYLE_TEXT_SEL_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_SCALE_END_COLOR, LV_STYLE_SCALE_END_COLOR & LV_STYLE_PROP_ALL}, + // {ATTR_TEXT_SEL_BG_COLOR, LV_STYLE_TEXT_SEL_BG_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_OPA_SCALE, LV_STYLE_OPA_SCALE & LV_STYLE_PROP_ALL}, + {ATTR_BG_OPA, LV_STYLE_BG_OPA & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_OPA, LV_STYLE_BORDER_OPA & LV_STYLE_PROP_ALL}, + {ATTR_OUTLINE_OPA, LV_STYLE_OUTLINE_OPA & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_OPA, LV_STYLE_PATTERN_OPA & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_OPA, LV_STYLE_VALUE_OPA & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_OPA, LV_STYLE_TEXT_OPA & LV_STYLE_PROP_ALL}, + {ATTR_LINE_OPA, LV_STYLE_LINE_OPA & LV_STYLE_PROP_ALL}, + {ATTR_IMAGE_OPA, LV_STYLE_IMAGE_OPA & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_RECOLOR_OPA, LV_STYLE_PATTERN_RECOLOR_OPA & LV_STYLE_PROP_ALL}, + {ATTR_IMAGE_RECOLOR_OPA, LV_STYLE_IMAGE_RECOLOR_OPA & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_IMAGE, LV_STYLE_PATTERN_IMAGE & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_FONT, LV_STYLE_VALUE_FONT & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_FONT, LV_STYLE_TEXT_FONT & LV_STYLE_PROP_ALL}, + {ATTR_TRANSITION_PATH, LV_STYLE_TRANSITION_PATH & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_STR, LV_STYLE_VALUE_STR & LV_STYLE_PROP_ALL}, + +#if LV_USE_SHADOW + {ATTR_SHADOW_WIDTH, LV_STYLE_SHADOW_WIDTH & LV_STYLE_PROP_ALL}, + {ATTR_SHADOW_OFS_X, LV_STYLE_SHADOW_OFS_X & LV_STYLE_PROP_ALL}, + {ATTR_SHADOW_OFS_Y, LV_STYLE_SHADOW_OFS_Y & LV_STYLE_PROP_ALL}, + {ATTR_SHADOW_SPREAD, LV_STYLE_SHADOW_SPREAD & LV_STYLE_PROP_ALL}, + {ATTR_SHADOW_COLOR, LV_STYLE_SHADOW_COLOR & LV_STYLE_PROP_ALL}, + {ATTR_SHADOW_OPA, LV_STYLE_SHADOW_OPA & LV_STYLE_PROP_ALL}, +#endif + +#if LV_USE_BLEND_MODES && LV_USE_SHADOW + {ATTR_SHADOW_BLEND_MODE, LV_STYLE_SHADOW_BLEND_MODE & LV_STYLE_PROP_ALL}, +#endif + +#if LV_USE_BLEND_MODES + {ATTR_BG_BLEND_MODE, LV_STYLE_BG_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_PATTERN_BLEND_MODE, LV_STYLE_PATTERN_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_IMAGE_BLEND_MODE, LV_STYLE_IMAGE_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_LINE_BLEND_MODE, LV_STYLE_LINE_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_BORDER_BLEND_MODE, LV_STYLE_BORDER_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_OUTLINE_BLEND_MODE, LV_STYLE_OUTLINE_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_VALUE_BLEND_MODE, LV_STYLE_VALUE_BLEND_MODE & LV_STYLE_PROP_ALL}, + {ATTR_TEXT_BLEND_MODE, LV_STYLE_TEXT_BLEND_MODE & LV_STYLE_PROP_ALL}, +#endif + }; + + for(uint32_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { + if(props[i].hash == hash) { + *prop = props[1].prop; + LOG_WARNING(TAG_ATTR, F("%d found and has propery %d"), hash, props[i].prop); + return true; + } + } + LOG_ERROR(TAG_ATTR, F("%d has no property id"), hash); + return false; +} + +static bool attribute_get_lv_property() +{ + lv_res_t res _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, void * res); + return res == LV_RES_OK +} + +static bool attribute_set_lv_property() +{ + lv_res_t res _lv_style_get_int(const lv_style_t * style, lv_style_property_t prop, void * res); + return res == LV_RES_OK +} + +static bool attribute_update_lv_property(lv_obj_t * obj, const char * attr_p, uint16_t attr_hash, const char * payload, + bool update) +{ + uint8_t prop; + uint8_t prop_type; + + // convert sdbm hash to lv property number + if(!attribute_lookup_lv_property(attr_hash, &prop)) return false; + + // find the parameter type for this property + prop_type = prop & 0xF; + + if(prop_type < LV_STYLE_ID_COLOR) { + if(update) { + _lv_obj_set_style_local_int(obj, part, prop | (state << LV_STYLE_STATE_POS), atoi(payload)) + } else { + attr_out_str(obj, attr_p, lv_obj_get_style_value_str(obj, part)); + } + } else if(prop_type < LV_STYLE_ID_OPA) { + } else if(prop_type < LV_STYLE_ID_PTR) { + } else { + } +} +#endif diff --git a/src/hasp/hasp_dispatch.cpp b/src/hasp/hasp_dispatch.cpp index 2fe13d47..cbb92472 100644 --- a/src/hasp/hasp_dispatch.cpp +++ b/src/hasp/hasp_dispatch.cpp @@ -1,8 +1,6 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include - //#include "ArduinoLog.h" #include "hasplib.h" @@ -38,20 +36,13 @@ #include "hasp_config.h" #endif -extern uint8_t hasp_sleep_state; - dispatch_conf_t dispatch_setings = {.teleperiod = 300}; -uint32_t dispatchLastMillis; -uint8_t nCommands = 0; -haspCommand_t commands[18]; +uint32_t dispatchLastMillis = -3000000; // force discovery +uint8_t nCommands = 0; +haspCommand_t commands[20]; -struct moodlight_t -{ - uint8_t power; - uint8_t r, g, b; -}; -moodlight_t moodlight; +moodlight_t moodlight = {.brightness = 255}; // static void dispatch_config(const char* topic, const char* payload); // void dispatch_group_value(uint8_t groupid, int16_t state, lv_obj_t * obj); @@ -73,10 +64,10 @@ void dispatch_state_subtopic(const char* subtopic, const char* payload) LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " %s => %s"), subtopic, payload); break; case MQTT_ERR_NO_CONN: - LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED)); + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED " %s => %s"), subtopic, payload); break; default: - LOG_ERROR(TAG_MQTT, F(D_ERROR_UNKNOWN)); + LOG_ERROR(TAG_MQTT, F(D_ERROR_UNKNOWN " %s => %s"), subtopic, payload); } #endif @@ -110,7 +101,7 @@ void dispatch_json_error(uint8_t tag, DeserializationError& jsonError) } // p[x].b[y].attr=value -static inline bool dispatch_parse_button_attribute(const char* topic_p, const char* payload) +static inline bool dispatch_parse_button_attribute(const char* topic_p, const char* payload, bool update) { long num; char* pEnd; @@ -155,99 +146,83 @@ static inline bool dispatch_parse_button_attribute(const char* topic_p, const ch if(*topic_p != '.') return false; // obligated seperator topic_p++; - hasp_process_attribute(pageid, objid, topic_p, payload); + hasp_process_attribute(pageid, objid, topic_p, payload, update); return true; - - /* - if(sscanf(topic_p, HASP_OBJECT_NOTATION ".", &pageid, &objid) == 2) { // Literal String - - // OK, continue below - - } else if(sscanf(topic_p, "p[%u].b[%u].", &pageid, &objid) == 2) { // Literal String - - // TODO: obsolete old syntax p[x].b[y]. - // OK, continue below - while(*topic_p++ != '.') { - // strip to '.' character - } - - } else { - return false; - } - - while(*topic_p != '.') { - if(*topic_p == 0) return false; // strip to '.' character - topic_p++; - } - - hasp_process_attribute((uint8_t)pageid, (uint8_t)objid, topic_p, payload); - return true; */ - - // String strPageId((char *)0); - // String strTemp((char *)0); - - // strPageId = strTopic.substring(2, strTopic.indexOf("]")); - // strTemp = strTopic.substring(strTopic.indexOf("]") + 1, strTopic.length()); - - // if(strTemp.startsWith(".b[")) { - // String strObjId((char *)0); - // String strAttr((char *)0); - - // strObjId = strTemp.substring(3, strTemp.indexOf("]")); - // strAttr = strTemp.substring(strTemp.indexOf("]") + 1, strTemp.length()); - // // debugPrintln(strPageId + " && " + strObjId + " && " + strAttr); - - // pageid = strPageId.toInt(); - // objid = strObjId.toInt(); - - // if(pageid >= 0 && pageid <= 255 && objid >= 0 && objid <= 255) { - // hasp_process_attribute(pageid, objid, strAttr.c_str(), payload); - // } // valid page - // } } static void dispatch_gpio(const char* topic, const char* payload) { #if HASP_USE_GPIO > 0 - int16_t val; - uint8_t pin; - - if(topic == strstr_P(topic, PSTR("relay"))) { - topic += 5; - val = Parser::is_true(payload); - - } else if(topic == strstr_P(topic, PSTR("led"))) { - topic += 3; - val = atoi(payload); - - } else if(topic == strstr_P(topic, PSTR("pwm"))) { - topic += 3; - val = atoi(payload); - - } else { - LOG_WARNING(TAG_MSGR, F("Invalid gpio %s"), topic); + if(!Parser::is_only_digits(topic)) { + LOG_WARNING(TAG_MSGR, F("Invalid pin %s"), topic); return; } - if(Parser::is_only_digits(topic)) { - pin = atoi(topic); - if(strlen(payload) > 0) { - gpio_set_value(pin, val); + uint8_t pin = atoi(topic); + + if(strlen(payload) > 0) { + + size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 128; + DynamicJsonDocument json(maxsize); + + // Note: Deserialization needs to be (const char *) so the objects WILL be copied + // this uses more memory but otherwise the mqtt receive buffer can get overwritten by the send buffer !! + DeserializationError jsonError = deserializeJson(json, payload); + json.shrinkToFit(); + + if(jsonError) { // Couldn't parse incoming JSON command + dispatch_json_error(TAG_MSGR, jsonError); } else { - gpio_get_value(pin); + + // Save the current state + int32_t state_value; + bool power_state; + bool updated = false; + + if(!gpio_get_pin_state(pin, power_state, state_value)) { + LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d can not be set"), pin); + return; + } + + JsonVariant state = json[F("state")]; + JsonVariant value = json[F("val")]; + + // Check if the state needs to change + if(!state.isNull() && power_state != state.as()) { + power_state = state.as(); + updated = true; + } + + if(!value.isNull() && state_value != value.as()) { + state_value = value.as(); + updated = true; + } + + // Set new state + if(updated && gpio_set_pin_state(pin, power_state, state_value)) { + return; // value was set and state output already + } else { + // output the new state to the log + } } - } else { - LOG_WARNING(TAG_MSGR, F("Invalid pin %s"), topic); } + + // just output this pin + if(!gpio_output_pin_state(pin)) { + LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d is not configured"), pin); + } + #endif } // objectattribute=value -void dispatch_command(const char* topic, const char* payload) +void dispatch_command(const char* topic, const char* payload, bool update) { /* ================================= Standard payload commands ======================================= */ + if(dispatch_parse_button_attribute(topic, payload, update)) return; // matched pxby.attr, first for speed + // check and execute commands from commands array for(int i = 0; i < nCommands; i++) { if(!strcasecmp_P(topic, commands[i].p_cmdstr)) { @@ -259,16 +234,14 @@ void dispatch_command(const char* topic, const char* payload) /* =============================== Not standard payload commands ===================================== */ - if(topic == strstr_P(topic, PSTR("gpio/"))) { + // if(topic == strstr_P(topic, PSTR("gpio/"))) { + if(topic == strstr_P(topic, PSTR("output"))) { - dispatch_gpio(topic + 5, payload); + dispatch_gpio(topic + 6, payload); // } else if(strcasecmp_P(topic, PSTR("screenshot")) == 0) { // guiTakeScreenshot("/screenshot.bmp"); // Literal String - } else if((topic[0] == 'p' || topic[0] == 'P') && dispatch_parse_button_attribute(topic, payload)) { - return; // matched pxby.attr - #if HASP_USE_CONFIG > 0 #if HASP_USE_WIFI > 0 @@ -292,7 +265,6 @@ void dispatch_command(const char* topic, const char* payload) #endif // HASP_USE_MQTT #endif // HASP_USE_CONFIG - } else { if(strlen(payload) == 0) { // dispatch_text_line(topic); // Could cause an infinite loop! @@ -302,30 +274,28 @@ void dispatch_command(const char* topic, const char* payload) } // Strip command/config prefix from the topic and process the payload -void dispatch_topic_payload(const char* topic, const char* payload) +void dispatch_topic_payload(const char* topic, const char* payload, bool update) { - // LOG_VERBOSE(TAG_MSGR,F("TOPIC: short topic: %s"), topic); - - if(!strcmp_P(topic, PSTR("command"))) { + if(!strcmp_P(topic, PSTR(MQTT_TOPIC_COMMAND))) { dispatch_text_line((char*)payload); return; } - if(topic == strstr_P(topic, PSTR("command/"))) { // startsWith command/ + if(topic == strstr_P(topic, PSTR(MQTT_TOPIC_COMMAND "/"))) { // startsWith command/ topic += 8u; - dispatch_command(topic, (char*)payload); + dispatch_command(topic, (char*)payload, update); return; } #if HASP_USE_CONFIG > 0 - if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith command/ + if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith config/ topic += 7u; dispatch_config(topic, (char*)payload); return; } #endif - dispatch_command(topic, (char*)payload); // dispatch as is + dispatch_command(topic, (char*)payload, update); // dispatch as is } // Parse one line of text and execute the command @@ -334,14 +304,16 @@ void dispatch_text_line(const char* cmnd) size_t pos1 = std::string(cmnd).find("="); size_t pos2 = std::string(cmnd).find(" "); size_t pos = 0; + bool update = false; // Find what comes first, ' ' or '=' - if(pos1 != std::string::npos) { - if(pos2 != std::string::npos) { + if(pos1 != std::string::npos) { // '=' found + if(pos2 != std::string::npos) { // ' ' found pos = (pos1 < pos2 ? pos1 : pos2); } else { pos = pos1; } + update = pos == pos1; // equal sign wins } else { pos = (pos2 != std::string::npos) ? pos2 : 0; @@ -356,40 +328,16 @@ void dispatch_text_line(const char* cmnd) memcpy(topic, cmnd, sizeof(topic) - 1); // topic is before '=', payload is after '=' position - LOG_TRACE(TAG_MSGR, F("%s=%s"), topic, cmnd + pos + 1); - dispatch_topic_payload(topic, cmnd + pos + 1); + update |= strlen(cmnd + pos + 1) > 0; // equal sign OR space with payload + LOG_TRACE(TAG_MSGR, update ? F("%s=%s") : F("%s%s"), topic, cmnd + pos + 1); + dispatch_topic_payload(topic, cmnd + pos + 1, update); } else { char empty_payload[1] = {0}; - LOG_TRACE(TAG_MSGR, F("%s=%s"), cmnd, empty_payload); - dispatch_topic_payload(cmnd, empty_payload); + LOG_TRACE(TAG_MSGR, cmnd); + dispatch_topic_payload(cmnd, empty_payload, false); } } -void dispatch_get_idle_state(uint8_t state, char* payload) -{ - switch(state) { - case HASP_SLEEP_LONG: - memcpy_P(payload, PSTR("long"), 5); - break; - case HASP_SLEEP_SHORT: - memcpy_P(payload, PSTR("short"), 6); - break; - default: - memcpy_P(payload, PSTR("off"), 4); - } -} - -// send idle state to the client -void dispatch_output_idle_state(uint8_t state) -{ - char topic[6]; - char payload[6]; - memcpy_P(topic, PSTR("idle"), 5); - - dispatch_get_idle_state(state, payload); - dispatch_state_subtopic(topic, payload); -} - // void dispatch_output_group_state(uint8_t groupid, uint16_t state) // { // char payload[64]; @@ -506,47 +454,20 @@ void dispatch_config(const char* topic, const char* payload) #endif // HASP_USE_CONFIG /********************************************** Output States ******************************************/ -/* -static inline void dispatch_state_msg(const __FlashStringHelper* subtopic, const char* payload) + +void dispatch_normalized_group_values(hasp_update_value_t& value) { + if(value.group == 0) return; -}*/ - -// void dispatch_group_onoff(uint8_t groupid, uint16_t eventid, lv_obj_t * obj) -// { -// if((eventid == HASP_EVENT_LONG) || (eventid == HASP_EVENT_HOLD)) return; // don't send repeat events - -// if(groupid >= 0) { -// bool state = Parser::get_event_state(eventid); -// gpio_set_group_onoff(groupid, state); -// object_set_normalized_group_value(groupid, eventid, obj); -// } - -// char payload[8]; -// Parser::get_event_name(eventid, payload, sizeof(payload)); -// // dispatch_output_group_state(groupid, payload); -// } - -// void dispatch_group_value(uint8_t groupid, int16_t state, lv_obj_t * obj) -// { -// if(groupid >= 0) { -// gpio_set_group_value(groupid, state); -// object_set_normalized_group_value(groupid, state, obj); -// } - -// char payload[8]; -// // dispatch_output_group_state(groupid, payload); -// } - -void dispatch_normalized_group_value(uint8_t groupid, lv_obj_t* obj, int16_t val, int16_t min, int16_t max) -{ - if(groupid == 0) return; - - LOG_VERBOSE(TAG_MSGR, F("GROUP %d value %d (%d-%d)"), groupid, val, min, max); #if HASP_USE_GPIO > 0 - gpio_set_normalized_group_value(groupid, val, min, max); // Update GPIO states + gpio_set_normalized_group_values(value); // Update GPIO states first +#endif + object_set_normalized_group_values(value); // Update onsreen objects except originating obj + + LOG_VERBOSE(TAG_MSGR, F("GROUP %d value %d (%d-%d)"), value.group, value.val, value.min, value.max); +#if HASP_USE_GPIO > 0 + gpio_output_group_values(value.group); // Output new gpio values #endif - object_set_normalized_group_value(groupid, obj, val, min, max); // Update onsreen objects } /********************************************** Native Commands ****************************************/ @@ -560,7 +481,7 @@ void dispatch_screenshot(const char*, const char* filename) memcpy_P(tempfile, PSTR("/screenshot.bmp"), sizeof(tempfile)); guiTakeScreenshot(tempfile); } else if(strlen(filename) > 31 || filename[0] != '/') { // Invalid filename - LOG_WARNING(TAG_MSGR, "Invalid filename %s", filename); + LOG_WARNING(TAG_MSGR, F("D_FILE_SAVE_FAILED"), filename); } else { // Valid filename guiTakeScreenshot(filename); } @@ -611,6 +532,9 @@ void dispatch_parse_json(const char*, const char* payload) } else if(json.is()) { // handle json as a single command dispatch_text_line(json.as()); + // } else if(json.is()) { // handle json as a single command + // dispatch_text_line(json.as()); + } else { LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND), payload); } @@ -623,7 +547,7 @@ void dispatch_parse_jsonl(std::istream& stream) #endif { uint8_t savedPage = haspPages.get(); - size_t line = 1; + uint16_t line = 1; DynamicJsonDocument jsonl(MQTT_MAX_PACKET_SIZE / 2 + 128); // max ~256 characters per line DeserializationError jsonError = deserializeJson(jsonl, stream); @@ -660,7 +584,7 @@ void dispatch_parse_jsonl(const char*, const char* payload) #endif } -void dispatch_output_current_page() +void dispatch_current_page() { char topic[8]; char payload[8]; @@ -674,19 +598,19 @@ void dispatch_output_current_page() void dispatch_page_next(lv_scr_load_anim_t animation) { haspPages.next(animation); - dispatch_output_current_page(); + dispatch_current_page(); } void dispatch_page_prev(lv_scr_load_anim_t animation) { haspPages.prev(animation); - dispatch_output_current_page(); + dispatch_current_page(); } void dispatch_page_back(lv_scr_load_anim_t animation) { haspPages.back(animation); - dispatch_output_current_page(); + dispatch_current_page(); } void dispatch_set_page(uint8_t pageid) @@ -697,13 +621,13 @@ void dispatch_set_page(uint8_t pageid) void dispatch_set_page(uint8_t pageid, lv_scr_load_anim_t animation) { haspPages.set(pageid, animation); - dispatch_output_current_page(); + dispatch_current_page(); } void dispatch_page(const char*, const char* page) { if(strlen(page) == 0) { - dispatch_output_current_page(); // No payload, send current page + dispatch_current_page(); // No payload, send current page return; } @@ -756,7 +680,7 @@ void dispatch_moodlight(const char* topic, const char* payload) // Set the current state if(strlen(payload) != 0) { - size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 512; + size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 128; DynamicJsonDocument json(maxsize); // Note: Deserialization needs to be (const char *) so the objects WILL be copied @@ -771,19 +695,20 @@ void dispatch_moodlight(const char* topic, const char* payload) if(!json[F("state")].isNull()) moodlight.power = Parser::is_true(json[F("state")].as().c_str()); - if(!json["r"].isNull()) moodlight.r = json["r"].as(); - if(!json["g"].isNull()) moodlight.g = json["g"].as(); - if(!json["b"].isNull()) moodlight.b = json["b"].as(); + if(!json["r"].isNull()) moodlight.rgbww[0] = json["r"].as(); + if(!json["g"].isNull()) moodlight.rgbww[1] = json["g"].as(); + if(!json["b"].isNull()) moodlight.rgbww[2] = json["b"].as(); + if(!json["brightness"].isNull()) moodlight.brightness = json["brightness"].as(); if(!json[F("color")].isNull()) { if(!json[F("color")]["r"].isNull()) { - moodlight.r = json[F("color")]["r"].as(); + moodlight.rgbww[0] = json[F("color")]["r"].as(); } if(!json[F("color")]["g"].isNull()) { - moodlight.g = json[F("color")]["g"].as(); + moodlight.rgbww[1] = json[F("color")]["g"].as(); } if(!json[F("color")]["b"].isNull()) { - moodlight.b = json[F("color")]["b"].as(); + moodlight.rgbww[2] = json[F("color")]["b"].as(); } // lv_color32_t color; // if(Parser::haspPayloadToColor(json[F("color")].as(), color)) { @@ -794,10 +719,7 @@ void dispatch_moodlight(const char* topic, const char* payload) } #if HASP_USE_GPIO > 0 - if(moodlight.power) - gpio_set_moodlight(moodlight.r, moodlight.g, moodlight.b); - else - gpio_set_moodlight(0, 0, 0); + gpio_set_moodlight(moodlight); #endif } } @@ -809,8 +731,9 @@ void dispatch_moodlight(const char* topic, const char* payload) snprintf_P( // buffer, sizeof(buffer), // PSTR("{\"state\":\"%s\",\"color\":\"#%02x%02x%02x\",\"r\":%u,\"g\":%u,\"b\":%u}"), - buffer, sizeof(buffer), PSTR("{\"state\":\"%s\",\"color\":{\"r\":%u,\"g\":%u,\"b\":%u}}"), - moodlight.power ? "ON" : "OFF", moodlight.r, moodlight.g, moodlight.b); + buffer, sizeof(buffer), PSTR("{\"state\":\"%s\",\"color\":{\"r\":%u,\"g\":%u,\"b\":%u,\"brightness\":%u}}"), + moodlight.power ? "ON" : "OFF", moodlight.rgbww[0], moodlight.rgbww[1], moodlight.rgbww[2], + moodlight.brightness); dispatch_state_subtopic(out_topic, buffer); } @@ -873,32 +796,66 @@ void dispatch_reboot(bool saveConfig) haspDevice.reboot(); } -void dispatch_current_state() -{ - dispatch_output_statusupdate(NULL, NULL); - dispatch_output_idle_state(hasp_sleep_state); - dispatch_output_current_page(); -} - /******************************************* Command Wrapper Functions *********************************/ // Periodically publish a JSON string indicating system status -void dispatch_output_statusupdate(const char*, const char*) +void dispatch_send_discovery(const char*, const char*) { #if HASP_USE_MQTT > 0 - char data[512]; + StaticJsonDocument<1024> doc; + + doc[F("node")] = haspDevice.get_hostname(); + doc[F("mdl")] = haspDevice.get_model(); + doc[F("mf")] = F(D_MANUFACTURER); + doc[F("hwid")] = haspDevice.get_hardware_id(); + doc[F("pages")] = haspPages.count(); + doc[F("sw")] = haspDevice.get_version(); + + JsonObject input = doc.createNestedObject(F("input")); + JsonArray relay = doc.createNestedArray(F("power")); + JsonArray led = doc.createNestedArray(F("light")); + JsonArray dimmer = doc.createNestedArray(F("dim")); + +#if HASP_USE_GPIO > 0 + gpio_discovery(input, relay, led, dimmer); +#endif + + char data[1024]; + size_t len = serializeJson(doc, data); + + switch(mqtt_send_discovery(data, len)) { + case MQTT_ERR_OK: + LOG_TRACE(TAG_MQTT_PUB, F(MQTT_TOPIC_DISCOVERY " => %s"), data); + break; + case MQTT_ERR_PUB_FAIL: + LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " " MQTT_TOPIC_DISCOVERY " => %s"), data); + break; + case MQTT_ERR_NO_CONN: + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED)); + break; + default: + LOG_ERROR(TAG_MQTT, F(D_ERROR_UNKNOWN)); + } + // dispatchLastMillis = millis(); + +#endif +} + +// Periodically publish a JSON string indicating system status +void dispatch_statusupdate(const char*, const char*) +{ +#if HASP_USE_MQTT > 0 + + char data[400]; char topic[16]; { char buffer[128]; - haspGetVersion(buffer, sizeof(buffer)); - dispatch_get_idle_state(hasp_sleep_state, topic); - snprintf_P(data, sizeof(data), - PSTR("{\"node\":\"%s\",\"manufacturer\":\"" D_MANUFACTURER - "\",\"model\":\"%s\",\"idle\":\"%s\",\"version\":\"%s\",\"uptime\":%lu,"), - haspDevice.get_hostname(), haspDevice.get_model(), topic, buffer, - (unsigned long)(millis() / 1000)); // \"status\":\"available\", + hasp_get_sleep_state(topic); + snprintf_P(data, sizeof(data), PSTR("{\"node\":\"%s\",\"idle\":\"%s\",\"version\":\"%s\",\"uptime\":%lu,"), + haspDevice.get_hostname(), topic, haspDevice.get_version(), + long(millis() / 1000)); // \"status\":\"available\", #if HASP_USE_WIFI > 0 || HASP_USE_ETHERNET > 0 network_get_statusupdate(buffer, sizeof(buffer)); @@ -930,10 +887,25 @@ void dispatch_output_statusupdate(const char*, const char*) dispatch_state_subtopic(topic, data); dispatchLastMillis = millis(); + /* if(updateEspAvailable) { + mqttStatusPayload += F("\"updateEspAvailable\":true,"); + } else { + mqttStatusPayload += F("\"updateEspAvailable\":false,"); + } + */ + #endif } -void dispatch_calibrate(const char* topic = NULL, const char* payload = NULL) +void dispatch_current_state() +{ + dispatch_statusupdate(NULL, NULL); + dispatch_idle(NULL, NULL); + dispatch_current_page(); + dispatch_send_discovery(NULL, NULL); +} + +void dispatch_calibrate(const char*, const char*) { guiCalibrate(); } @@ -949,6 +921,16 @@ void dispatch_sleep(const char*, const char*) hasp_enable_wakeup_touch(); } +void dispatch_idle(const char*, const char*) +{ + char topic[6]; + char payload[6]; + memcpy_P(topic, PSTR("idle"), 5); + + hasp_get_sleep_state(payload); + dispatch_state_subtopic(topic, payload); +} + void dispatch_reboot(const char*, const char*) { dispatch_reboot(true); @@ -981,15 +963,18 @@ void dispatchSetup() // In order of importance : commands are NOT case-sensitive // The command.func() call will receive the full topic and payload parameters! + LOG_TRACE(TAG_MSGR, F(D_SERVICE_STARTING)); + /* WARNING: remember to expand the commands array when adding new commands */ dispatch_add_command(PSTR("json"), dispatch_parse_json); dispatch_add_command(PSTR("page"), dispatch_page); dispatch_add_command(PSTR("wakeup"), dispatch_wakeup); dispatch_add_command(PSTR("sleep"), dispatch_sleep); - dispatch_add_command(PSTR("statusupdate"), dispatch_output_statusupdate); + dispatch_add_command(PSTR("statusupdate"), dispatch_statusupdate); dispatch_add_command(PSTR("clearpage"), dispatch_clear_page); dispatch_add_command(PSTR("jsonl"), dispatch_parse_jsonl); dispatch_add_command(PSTR("dim"), dispatch_dim); + dispatch_add_command(PSTR("idle"), dispatch_idle); dispatch_add_command(PSTR("brightness"), dispatch_dim); dispatch_add_command(PSTR("light"), dispatch_backlight); dispatch_add_command(PSTR("moodlight"), dispatch_moodlight); @@ -998,24 +983,27 @@ void dispatchSetup() dispatch_add_command(PSTR("reboot"), dispatch_reboot); dispatch_add_command(PSTR("restart"), dispatch_reboot); dispatch_add_command(PSTR("screenshot"), dispatch_screenshot); + dispatch_add_command(PSTR("discovery"), dispatch_send_discovery); dispatch_add_command(PSTR("factoryreset"), dispatch_factory_reset); #if HASP_USE_CONFIG > 0 dispatch_add_command(PSTR("setupap"), oobeFakeSetup); #endif /* WARNING: remember to expand the commands array when adding new commands */ + + LOG_INFO(TAG_MSGR, F(D_SERVICE_STARTED)); } -void dispatchLoop() -{ - lv_task_handler(); // process animations -} +IRAM_ATTR void dispatchLoop() +{} #if 1 || ARDUINO void dispatchEverySecond() { - if(dispatch_setings.teleperiod > 0 && (millis() - dispatchLastMillis) >= dispatch_setings.teleperiod * 1000) { + if(mqttIsConnected() && dispatch_setings.teleperiod > 0 && + (millis() - dispatchLastMillis) >= dispatch_setings.teleperiod * 1000) { dispatchLastMillis += dispatch_setings.teleperiod * 1000; - dispatch_output_statusupdate(NULL, NULL); + dispatch_statusupdate(NULL, NULL); + dispatch_send_discovery(NULL, NULL); } } #else @@ -1029,7 +1017,7 @@ void everySecond() if(elapsed.count() >= dispatch_setings.teleperiod) { std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - dispatch_output_statusupdate(NULL, NULL); + dispatch_statusupdate(NULL, NULL); } } } diff --git a/src/hasp/hasp_dispatch.h b/src/hasp/hasp_dispatch.h index 04645e50..fb51d558 100644 --- a/src/hasp/hasp_dispatch.h +++ b/src/hasp/hasp_dispatch.h @@ -4,14 +4,20 @@ #ifndef HASP_DISPATCH_H #define HASP_DISPATCH_H -#include "ArduinoJson.h" -#include "lvgl.h" +#include "hasplib.h" struct dispatch_conf_t { uint16_t teleperiod; }; +struct moodlight_t +{ + uint8_t brightness; + uint8_t power; + uint8_t rgbww[5]; +}; + enum hasp_event_t { // even = released, odd = pressed HASP_EVENT_OFF = 0, HASP_EVENT_ON = 1, @@ -23,18 +29,24 @@ enum hasp_event_t { // even = released, odd = pressed HASP_EVENT_LOST = 7, HASP_EVENT_DOUBLE = 8, + HASP_EVENT_OPEN = 10, + HASP_EVENT_OPENING = 11, + HASP_EVENT_CLOSED = 12, + HASP_EVENT_CLOSING = 13, + HASP_EVENT_STOP = 14, + HASP_EVENT_CHANGED = 32 }; /* ===== Default Event Processors ===== */ void dispatchSetup(void); -void dispatchLoop(void); +IRAM_ATTR void dispatchLoop(void); void dispatchEverySecond(void); void dispatchStart(void); void dispatchStop(void); /* ===== Special Event Processors ===== */ -void dispatch_topic_payload(const char* topic, const char* payload); +void dispatch_topic_payload(const char* topic, const char* payload, bool update); void dispatch_text_line(const char* cmnd); #ifdef ARDUINO @@ -46,28 +58,25 @@ void dispatch_parse_jsonl(std::istream& stream); void dispatch_clear_page(const char* page); void dispatch_json_error(uint8_t tag, DeserializationError& jsonError); -// void dispatch_set_page(uint8_t pageid); void dispatch_set_page(uint8_t pageid, lv_scr_load_anim_t effectid); void dispatch_page_next(lv_scr_load_anim_t effectid); void dispatch_page_prev(lv_scr_load_anim_t effectid); void dispatch_page_back(lv_scr_load_anim_t effectid); -void dispatch_dim(const char* level); -void dispatch_backlight(const char*, const char* payload); - -void dispatch_web_update(const char*, const char* espOtaUrl); void dispatch_reboot(bool saveConfig); - -void dispatch_output_idle_state(uint8_t state); -void dispatch_output_statusupdate(const char*, const char*); void dispatch_current_state(); -void dispatch_output_current_page(); +void dispatch_current_page(); +void dispatch_backlight(const char*, const char* payload); +void dispatch_web_update(const char*, const char* espOtaUrl); +void dispatch_statusupdate(const char*, const char*); +void dispatch_send_discovery(const char*, const char*); +void dispatch_idle(const char*, const char*); +void dispatch_calibrate(const char*, const char*); +void dispatch_wakeup(const char*, const char*); void dispatch_gpio_input_event(uint8_t pin, uint8_t group, uint8_t eventid); -void dispatch_object_value_changed(lv_obj_t* obj, int16_t state); - -void dispatch_normalized_group_value(uint8_t groupid, lv_obj_t* obj, int16_t val, int16_t min, int16_t max); +void dispatch_normalized_group_values(hasp_update_value_t& value); void dispatch_state_subtopic(const char* subtopic, const char* payload); diff --git a/src/hasp/hasp_event.cpp b/src/hasp/hasp_event.cpp index 7e546f5f..20d19cc2 100644 --- a/src/hasp/hasp_event.cpp +++ b/src/hasp/hasp_event.cpp @@ -22,9 +22,13 @@ * ******************************************************************************************** */ -#include "hasp_conf.h" +#include +#include + #include "hasplib.h" +#include "lv_core/lv_obj.h" // for tabview ext + static lv_style_int_t last_value_sent; static lv_color_t last_color_sent; @@ -36,23 +40,117 @@ void swipe_event_handler(lv_obj_t* obj, lv_event_t event); */ static void event_delete_object(lv_obj_t* obj) { - switch(obj->user_data.objid) { + switch(obj_get_type(obj)) { case LV_HASP_LINE: line_clear_points(obj); break; case LV_HASP_BTNMATRIX: my_btnmatrix_map_clear(obj); - _LV_WIN_PART_REAL_LAST; - _LV_WIN_PART_VIRTUAL_LAST; + break; + + case LV_HASP_MSGBOX: + my_msgbox_map_clear(obj); + break; + + case LV_HASP_IMAGE: + lv_img_cache_invalidate_src(NULL); break; case LV_HASP_GAUGE: break; + + default: + break; } // TODO: delete value_str data for ALL parts - my_obj_set_value_str_txt(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, NULL); + my_obj_set_value_str_text(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, NULL); +} + +/* ============================== Timer Event ============================ */ +void event_timer_calendar(lv_task_t* task) +{ + hasp_task_user_data_t* data = (hasp_task_user_data_t*)task->user_data; + lv_obj_t* obj = NULL; + + if(data) obj = hasp_find_obj_from_page_id(data->pageid, data->objid); + if(!obj || !data || !obj_check_type(obj, LV_HASP_CALENDER)) { + if(data) lv_mem_free(data); // the object that the user_data points to is gone + lv_task_del(task); // the calendar object for this task was deleted + LOG_WARNING(TAG_EVENT, "event_timer_calendar could not find the linked object"); + return; + } + + lv_calendar_date_t date; + + timeval curTime; + int rslt = gettimeofday(&curTime, NULL); + time_t t = curTime.tv_sec; + tm* timeinfo = localtime(&t); + (void)rslt; // unused + + if(timeinfo->tm_year < 120) { + lv_task_set_period(task, 60000); // try again in a minute + LOG_WARNING(TAG_EVENT, "event_timer_calendar could not sync the clock"); + return; + } else { + uint32_t next_hour = (3600 - (t % 3600)) * 1000; // ms to next top of hour + // lv_task_set_period(task, next_hour + 128); // small offset so all tasks don't run at once + lv_task_set_period(task, data->interval); + } + + date.day = timeinfo->tm_mday; + date.month = timeinfo->tm_mon + 1; // months since January 0-11 + date.year = timeinfo->tm_year + 1900; // years since 1900 + + LOG_VERBOSE(TAG_EVENT, "event_timer_calendar called with user %d:%d:%d", timeinfo->tm_hour, timeinfo->tm_min, + timeinfo->tm_sec); + + lv_calendar_set_today_date(obj, &date); +} + +void event_timer_clock(lv_task_t* task) +{ + hasp_task_user_data_t* data = (hasp_task_user_data_t*)task->user_data; + lv_obj_t* obj; + + if(data) obj = hasp_find_obj_from_page_id(data->pageid, data->objid); + if(!obj || !data) { + if(data) lv_mem_free(data); // the object that the user_data points to is gone + lv_task_del(task); // the calendar object for this task was deleted + LOG_WARNING(TAG_EVENT, "event_timer_clock could not find the linked object"); + return; + } + + timeval curTime; + int rslt = gettimeofday(&curTime, NULL); + time_t seconds = curTime.tv_sec; + tm* timeinfo = localtime(&seconds); + (void)rslt; // unused + + char buffer[24] = {0}; + if(timeinfo->tm_year < 120) { + snprintf_P(buffer, sizeof(buffer), PSTR("%d"), seconds); + } else { + strftime(buffer, sizeof(buffer), D_TIMESTAMP, timeinfo); // Literal String + } + + // LOG_VERBOSE(TAG_EVENT, "event_timer_clock called with user %d:%d:%d", timeinfo->tm_hour, timeinfo->tm_min, + // timeinfo->tm_sec); + + lv_label_set_text(obj, buffer); + lv_task_set_period(task, data->interval); +} + +/* ============================== Timer Event ============================ */ +void event_timer_refresh(lv_task_t* task) +{ + lv_obj_t* obj = (lv_obj_t*)task->user_data; + printf("event_timer_refresh called with user data\n"); + if(!obj) return; + + lv_obj_invalidate(obj); } /* ============================== Event Senders ============================ */ @@ -104,7 +202,7 @@ static bool translate_event(lv_obj_t* obj, lv_event_t event, uint8_t& eventid) // ##################### Value Senders ######################################################## -void event_send_object_data(lv_obj_t* obj, const char* data) +static void event_send_object_data(lv_obj_t* obj, const char* data) { uint8_t pageid; uint8_t objid; @@ -118,7 +216,7 @@ void event_send_object_data(lv_obj_t* obj, const char* data) } // Send out events with a val attribute -void event_object_val_event(lv_obj_t* obj, uint8_t eventid, int16_t val) +static void event_object_val_event(lv_obj_t* obj, uint8_t eventid, int16_t val) { char data[40]; char eventname[8]; @@ -129,7 +227,7 @@ void event_object_val_event(lv_obj_t* obj, uint8_t eventid, int16_t val) } // Send out events with a val and text attribute -void event_object_selection_changed(lv_obj_t* obj, uint8_t eventid, int16_t val, const char* text) +static void event_object_selection_changed(lv_obj_t* obj, uint8_t eventid, int16_t val, const char* text) { char data[200]; char eventname[8]; @@ -141,24 +239,32 @@ void event_object_selection_changed(lv_obj_t* obj, uint8_t eventid, int16_t val, // ##################### Event Handlers ######################################################## -#if HASP_USE_GPIO > 0 -void event_gpio_input(uint8_t pin, uint8_t group, uint8_t eventid) +static inline void event_update_group(uint8_t group, lv_obj_t* obj, bool power, int32_t val, int32_t min, int32_t max) { - char payload[64]; - char topic[8]; + hasp_update_value_t value = {.obj = obj, .group = group, .min = min, .max = max, .val = val, .power = power}; + dispatch_normalized_group_values(value); +} + +#if HASP_USE_GPIO > 0 +void event_gpio_input(uint8_t pin, uint8_t eventid) +{ + char payload[32]; + char topic[10]; char eventname[8]; - Parser::get_event_name(eventid, eventname, sizeof(eventname)); - snprintf_P(payload, sizeof(payload), PSTR("{\"pin\":%d,\"group\":%d,\"event\":\"%s\"}"), pin, group, eventname); - memcpy_P(topic, PSTR("input"), 6); + snprintf_P(topic, sizeof(topic), PSTR("input%d"), pin); + if(eventid == HASP_EVENT_ON || eventid == HASP_EVENT_OFF) { + Parser::get_event_name(HASP_EVENT_CHANGED, eventname, sizeof(eventname)); + snprintf_P(payload, sizeof(payload), PSTR("{\"event\":\"%s\",\"val\":%d}"), eventname, eventid); + } else { + Parser::get_event_name(eventid, eventname, sizeof(eventname)); + snprintf_P(payload, sizeof(payload), PSTR("{\"event\":\"%s\"}"), eventname); + } dispatch_state_subtopic(topic, payload); - - // update outputstates - // dispatch_group_onoff(group, Parser::get_event_state(eventid), NULL); } #endif -void log_event(const char* name, lv_event_t event) +static void log_event(const char* name, lv_event_t event) { return; @@ -252,7 +358,7 @@ void swipe_event_handler(lv_obj_t* obj, lv_event_t event) default: return; } - dispatch_output_current_page(); + dispatch_current_page(); } } @@ -338,7 +444,7 @@ void generic_event_handler(lv_obj_t* obj, lv_event_t event) default: haspPages.set(obj->user_data.actionid, transitionid); } - dispatch_output_current_page(); + dispatch_current_page(); } } else { char data[40]; @@ -347,8 +453,12 @@ void generic_event_handler(lv_obj_t* obj, lv_event_t event) snprintf_P(data, sizeof(data), PSTR("{\"event\":\"%s\"}"), eventname); event_send_object_data(obj, data); } - dispatch_normalized_group_value(obj->user_data.groupid, obj, Parser::get_event_state(last_value_sent), - HASP_EVENT_OFF, HASP_EVENT_ON); + + // Update group objects and gpios on release + if(last_value_sent != LV_EVENT_LONG_PRESSED || last_value_sent != LV_EVENT_LONG_PRESSED_REPEAT) { + bool state = Parser::get_event_state(last_value_sent); + event_update_group(obj->user_data.groupid, obj, state, state, HASP_EVENT_OFF, HASP_EVENT_ON); + } } /** @@ -387,7 +497,12 @@ void toggle_event_handler(lv_obj_t* obj, lv_event_t event) } event_object_val_event(obj, hasp_event_id, last_value_sent); - dispatch_normalized_group_value(obj->user_data.groupid, obj, last_value_sent, HASP_EVENT_OFF, HASP_EVENT_ON); + + // Update group objects and gpios on release + if(obj->user_data.groupid && hasp_event_id == HASP_EVENT_UP) { + event_update_group(obj->user_data.groupid, obj, last_value_sent, last_value_sent, HASP_EVENT_OFF, + HASP_EVENT_ON); + } } /** @@ -422,6 +537,15 @@ void selector_event_handler(lv_obj_t* obj, lv_event_t event) lv_roller_get_selected_str(obj, buffer, sizeof(buffer)); break; + case LV_HASP_TABVIEW: { + val = lv_tabview_get_tab_act(obj); + max = lv_tabview_get_tab_count(obj) - 1; + + lv_tabview_ext_t* ext = (lv_tabview_ext_t*)lv_obj_get_ext_attr(obj); + strcpy(buffer, ext->tab_name_ptr[val]); + break; + } + case LV_HASP_TABLE: { uint16_t row; uint16_t col; @@ -442,7 +566,11 @@ void selector_event_handler(lv_obj_t* obj, lv_event_t event) if(hasp_event_id == HASP_EVENT_CHANGED && last_value_sent == val) return; // same value as before last_value_sent = val; event_object_selection_changed(obj, hasp_event_id, val, buffer); - // if(max > 0) dispatch_normalized_group_value(obj->user_data.groupid, obj, val, 0, max); + + if(obj->user_data.groupid && max > 0) // max a cannot be 0, its the divider + if(hasp_event_id == HASP_EVENT_UP || hasp_event_id == LV_EVENT_VALUE_CHANGED) { + event_update_group(obj->user_data.groupid, obj, !!last_value_sent, last_value_sent, 0, max); + } // set the property // snprintf_P(property, sizeof(property), PSTR("val\":%d,\"text"), val); @@ -463,9 +591,7 @@ void btnmatrix_event_handler(lv_obj_t* obj, lv_event_t event) /* Get the new value */ char buffer[128]; - char property[36]; uint16_t val = 0; - uint16_t max = 0; val = lv_btnmatrix_get_active_btn(obj); if(val != LV_BTNMATRIX_BTN_NONE) { @@ -479,11 +605,43 @@ void btnmatrix_event_handler(lv_obj_t* obj, lv_event_t event) last_value_sent = val; event_object_selection_changed(obj, hasp_event_id, val, buffer); - // if(max > 0) dispatch_normalized_group_value(obj->user_data.groupid, obj, val, 0, max); - // set the property - // snprintf_P(property, sizeof(property), PSTR("val\":%d,\"text"), val); - // attr_out_str(obj, property, buffer); + // if(max > 0) // max a cannot be 0, its the divider + // if(hasp_event_id == HASP_EVENT_UP || hasp_event_id == LV_EVENT_VALUE_CHANGED) { + // event_update_group(obj->user_data.groupid, obj, last_value_sent, 0, max); + // } +} + +/** + * Called when a msgbox value has changed + * @param obj pointer to a dropdown list or roller + * @param event type of event that occured + */ +void msgbox_event_handler(lv_obj_t* obj, lv_event_t event) +{ + log_event("msgbox", event); + + uint8_t hasp_event_id; + if(!translate_event(obj, event, hasp_event_id)) return; // Use LV_EVENT_VALUE_CHANGED + + /* Get the new value */ + char buffer[128]; + uint16_t val = 0; + + val = lv_msgbox_get_active_btn(obj); + if(val != LV_BTNMATRIX_BTN_NONE) { + const char* txt = lv_msgbox_get_active_btn_text(obj); + strncpy(buffer, txt, sizeof(buffer)); + if(hasp_event_id == HASP_EVENT_UP || hasp_event_id == HASP_EVENT_RELEASE) lv_msgbox_start_auto_close(obj, 0); + } else { + buffer[0] = 0; // empty string + } + + if(hasp_event_id == HASP_EVENT_CHANGED && last_value_sent == val) return; // same value as before + + last_value_sent = val; + event_object_selection_changed(obj, hasp_event_id, val, buffer); + // if(max > 0) event_update_group(obj->user_data.groupid, obj, val, 0, max); } /** @@ -521,7 +679,9 @@ void slider_event_handler(lv_obj_t* obj, lv_event_t event) last_value_sent = val; event_object_val_event(obj, hasp_event_id, val); - dispatch_normalized_group_value(obj->user_data.groupid, obj, val, min, max); + + if(obj->user_data.groupid && (hasp_event_id == HASP_EVENT_CHANGED || hasp_event_id == HASP_EVENT_UP) && min != max) + event_update_group(obj->user_data.groupid, obj, !!val, val, min, max); } /** @@ -553,16 +713,37 @@ void cpicker_event_handler(lv_obj_t* obj, lv_event_t event) eventname, c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue); event_send_object_data(obj, data); - // dispatch_normalized_group_value(obj->user_data.groupid, obj, val, min, max); + // event_update_group(obj->user_data.groupid, obj, val, min, max); } -/** - * Called when an object is deleted - * @param obj pointer to a generic object - * @param event type of event that occured - */ -void deleted_event_handler(lv_obj_t* obj, lv_event_t event) +void calendar_event_handler(lv_obj_t* obj, lv_event_t event) { + log_event("calendar", event); + uint8_t hasp_event_id; - translate_event(obj, event, hasp_event_id); + if(event != LV_EVENT_PRESSED && event != LV_EVENT_RELEASED && event != LV_EVENT_VALUE_CHANGED) return; + if(!translate_event(obj, event, hasp_event_id)) return; // Use LV_EVENT_VALUE_CHANGED + + /* Get the new value */ + lv_calendar_date_t* date; + if(hasp_event_id == HASP_EVENT_CHANGED) + date = lv_calendar_get_pressed_date(obj); // pressed date + else + date = lv_calendar_get_showed_date(obj); // current month + if(!date) return; + + lv_style_int_t val = date->day + date->month * 31; + if(hasp_event_id == HASP_EVENT_CHANGED && last_value_sent == val) return; // same value as before + + char data[100]; + char eventname[8]; + Parser::get_event_name(hasp_event_id, eventname, sizeof(eventname)); + + last_value_sent = val; + + snprintf_P(data, sizeof(data), PSTR("{\"event\":\"%s\",\"val\":\"%d\",\"text\":\"%04d-%02d-%02dT00:00:00Z\"}"), + eventname, date->day, date->year, date->month, date->day); + event_send_object_data(obj, data); + + // event_update_group(obj->user_data.groupid, obj, val, min, max); } \ No newline at end of file diff --git a/src/hasp/hasp_event.h b/src/hasp/hasp_event.h index b113575f..517c83ea 100644 --- a/src/hasp/hasp_event.h +++ b/src/hasp/hasp_event.h @@ -11,17 +11,24 @@ #define HASP_NUM_PAGE_BACK (HASP_NUM_PAGES + 2) #define HASP_NUM_PAGE_NEXT (HASP_NUM_PAGES + 3) +// Timer event Handlers +void event_timer_calendar(lv_task_t* task); +void event_timer_clock(lv_task_t* task); + +// Object event Handlers void wakeup_event_handler(lv_obj_t* obj, lv_event_t event); void generic_event_handler(lv_obj_t* obj, lv_event_t event); void toggle_event_handler(lv_obj_t* obj, lv_event_t event); void slider_event_handler(lv_obj_t* obj, lv_event_t event); void selector_event_handler(lv_obj_t* obj, lv_event_t event); void btnmatrix_event_handler(lv_obj_t* obj, lv_event_t event); +void msgbox_event_handler(lv_obj_t* obj, lv_event_t event); void cpicker_event_handler(lv_obj_t* obj, lv_event_t event); -void deleted_event_handler(lv_obj_t* obj, lv_event_t event); +void calendar_event_handler(lv_obj_t* obj, lv_event_t event); #if HASP_USE_GPIO > 0 -void event_gpio_input(uint8_t pin, uint8_t group, uint8_t eventid); +// GPIO event Handler +void event_gpio_input(uint8_t pin, uint8_t eventid); #endif #endif // HASP_EVENT_H \ No newline at end of file diff --git a/src/hasp/hasp_object.cpp b/src/hasp/hasp_object.cpp index c6beef08..ee87d9d9 100644 --- a/src/hasp/hasp_object.cpp +++ b/src/hasp/hasp_object.cpp @@ -15,17 +15,14 @@ #include "ArduinoLog.h" #endif -#include "lvgl.h" -#if LVGL_VERSION_MAJOR != 7 -#include "../lv_components.h" -#endif - #include "hasplib.h" -const char** btnmatrix_default_map; // memory pointer to lvgl default btnmatrix map +const char** btnmatrix_default_map; // memory pointer to lvgl default btnmatrix map +const char* msgbox_default_map[] = {"OK", ""}; // memory pointer to hasp default msgbox map // ##################### Object Finders ######################################################## +// Return a child object from a parent with a specific objid lv_obj_t* hasp_find_obj_from_parent_id(lv_obj_t* parent, uint8_t objid) { if(objid == 0 || parent == nullptr) return parent; @@ -41,7 +38,7 @@ lv_obj_t* hasp_find_obj_from_parent_id(lv_obj_t* parent, uint8_t objid) if(grandchild) return grandchild; /* grandchild found, return it */ /* check tabs */ - if(check_obj_type(child, LV_HASP_TABVIEW)) { + if(obj_check_type(child, LV_HASP_TABVIEW)) { uint16_t tabcount = lv_tabview_get_tab_count(child); for(uint16_t i = 0; i < tabcount; i++) { lv_obj_t* tab = lv_tabview_get_tab(child, i); @@ -60,12 +57,14 @@ lv_obj_t* hasp_find_obj_from_parent_id(lv_obj_t* parent, uint8_t objid) return NULL; } -// lv_obj_t * hasp_find_obj_from_page_id(uint8_t pageid, uint8_t objid) -// { -// return hasp_find_obj_from_parent_id(get_page_obj(pageid), objid); -// } +// Return the object with a specific pageid and objid +lv_obj_t* hasp_find_obj_from_page_id(uint8_t pageid, uint8_t objid) +{ + return hasp_find_obj_from_parent_id(haspPages.get_obj(pageid), objid); +} -bool hasp_find_id_from_obj(lv_obj_t* obj, uint8_t* pageid, uint8_t* objid) +// Return the pageid and objid of an object +bool hasp_find_id_from_obj(const lv_obj_t* obj, uint8_t* pageid, uint8_t* objid) { if(!obj || !haspPages.get_id(obj, pageid)) return false; if(obj->user_data.id == 0 && obj != haspPages.get_obj(*pageid)) return false; @@ -73,122 +72,18 @@ bool hasp_find_id_from_obj(lv_obj_t* obj, uint8_t* pageid, uint8_t* objid) return true; } -/** - * Check if an lvgl object typename corresponds to a given HASP object ID - * @param lvobjtype a char* to a string - * @param haspobjtype the HASP object ID to check against - * @return true or false wether the types match - * @note - */ -// bool check_obj_type_str(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype) -// { -// lvobjtype += 3; // skip "lv_" - -// switch(haspobjtype) { -// case LV_HASP_BTNMATRIX: -// return (strcmp_P(lvobjtype, PSTR("btnmatrix")) == 0); -// case LV_HASP_TABLE: -// return (strcmp_P(lvobjtype, PSTR("table")) == 0); -// case LV_HASP_BUTTON: -// return (strcmp_P(lvobjtype, PSTR("btn")) == 0); -// case LV_HASP_LABEL: -// return (strcmp_P(lvobjtype, PSTR("label")) == 0); -// case LV_HASP_CHECKBOX: -// return (strcmp_P(lvobjtype, PSTR("checkbox")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cb")) == 0); -// case LV_HASP_DROPDOWN: -// return (strcmp_P(lvobjtype, PSTR("dropdown")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_ddlist")) == 0); -// case LV_HASP_CPICKER: -// return (strcmp_P(lvobjtype, PSTR("cpicker")) == 0); -// case LV_HASP_SPINNER: -// return (strcmp_P(lvobjtype, PSTR("spinner")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_preload")) == 0); -// case LV_HASP_SLIDER: -// return (strcmp_P(lvobjtype, PSTR("slider")) == 0); -// case LV_HASP_GAUGE: -// return (strcmp_P(lvobjtype, PSTR("gauge")) == 0); -// case LV_HASP_ARC: -// return (strcmp_P(lvobjtype, PSTR("arc")) == 0); -// case LV_HASP_BAR: -// return (strcmp_P(lvobjtype, PSTR("bar")) == 0); -// case LV_HASP_LMETER: -// return (strcmp_P(lvobjtype, PSTR("linemeter")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_lmeter")) == 0) -// case LV_HASP_ROLLER: -// return (strcmp_P(lvobjtype, PSTR("roller")) == 0); -// case LV_HASP_SWITCH: -// return (strcmp_P(lvobjtype, PSTR("switch")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_sw")) == 0) -// case LV_HASP_LED: -// return (strcmp_P(lvobjtype, PSTR("led")) == 0); -// case LV_HASP_IMAGE: -// return (strcmp_P(lvobjtype, PSTR("img")) == 0); -// case LV_HASP_IMGBTN: -// return (strcmp_P(lvobjtype, PSTR("imgbtn")) == 0); -// case LV_HASP_CONTAINER: -// return (strcmp_P(lvobjtype, PSTR("container")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) -// case LV_HASP_OBJECT: -// return (strcmp_P(lvobjtype, PSTR("page")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) -// case LV_HASP_PAGE: -// return (strcmp_P(lvobjtype, PSTR("obj")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) -// case LV_HASP_TABVIEW: -// return (strcmp_P(lvobjtype, PSTR("tabview")) == 0); -// case LV_HASP_TILEVIEW: -// return (strcmp_P(lvobjtype, PSTR("tileview")) == 0); -// case LV_HASP_CHART: -// return (strcmp_P(lvobjtype, PSTR("chart")) == 0); -// case LV_HASP_CANVAS: -// return (strcmp_P(lvobjtype, PSTR("canvas")) == 0); -// case LV_HASP_CALENDER: -// return (strcmp_P(lvobjtype, PSTR("calender")) == 0); -// case LV_HASP_MSGBOX: -// return (strcmp_P(lvobjtype, PSTR("msgbox")) == 0); -// case LV_HASP_WINDOW: -// return (strcmp_P(lvobjtype, PSTR("win")) == 0); - -// default: -// return false; -// } -// } - -/** - * Get the object type name of an object - * @param obj an lv_obj_t* of the object to check its type - * @return name of the object type - * @note - */ -const char* get_obj_type_name(lv_obj_t* obj) -{ - lv_obj_type_t list; - lv_obj_get_type(obj, &list); - const char* objtype = list.type[0]; - return objtype + 3; // skip lv_ -} - -/** - * Check if an lvgl objecttype name corresponds to a given HASP object ID - * @param obj an lv_obj_t* of the object to check its type - * @param haspobjtype the HASP object ID to check against - * @return true or false wether the types match - * @note - */ -bool check_obj_type(lv_obj_t* obj, lv_hasp_obj_type_t haspobjtype) -{ -#if 1 - return obj->user_data.objid == (uint8_t)haspobjtype; -#else - lv_obj_type_t list; - lv_obj_get_type(obj, &list); - const char* objtype = list.type[0]; - return check_obj_type(objtype, haspobjtype); -#endif -} - -void hasp_object_tree(lv_obj_t* parent, uint8_t pageid, uint16_t level) +void hasp_object_tree(const lv_obj_t* parent, uint8_t pageid, uint16_t level) { if(parent == nullptr) return; /* Output parent info */ - lv_obj_type_t list; - lv_obj_get_type(parent, &list); - const char* objtype = list.type[0]; - LOG_VERBOSE(TAG_HASP, F("[%d] " HASP_OBJECT_NOTATION " %s"), level, pageid, parent->user_data.id, objtype); + char indent[31]; + memset(indent, 32, 31); + if(level < 15) indent[level * 2] = 0; + indent[30] = 0; + + LOG_VERBOSE(TAG_HASP, F("%s- " HASP_OBJECT_NOTATION ": %s"), indent, pageid, parent->user_data.id, + obj_get_type_name(parent)); lv_obj_t* child; child = lv_obj_get_child(parent, NULL); @@ -201,7 +96,7 @@ void hasp_object_tree(lv_obj_t* parent, uint8_t pageid, uint16_t level) } /* check tabs */ - if(check_obj_type(parent, LV_HASP_TABVIEW)) { + if(obj_check_type(parent, LV_HASP_TABVIEW)) { #if 1 uint16_t tabcount = lv_tabview_get_tab_count(parent); for(uint16_t i = 0; i < tabcount; i++) { @@ -225,73 +120,53 @@ void object_dispatch_state(uint8_t pageid, uint8_t btnid, const char* payload) // ##################### State Changers ######################################################## -void object_set_group_value(lv_obj_t* parent, uint8_t groupid, int16_t intval) +// Recursive function that goes over all objects only ONCE +void object_set_group_values(lv_obj_t* parent, hasp_update_value_t& value) { - if(groupid == 0 || parent == nullptr) return; + if(parent == nullptr) return; - lv_obj_t* child; - child = lv_obj_get_child(parent, NULL); - while(child) { - /* child found, update it */ - if(groupid == child->user_data.groupid) hasp_process_obj_attribute_val(child, NULL, intval, intval, true); + // Update object if it's in the same group + if(value.group == parent->user_data.groupid && value.obj != parent) { + attribute_set_normalized_value(parent, value); + } - /* update grandchildren */ - object_set_group_value(child, groupid, intval); - - /* check tabs */ - if(check_obj_type(child, LV_HASP_TABVIEW)) { - //#if LVGL_VERSION_MAJOR == 7 - uint16_t tabcount = lv_tabview_get_tab_count(child); - for(uint16_t i = 0; i < tabcount; i++) { - lv_obj_t* tab = lv_tabview_get_tab(child, i); - LOG_VERBOSE(TAG_HASP, F("Found tab %i"), i); - if(tab->user_data.groupid && groupid == tab->user_data.groupid) - hasp_process_obj_attribute_val(tab, NULL, intval, intval, true); /* tab found, update it */ - - /* check grandchildren */ - object_set_group_value(tab, groupid, intval); - } - //#endif + /* check tabs */ + if(obj_get_type(parent) == LV_HASP_TABVIEW) { + uint16_t tabcount = lv_tabview_get_tab_count(parent); + for(uint16_t i = 0; i < tabcount; i++) { + lv_obj_t* tab = lv_tabview_get_tab(parent, i); + object_set_group_values(tab, value); + } + } else { + lv_obj_t* child; + child = lv_obj_get_child(parent, NULL); + while(child) { + object_set_group_values(child, value); + child = lv_obj_get_child(parent, child); } - - /* try next sibling */ - child = lv_obj_get_child(parent, child); } } -// TODO make this a recursive function that goes over all objects only ONCE -void object_set_normalized_group_value(uint8_t groupid, lv_obj_t* src_obj, int16_t val, int16_t min, int16_t max) +// SHOULD only by called from DISPATCH +void object_set_normalized_group_values(hasp_update_value_t& value) { - if(groupid == 0) return; - if(min == max) return; + if(value.group == 0 || value.min == value.max) return; - for(uint8_t page = 0; page < HASP_NUM_PAGES; page++) { - object_set_group_value(haspPages.get_obj(page), groupid, val); - // uint8_t startid = 1; - // for(uint8_t objid = startid; objid < 20; objid++) { - // lv_obj_t* obj = hasp_find_obj_from_parent_id(get_page_obj(page), objid); - // if(obj && obj != src_obj && obj->user_data.groupid == groupid) { // skip source object, if set - // LOG_VERBOSE(TAG_HASP, F("Found p%db%d in group %d"), page, objid, groupid); - // lv_obj_set_state(obj, val > 0 ? LV_STATE_PRESSED | LV_STATE_CHECKED : LV_STATE_DEFAULT); - // switch(obj->user_data.objid) { - // case HASP_OBJ_ARC: - // case HASP_OBJ_SLIDER: - // case HASP_OBJ_CHECKBOX: - // hasp_process_obj_attribute_val(); - // default: - // } - // } - // } + uint8_t page = haspPages.get(); + object_set_group_values(haspPages.get_obj(page), value); // Update visible objects first + + for(uint8_t i = 0; i < HASP_NUM_PAGES; i++) { + if(i != page) object_set_group_values(haspPages.get_obj(i), value); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Used in the dispatcher & hasp_new_object -void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char* attr, const char* payload) +void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char* attr, const char* payload, bool update) { - if(lv_obj_t* obj = hasp_find_obj_from_parent_id(haspPages.get_obj(pageid), objid)) { - hasp_process_obj_attribute(obj, attr, payload, strlen(payload) > 0); + if(lv_obj_t* obj = hasp_find_obj_from_page_id(pageid, objid)) { + hasp_process_obj_attribute(obj, attr, payload, update); // || strlen(payload) > 0); } else { LOG_WARNING(TAG_HASP, F(D_OBJECT_UNKNOWN " " HASP_OBJECT_NOTATION), pageid, objid); } @@ -299,7 +174,8 @@ void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char* attr, con // ##################### Object Creator ######################################################## -int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) +// Called from hasp_new_object only to process all attributes +static inline int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) { int i = 0; #if defined(WINDOWS) || defined(POSIX) @@ -308,7 +184,8 @@ int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) std::string v; for(JsonPair keyValue : doc) { - LOG_VERBOSE(TAG_HASP, F(D_BULLET "%s=%s"), keyValue.key().c_str(), keyValue.value().as().c_str()); + // LOG_VERBOSE(TAG_HASP, F(D_BULLET "%s=%s"), keyValue.key().c_str(), + // keyValue.value().as().c_str()); v = keyValue.value().as(); hasp_process_obj_attribute(obj, keyValue.key().c_str(), keyValue.value().as().c_str(), true); i++; @@ -318,7 +195,7 @@ int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) v.reserve(64); for(JsonPair keyValue : doc) { - LOG_DEBUG(TAG_HASP, F(D_BULLET "%s=%s"), keyValue.key().c_str(), keyValue.value().as().c_str()); + // LOG_DEBUG(TAG_HASP, F(D_BULLET "%s=%s"), keyValue.key().c_str(), keyValue.value().as().c_str()); v = keyValue.value().as(); hasp_process_obj_attribute(obj, keyValue.key().c_str(), keyValue.value().as().c_str(), true); i++; @@ -328,6 +205,18 @@ int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) return i; } +static void object_add_task(lv_obj_t* obj, uint8_t pageid, uint8_t objid, lv_task_cb_t task_xcb, uint16_t interval) +{ + hasp_task_user_data_t* user_data = (hasp_task_user_data_t*)lv_mem_alloc(sizeof(hasp_task_user_data_t)); + if(!user_data) return; + + user_data->pageid = pageid; + user_data->objid = objid; + user_data->interval = interval; + lv_task_t* task = lv_task_create(task_xcb, 25, LV_TASK_PRIO_LOWEST, (void*)user_data); + (void)task; // unused +} + /** * Create a new object according to the json config * @param config Json representation for this object @@ -365,9 +254,8 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) config.remove(FPSTR(FP_PARENTID)); } - uint16_t sdbm = 0; - uint8_t groupid = config[FPSTR(FP_GROUPID)].as(); - uint8_t id = config[FPSTR(FP_ID)].as(); + uint16_t sdbm = 0; + uint8_t id = config[FPSTR(FP_ID)].as(); config.remove(FPSTR(FP_ID)); /* Create the object if it does not exist */ @@ -447,6 +335,8 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) lv_label_set_recolor(obj, true); lv_obj_set_event_cb(obj, generic_event_handler); obj->user_data.objid = LV_HASP_LABEL; + + // object_add_task(obj, pageid, id, event_timer_clock, 1000); } break; @@ -489,20 +379,16 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) case LV_HASP_PAGE: case HASP_OBJ_PAGE: obj = lv_page_create(parent_obj, NULL); - if(obj) { - obj->user_data.objid = LV_HASP_PAGE; - lv_obj_set_event_cb(obj, deleted_event_handler); // Needed for memory dealocation - } + if(obj) obj->user_data.objid = LV_HASP_PAGE; + // No event handler for pages break; #if LV_USE_WIN && LVGL_VERSION_MAJOR == 7 case LV_HASP_WINDOW: case HASP_OBJ_WIN: obj = lv_win_create(parent_obj, NULL); - if(obj) { - obj->user_data.objid = LV_HASP_WINDOW; - lv_obj_set_event_cb(obj, deleted_event_handler); // Needed for memory dealocation - } + if(obj) obj->user_data.objid = LV_HASP_WINDOW; + // No event handler for pages break; #endif @@ -529,14 +415,7 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) obj = lv_tabview_create(parent_obj, LV_DIR_TOP, 100); // No event handler for tabs if(obj) { - lv_obj_t* tab; - tab = lv_tabview_add_tab(obj, "tab 1"); - // lv_obj_set_user_data(tab, id + 1); - tab = lv_tabview_add_tab(obj, "tab 2"); - // lv_obj_set_user_data(tab, id + 2); - tab = lv_tabview_add_tab(obj, "tab 3"); - // lv_obj_set_user_data(tab, id + 3); - + lv_obj_set_event_cb(obj, selector_event_handler); obj->user_data.objid = LV_HASP_TABVIEW; } break; @@ -554,30 +433,35 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) case LV_HASP_TILEVIEW: case HASP_OBJ_TILEVIEW: obj = lv_tileview_create(parent_obj, NULL); - if(obj) { - obj->user_data.objid = LV_HASP_TILEVIEW; - lv_obj_set_event_cb(obj, deleted_event_handler); // Needed for memory dealocation - } + if(obj) obj->user_data.objid = LV_HASP_TILEVIEW; + + // No event handler for tileviews break; case LV_HASP_TABVIEW: case HASP_OBJ_TABVIEW: obj = lv_tabview_create(parent_obj, NULL); + // No event handler for tabs if(obj) { - lv_obj_set_event_cb(obj, deleted_event_handler); // Needed for memory dealocation - - lv_obj_t* tab; - tab = lv_tabview_add_tab(obj, "tab 1"); - // lv_obj_set_user_data(tab, id + 1); - tab = lv_tabview_add_tab(obj, "tab 2"); - // lv_obj_set_user_data(tab, id + 2); - tab = lv_tabview_add_tab(obj, "tab 3"); - // lv_obj_set_user_data(tab, id + 3); - + lv_obj_set_event_cb(obj, selector_event_handler); obj->user_data.objid = LV_HASP_TABVIEW; } break; + case LV_HASP_TAB: + case HASP_OBJ_TAB: + if(parent_obj && parent_obj->user_data.objid == LV_HASP_TABVIEW) { + obj = lv_tabview_add_tab(parent_obj, "Tab"); + if(obj) { + lv_obj_set_event_cb(obj, generic_event_handler); + obj->user_data.objid = LV_HASP_TAB; + } + } else { + LOG_WARNING(TAG_HASP, F("Parent of a tab must be a tabview object")); + return; + } + break; + #endif /* ----- Color Objects ------ */ case LV_HASP_CPICKER: @@ -595,11 +479,11 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) obj = lv_spinner_create(parent_obj, NULL); if(obj) { obj->user_data.objid = LV_HASP_SPINNER; - lv_obj_set_event_cb(obj, deleted_event_handler); // Needed for memory dealocation + lv_obj_set_event_cb(obj, generic_event_handler); } break; - #endif + /* ----- Range Objects ------ */ case LV_HASP_SLIDER: case HASP_OBJ_SLIDER: @@ -633,13 +517,14 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) } break; - case LV_HASP_LMETER: - case HASP_OBJ_LMETER: + case LV_HASP_LINEMETER: + case HASP_OBJ_LMETER: // obsolete + case HASP_OBJ_LINEMETER: obj = lv_linemeter_create(parent_obj, NULL); if(obj) { lv_linemeter_set_range(obj, 0, 100); lv_obj_set_event_cb(obj, generic_event_handler); - obj->user_data.objid = LV_HASP_LMETER; + obj->user_data.objid = LV_HASP_LINEMETER; } break; @@ -699,13 +584,27 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) } break; + case LV_HASP_MSGBOX: + case HASP_OBJ_MSGBOX: + obj = lv_msgbox_create(parent_obj, NULL); + if(obj) { + lv_obj_align(obj, NULL, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_auto_realign(obj, true); + lv_obj_set_event_cb(obj, msgbox_event_handler); + if(msgbox_default_map) lv_msgbox_add_btns(obj, msgbox_default_map); + obj->user_data.objid = LV_HASP_MSGBOX; + } + break; + case LV_HASP_CALENDER: case HASP_OBJ_CALENDAR: obj = lv_calendar_create(parent_obj, NULL); // lv_obj_align(obj, NULL, LV_ALIGN_IN_TOP_MID, 0, 20); if(obj) { - lv_obj_set_event_cb(obj, selector_event_handler); + lv_obj_set_event_cb(obj, calendar_event_handler); obj->user_data.objid = LV_HASP_CALENDER; + + object_add_task(obj, pageid, id, event_timer_calendar, 5000); } break; @@ -726,30 +625,34 @@ void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id) lv_obj_set_gesture_parent(obj, false); /* id tag the object */ - // lv_obj_set_user_data(obj, id); obj->user_data.id = id; - // obj->user_data.groupid = groupid; // get/set in atttr + uint8_t temp; // needed for debug tests + (void)temp; + +#ifdef HASP_DEBUG /** testing start **/ - uint8_t temp; if(!hasp_find_id_from_obj(obj, &pageid, &temp)) { LOG_ERROR(TAG_HASP, F(D_OBJECT_LOST)); return; } +#endif +#if HASP_LOG_LEVEL >= LOG_LEVEL_VERBOSE /** verbose reporting **/ - lv_obj_type_t list; - lv_obj_get_type(obj, &list); - LOG_VERBOSE(TAG_HASP, F(D_BULLET HASP_OBJECT_NOTATION " = %s"), pageid, temp, list.type[0]); + LOG_VERBOSE(TAG_HASP, F(D_BULLET HASP_OBJECT_NOTATION " = %s"), pageid, id, obj_get_type_name(obj)); +#endif +#ifdef HASP_DEBUG /* test double-check */ - lv_obj_t* test = hasp_find_obj_from_parent_id(haspPages.get_obj(pageid), (uint8_t)temp); - if(test != obj) { + lv_obj_t* test = hasp_find_obj_from_page_id(pageid, (uint8_t)temp); + if(test != obj || temp != id) { LOG_ERROR(TAG_HASP, F(D_OBJECT_MISMATCH)); return; } else { // object created successfully } +#endif } else { // object already exists diff --git a/src/hasp/hasp_object.h b/src/hasp/hasp_object.h index 6fca9528..14836bb7 100644 --- a/src/hasp/hasp_object.h +++ b/src/hasp/hasp_object.h @@ -4,8 +4,7 @@ #ifndef HASP_OBJECT_H #define HASP_OBJECT_H -#include -#include "lvgl.h" +#include "hasplib.h" const char FP_PAGE[] PROGMEM = "page"; const char FP_ID[] PROGMEM = "id"; @@ -14,68 +13,126 @@ const char FP_OBJID[] PROGMEM = "objid"; const char FP_PARENTID[] PROGMEM = "parentid"; const char FP_GROUPID[] PROGMEM = "groupid"; +typedef struct +{ + uint8_t pageid; + uint8_t objid; + uint16_t interval; +} hasp_task_user_data_t; + +typedef struct +{ + lv_obj_t* obj; + uint8_t group; + int32_t min; + int32_t max; + int32_t val; + bool power; +} hasp_update_value_t; + enum lv_hasp_obj_type_t { + /* Containers */ + LV_HASP_SCREEN = 1, + LV_HASP_CONTAINER = 2, + LV_HASP_WINDOW = 3, // placeholder + LV_HASP_MSGBOX = 4, // placeholder + LV_HASP_TILEVIEW = 5, // placeholder + LV_HASP_TABVIEW = 6, // placeholder + LV_HASP_TAB = 7, // placeholder + LV_HASP_PAGE = 8, // Obsolete in v8 + /* Controls */ - LV_HASP_OBJECT = 91, // 10 - LV_HASP_BUTTON = 10, // 12 + LV_HASP_OBJECT = 11, + LV_HASP_BUTTON = 12, LV_HASP_BTNMATRIX = 13, LV_HASP_IMGBTN = 14, // placeholder - LV_HASP_CHECKBOX = 11, // 15 - LV_HASP_SWITCH = 40, // 16 - LV_HASP_SLIDER = 30, // 17 + LV_HASP_CHECKBOX = 15, + LV_HASP_SWITCH = 16, + LV_HASP_SLIDER = 17, LV_HASP_TEXTAREA = 18, // placeholder LV_HASP_SPINBOX = 19, // placeholder LV_HASP_CPICKER = 20, - /* Selectors */ - LV_HASP_DROPDOWN = 50, - LV_HASP_ROLLER = 51, - LV_HASP_LIST = 52, // placeholder - LV_HASP_TABLE = 53, - LV_HASP_CALENDER = 54, - - /* Containers */ - LV_HASP_SCREEN = 1, - LV_HASP_CONTAINER = 70, - LV_HASP_WINDOW = 71, // placeholder - LV_HASP_MSGBOX = 72, // placeholder - LV_HASP_TILEVIEW = 73, // placeholder - LV_HASP_TABVIEW = 74, // placeholder - LV_HASP_TAB = 75, // placeholder - LV_HASP_PAGE = 79, // Obsolete in v8 - /* Visualizers */ - LV_HASP_LABEL = 12, // 30 - LV_HASP_GAUGE = 31, - LV_HASP_BAR = 32, - LV_HASP_LMETER = 33, - LV_HASP_LED = 41, // 34 - LV_HASP_ARC = 22, // 35 - LV_HASP_SPINNER = 21, // 36 - LV_HASP_CHART = 37, + LV_HASP_LABEL = 21, + LV_HASP_GAUGE = 22, + LV_HASP_BAR = 23, + LV_HASP_LINEMETER = 24, + LV_HASP_LED = 25, + LV_HASP_ARC = 26, + LV_HASP_SPINNER = 27, + LV_HASP_CHART = 28, + + /* Selectors */ + LV_HASP_DROPDOWN = 29, + LV_HASP_ROLLER = 30, + LV_HASP_LIST = 31, // placeholder + LV_HASP_TABLE = 32, + LV_HASP_CALENDER = 33, /* Graphics */ - LV_HASP_LINE = 60, - LV_HASP_IMAGE = 61, // placeholder - LV_HASP_CANVAS = 62, // placeholder - LV_HASP_MASK = 63, // placeholder + LV_HASP_LINE = 36, + LV_HASP_IMAGE = 37, // placeholder + LV_HASP_CANVAS = 38, // placeholder + LV_HASP_MASK = 39, // placeholder }; void hasp_new_object(const JsonObject& config, uint8_t& saved_page_id); lv_obj_t* hasp_find_obj_from_parent_id(lv_obj_t* parent, uint8_t objid); -// lv_obj_t * hasp_find_obj_from_page_id(uint8_t pageid, uint8_t objid); -bool hasp_find_id_from_obj(lv_obj_t* obj, uint8_t* pageid, uint8_t* objid); -// bool check_obj_type_str(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype); -const char* get_obj_type_name(lv_obj_t* obj); -bool check_obj_type(lv_obj_t* obj, lv_hasp_obj_type_t haspobjtype); -void hasp_object_tree(lv_obj_t* parent, uint8_t pageid, uint16_t level); +lv_obj_t* hasp_find_obj_from_page_id(uint8_t pageid, uint8_t objid); +bool hasp_find_id_from_obj(const lv_obj_t* obj, uint8_t* pageid, uint8_t* objid); + +void hasp_object_tree(const lv_obj_t* parent, uint8_t pageid, uint16_t level); void object_dispatch_state(uint8_t pageid, uint8_t btnid, const char* payload); -void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char* attr, const char* payload); +void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char* attr, const char* payload, bool update); -void object_set_normalized_group_value(uint8_t groupid, lv_obj_t* src_obj, int16_t val, int16_t min, int16_t max); +void object_set_normalized_group_values(hasp_update_value_t& value); + +/** + * Get the object type name of an object + * @param obj an lv_obj_t* of the object to check its type + * @return name of the object type + * @note + */ +inline const char* obj_get_type_name(const lv_obj_t* obj) +{ + lv_obj_type_t list; + lv_obj_get_type(obj, &list); + const char* objtype = list.type[0]; + return objtype + 3; // skip lv_ +} +/** + * Get the hasp object type of a given LVGL object + * @param obj an lv_obj_t* of the object to check its type + * @return lv_hasp_obj_type_t + * @note + */ +inline lv_hasp_obj_type_t obj_get_type(const lv_obj_t* obj) +{ + return (lv_hasp_obj_type_t)obj->user_data.objid; +} +/** + * Check if an lvgl objecttype name corresponds to a given HASP object ID + * @param obj an lv_obj_t* of the object to check its type + * @param haspobjtype the HASP object ID to check against + * @return true or false wether the types match + * @note + */ +inline bool obj_check_type(const lv_obj_t* obj, lv_hasp_obj_type_t haspobjtype) +{ +#if 1 + if(!obj) return false; + return obj->user_data.objid == (uint8_t)haspobjtype; +#else + lv_obj_type_t list; + lv_obj_get_type(obj, &list); + const char* objtype = list.type[0]; + return obj_check_type(objtype, haspobjtype); +#endif +} #define HASP_OBJ_BAR 1971 #define HASP_OBJ_BTN 3164 @@ -109,7 +166,9 @@ void object_set_normalized_group_value(uint8_t groupid, lv_obj_t* src_obj, int16 #define HASP_OBJ_OBJ 53623 #define HASP_OBJ_OBJMASK 55395 #define HASP_OBJ_LMETER 62749 +#define HASP_OBJ_LINEMETER 55189 #define HASP_OBJ_TABVIEW 63226 +#define HASP_OBJ_TAB 7861 #define HASP_OBJ_ARC 64594 #endif \ No newline at end of file diff --git a/src/hasp/hasp_page.cpp b/src/hasp/hasp_page.cpp index 9305cc4e..acc737c6 100644 --- a/src/hasp/hasp_page.cpp +++ b/src/hasp/hasp_page.cpp @@ -1,10 +1,8 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "hasp_conf.h" #include "hasplib.h" - -#include "hasp_page.h" +#include namespace hasp { @@ -21,14 +19,19 @@ Page::Page() // LVGL is not yet initialized at construction time } -size_t Page::count() +uint8_t Page::count() { - return sizeof(_pages) / sizeof(*_pages); + return (uint8_t)(sizeof(_pages) / sizeof(*_pages)); } void Page::init(uint8_t start_page) { + lv_obj_t* scr_act = lv_scr_act(); + lv_obj_clean(lv_layer_top()); + for(int i = 0; i < count(); i++) { + lv_obj_t* prev_page_obj = _pages[i]; + _pages[i] = lv_obj_create(NULL, NULL); _pages[i]->user_data.objid = LV_HASP_SCREEN; lv_obj_set_event_cb(_pages[i], generic_event_handler); @@ -40,6 +43,14 @@ void Page::init(uint8_t start_page) _meta_data[i].prev = thispage == PAGE_START_INDEX ? HASP_NUM_PAGES : thispage - PAGE_START_INDEX; _meta_data[i].next = thispage == HASP_NUM_PAGES ? PAGE_START_INDEX : thispage + PAGE_START_INDEX; _meta_data[i].back = start_page; + + if(prev_page_obj) { + if(scr_act == prev_page_obj) { + lv_scr_load_anim(_pages[i], LV_SCR_LOAD_ANIM_NONE, 500, 0, false); // update page screen obj + lv_obj_del_async(prev_page_obj); + } else + lv_obj_del(prev_page_obj); + } } } @@ -67,12 +78,12 @@ void Page::set(uint8_t pageid, lv_scr_load_anim_t animation) } else if(!page) { LOG_WARNING(TAG_HASP, F(D_HASP_INVALID_PAGE), pageid); } else { - LOG_TRACE(TAG_HASP, F(D_HASP_CHANGE_PAGE), pageid); - if(_current_page != pageid) { - _current_page = pageid; + _current_page = pageid; + if(page != lv_scr_act()) { + LOG_TRACE(TAG_HASP, F(D_HASP_CHANGE_PAGE), pageid); lv_scr_load_anim(page, animation, 500, 0, false); + hasp_object_tree(page, pageid, 0); } - hasp_object_tree(page, pageid, 0); } } @@ -132,30 +143,66 @@ void Page::load_jsonl(const char* pagesfile) if(pagesfile[0] == '\0') return; if(!filesystemSetup()) { - LOG_ERROR(TAG_HASP, F("FS not mounted. Failed to load %s"), pagesfile); + LOG_ERROR(TAG_HASP, F("FS not mounted. " D_FILE_LOAD_FAILED), pagesfile); return; } if(!HASP_FS.exists(pagesfile)) { - LOG_ERROR(TAG_HASP, F("Non existing file %s"), pagesfile); + LOG_ERROR(TAG_HASP, F(D_FILE_LOAD_FAILED), pagesfile); return; } - LOG_TRACE(TAG_HASP, F("Loading file %s"), pagesfile); + LOG_TRACE(TAG_HASP, F(D_FILE_LOADING), pagesfile); File file = HASP_FS.open(pagesfile, "r"); dispatch_parse_jsonl(file); file.close(); - LOG_INFO(TAG_HASP, F("File %s loaded"), pagesfile); -#else + LOG_INFO(TAG_HASP, F(D_FILE_LOADED), pagesfile); -#if HASP_USE_EEPROM > 0 +#elif HASP_USE_EEPROM > 0 LOG_TRACE(TAG_HASP, F("Loading jsonl from EEPROM...")); EepromStream eepromStream(4096, 1024); dispatch_parse_jsonl(eepromStream); LOG_INFO(TAG_HASP, F("Loaded jsonl from EEPROM")); -#endif + +#else + + char path[strlen(pagesfile) + 4]; + path[0] = '.'; + path[1] = '\0'; + strcat(path, pagesfile); + path[1] = '\\'; + + LOG_TRACE(TAG_HASP, F("Loading %s from disk..."), path); + std::ifstream f(path); // taking file as inputstream + if(f) { + dispatch_parse_jsonl(f); + } + f.close(); + LOG_INFO(TAG_HASP, F("Loaded %s from disk"), path); + + // char path[strlen(pagesfile) + 4]; + // path[0] = '\0'; + // strcat(path, "L:/"); + // strcat(path, pagesfile); + + // lv_fs_file_t file; + // lv_fs_res_t res; + // res = lv_fs_open(&file, path, LV_FS_MODE_RD); + // if(res == LV_FS_RES_OK) { + // LOG_VERBOSE(TAG_HASP, F("Opening %s"), path); + // } else { + // LOG_ERROR(TAG_HASP, F("TEST Opening %q from FS failed %d"), path, res); + // } + + // dispatch_parse_jsonl(file); + // res = lv_fs_close(&file); + // if(res == LV_FS_RES_OK) { + // LOG_VERBOSE(TAG_HASP, F("Closing %s OK"), path); + // } else { + // LOG_ERROR(TAG_HASP, F("Closing %s on FS failed %d"), path, res); + // } #endif } @@ -168,7 +215,7 @@ lv_obj_t* Page::get_obj(uint8_t pageid) return _pages[pageid - PAGE_START_INDEX]; } -bool Page::get_id(lv_obj_t* obj, uint8_t* pageid) +bool Page::get_id(const lv_obj_t* obj, uint8_t* pageid) { lv_obj_t* page = lv_obj_get_screen(obj); diff --git a/src/hasp/hasp_page.h b/src/hasp/hasp_page.h index ec94462e..1872eb5f 100644 --- a/src/hasp/hasp_page.h +++ b/src/hasp/hasp_page.h @@ -4,7 +4,6 @@ #ifndef HASP_PAGES_H #define HASP_PAGES_H -#include "hasp_conf.h" #include "hasplib.h" /********************* @@ -33,7 +32,7 @@ class Page { public: Page(); - size_t count(); + uint8_t count(); void init(uint8_t start_page); void clear(uint8_t pageid); // void set(uint8_t pageid); @@ -46,6 +45,7 @@ class Page { uint8_t get_next(uint8_t pageid); uint8_t get_prev(uint8_t pageid); uint8_t get_back(uint8_t pageid); + void set_next(uint8_t pageid, uint8_t nextid); void set_prev(uint8_t pageid, uint8_t previd); void set_back(uint8_t pageid, uint8_t backid); @@ -53,7 +53,7 @@ class Page { uint8_t get(); void load_jsonl(const char* pagesfile); lv_obj_t* get_obj(uint8_t pageid); - bool get_id(lv_obj_t* obj, uint8_t* pageid); + bool get_id(const lv_obj_t* obj, uint8_t* pageid); bool is_valid(uint8_t pageid); }; diff --git a/src/hasp/hasp_parser.cpp b/src/hasp/hasp_parser.cpp index 3ac427c1..036ecc23 100644 --- a/src/hasp/hasp_parser.cpp +++ b/src/hasp/hasp_parser.cpp @@ -1,17 +1,11 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include -#include -#include - #ifdef ARDUINO #include "pgmspace.h" #include "Arduino.h" #endif -#include "lvgl.h" - #include "hasplib.h" bool Parser::haspPayloadToColor(const char* payload, lv_color32_t& color) @@ -177,18 +171,20 @@ bool Parser::is_only_digits(const char* s) int Parser::format_bytes(size_t filesize, char* buf, size_t len) { - if(filesize < 1024) return snprintf_P(buf, len, PSTR("%d B"), filesize); + if(filesize < D_FILE_SIZE_DIVIDER) return snprintf_P(buf, len, PSTR("%d " D_FILE_SIZE_BYTES), filesize); + filesize = filesize * 10; - char labels[] = "kMGT"; - filesize = filesize * 10 / 1024; // multiply by 10 for 1 decimal place - int unit = 0; + filesize = filesize / D_FILE_SIZE_DIVIDER; // multiply by 10 for 1 decimal place + if(filesize < D_FILE_SIZE_DIVIDER * 10) + return snprintf_P(buf, len, PSTR("%d" D_DECIMAL_POINT "%d " D_FILE_SIZE_KILOBYTES), filesize / 10, + filesize % 10); - while(filesize >= 10240 && unit < sizeof(labels) - 1) { // it is multiplied by 10 - unit++; - filesize = filesize / 1024; - } + filesize = filesize / D_FILE_SIZE_DIVIDER; // multiply by 10 for 1 decimal place + if(filesize < D_FILE_SIZE_DIVIDER * 10) + return snprintf_P(buf, len, PSTR("%d" D_DECIMAL_POINT "%d " D_FILE_SIZE_MEGABYTES), filesize / 10, + filesize % 10); - return snprintf_P(buf, len, PSTR("%d.%d %ciB"), filesize / 10, filesize % 10, labels[unit]); + return snprintf_P(buf, len, PSTR("%d" D_DECIMAL_POINT "%d " D_FILE_SIZE_GIGABYTES), filesize / 10, filesize % 10); } uint8_t Parser::get_action_id(const char* action) diff --git a/src/hasp/hasp_parser.h b/src/hasp/hasp_parser.h index 862ee32f..3a2e0118 100644 --- a/src/hasp/hasp_parser.h +++ b/src/hasp/hasp_parser.h @@ -4,8 +4,7 @@ #ifndef HASP_PARSER_H #define HASP_PARSER_H -#include "lvgl.h" -#include "hasp_conf.h" +#include "hasplib.h" class Parser { diff --git a/src/hasp_config.cpp b/src/hasp_config.cpp index 6d4596a1..3f0afa47 100644 --- a/src/hasp_config.cpp +++ b/src/hasp_config.cpp @@ -3,14 +3,12 @@ #if HASP_USE_CONFIG > 0 -#include "ArduinoJson.h" -#include "StreamUtils.h" // For EEPromStream - -#include "hasp_conf.h" +#include "hasplib.h" #include "hasp_config.h" #include "hasp_debug.h" #include "hasp_gui.h" +#include "hal/hasp_hal.h" //#include "hasp_ota.h" included in conf //#include "hasp_filesystem.h" included in conf @@ -18,13 +16,13 @@ //#include "hasp_gpio.h" included in conf //#include "hasp_eeprom.h" -#include "hasp/hasp.h" -#include "hasp/hasp_dispatch.h" #if HASP_USE_EEPROM > 0 #include "EEPROM.h" #endif +#include "StreamUtils.h" // For EEPromStream + extern uint16_t dispatchTelePeriod; extern uint32_t dispatchLastMillis; @@ -76,24 +74,59 @@ bool configSet(uint16_t& value, const JsonVariant& setting, const __FlashStringH return false; } -void configStartDebug(bool setupdebug, String& configFile) +void configSetupDebug(JsonDocument& settings) +{ + debugSetup(settings[FPSTR(FP_DEBUG)]); + debugStart(); // Debug started, now we can use it; HASP header sent +} + +void configStorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass) +{ + const __FlashStringHelper* pass = F("pass"); + + wifiPass = settings[FPSTR(FP_WIFI)][pass].as(); + mqttPass = settings[FPSTR(FP_MQTT)][pass].as(); + httpPass = settings[FPSTR(FP_HTTP)][pass].as(); +} + +void configRestorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass) +{ + const __FlashStringHelper* pass = F("pass"); + + if(!settings[FPSTR(FP_WIFI)][pass].isNull()) settings[FPSTR(FP_WIFI)][pass] = wifiPass; + if(!settings[FPSTR(FP_MQTT)][pass].isNull()) settings[FPSTR(FP_MQTT)][pass] = mqttPass; + if(!settings[FPSTR(FP_HTTP)][pass].isNull()) settings[FPSTR(FP_HTTP)][pass] = httpPass; +} + +void configMaskPasswords(JsonDocument& settings) +{ + String passmask = F(D_PASSWORD_MASK); + configRestorePasswords(settings, passmask, passmask, passmask); +} + +DeserializationError configParseFile(String& configFile, JsonDocument& settings) { - if(setupdebug) { - debugStart(); // Debug started, now we can use it; HASP header sent #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 - LOG_INFO(TAG_CONF, F("SPI flash FS mounted")); - filesystemInfo(); - filesystemList(); -#endif + File file = HASP_FS.open(configFile, "r"); + DeserializationError result; + + if(file) { + size_t size = file.size(); + if(size > 1024) { + LOG_ERROR(TAG_CONF, F("Config file size is too large")); + return DeserializationError::NoMemory; + } + result = deserializeJson(settings, file); + file.close(); + return result; } -#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 - LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), configFile.c_str()); + return DeserializationError::InvalidInput; #else - LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), "EEPROM"); + return DeserializationError::InvalidInput; #endif } -void configRead(JsonDocument& settings, bool setupdebug = false) +DeserializationError configRead(JsonDocument& settings, bool setupdebug = false) { String configFile((char*)0); configFile.reserve(32); @@ -101,40 +134,34 @@ void configRead(JsonDocument& settings, bool setupdebug = false) DeserializationError error; #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 - File file = HASP_FS.open(configFile, "r"); + error = configParseFile(configFile, settings); + if(!error) { + String output, wifiPass, mqttPass, httpPass; - if(file) { - size_t size = file.size(); - if(size > 1024) { - LOG_ERROR(TAG_CONF, F("Config file size is too large")); - return; + /* Load Debug params */ + if(setupdebug) { + configSetupDebug(settings); // Now we can use log + LOG_INFO(TAG_CONF, F("SPI flash FS mounted")); + + filesystemInfo(); + filesystemList(); } - error = deserializeJson(settings, file); - file.close(); + LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), configFile.c_str()); + configStorePasswords(settings, wifiPass, mqttPass, httpPass); - if(!error) { - /* Load Debug params */ - if(setupdebug) { - debugPreSetup(settings[FPSTR(FP_DEBUG)]); - } - configStartDebug(setupdebug, configFile); + // Output settings in log with masked passwords + configMaskPasswords(settings); + serializeJson(settings, output); + LOG_VERBOSE(TAG_CONF, output.c_str()); - // show settings in log - String output; - serializeJson(settings, output); - String passmask = F(D_PASSWORD_MASK); - const __FlashStringHelper* pass = F("pass"); - output.replace(settings[FPSTR(FP_HTTP)][pass].as(), passmask); - output.replace(settings[FPSTR(FP_MQTT)][pass].as(), passmask); - output.replace(settings[FPSTR(FP_WIFI)][pass].as(), passmask); - LOG_VERBOSE(TAG_CONF, output.c_str()); - LOG_INFO(TAG_CONF, F(D_FILE_LOADED), configFile.c_str()); + configRestorePasswords(settings, wifiPass, mqttPass, httpPass); + LOG_INFO(TAG_CONF, F(D_FILE_LOADED), configFile.c_str()); - if(setupdebug) debugSetup(); - return; - } + // if(setupdebug) debugSetup(); + return error; } + #else #if HASP_USE_EEPROM > 0 @@ -145,20 +172,18 @@ void configRead(JsonDocument& settings, bool setupdebug = false) #endif // File does not exist or error reading file - if(setupdebug) { - debugPreSetup(settings[FPSTR(FP_DEBUG)]); - } - configStartDebug(setupdebug, configFile); + if(setupdebug) configSetupDebug(settings); // Now we can use log #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 LOG_ERROR(TAG_CONF, F(D_FILE_LOAD_FAILED), configFile.c_str()); #endif -#if HASP_USE_CONFIG > 0 && defined(HASP_GPIO_TEMPLATE) - char json[96]; - snprintf(json, sizeof(json), PSTR("{\"%s\":%s}"), (char*)(FPSTR(FP_GPIO_CONFIG)), (char*)(FPSTR(FP_GPIO_TEMPLATE))); - dispatch_config((char*)(FPSTR(FP_GPIO)), json); -#endif + + configFile = F("EEPROM"); + LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), configFile.c_str()); + LOG_INFO(TAG_CONF, F(D_FILE_LOADED), configFile.c_str()); + return error; } + /* void configBackupToEeprom() { @@ -314,7 +339,9 @@ void configWrite() File file = HASP_FS.open(configFile, "w"); if(file) { LOG_TRACE(TAG_CONF, F(D_FILE_SAVING), configFile.c_str()); - size_t size = serializeJson(doc, file); + WriteBufferingStream bufferedFile(file, 256); + size_t size = serializeJson(doc, bufferedFile); + bufferedFile.flush(); file.close(); if(size > 0) { LOG_INFO(TAG_CONF, F(D_FILE_SAVED), configFile.c_str()); @@ -376,7 +403,7 @@ void configSetup() #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 if(!filesystemSetup()) { LOG_ERROR(TAG_CONF, F("FILE: SPI flash init failed. Unable to mount FS: Using default settings...")); - return; + // return; // Keep going and initialize the console with default settings } #endif configRead(settings, true); diff --git a/src/hasp_config.h b/src/hasp_config.h index 99224b06..3ab61d58 100644 --- a/src/hasp_config.h +++ b/src/hasp_config.h @@ -6,8 +6,7 @@ #ifndef HASP_CONFIG_H #define HASP_CONFIG_H -#include "hasp_conf.h" -#include "ArduinoJson.h" +#include "hasplib.h" #include "hasp_debug.h" // for TAG_CONF /* ===== Default Event Processors ===== */ @@ -18,6 +17,8 @@ void configStart(void); void configStop(void); /* ===== Special Event Processors ===== */ +DeserializationError configParseFile(String& configFile, JsonDocument& settings); +bool configRead(String configFile, JsonDocument& settings, bool setupdebug = false); void configWrite(void); void configOutput(const JsonObject& settings, uint8_t tag = TAG_CONF); bool configClearEeprom(void); @@ -26,6 +27,7 @@ bool configClearEeprom(void); bool configSet(int8_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); bool configSet(uint8_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); bool configSet(uint16_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); +void configMaskPasswords(JsonDocument& settings); /* ===== Read/Write Configuration ===== */ void configSetConfig(JsonObject& settings); @@ -71,10 +73,6 @@ const char FP_HASP[] PROGMEM = "hasp"; const char FP_GUI[] PROGMEM = "gui"; const char FP_DEBUG[] PROGMEM = "debug"; -#ifdef HASP_GPIO_TEMPLATE -const char FP_GPIO_TEMPLATE[] PROGMEM = HASP_GPIO_TEMPLATE; -#endif - #endif #endif // HASP_USE_CONFIG \ No newline at end of file diff --git a/src/hasp_debug.cpp b/src/hasp_debug.cpp index 83fcad2f..699a02fc 100644 --- a/src/hasp_debug.cpp +++ b/src/hasp_debug.cpp @@ -1,15 +1,12 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "ArduinoJson.h" -#include "lvgl.h" +#include "hasplib.h" #include #include "lang/lang.h" -#include "hasp_conf.h" #include "hasp_debug.h" #include "hasp_macro.h" -#include "hasp/hasp.h" #if(!defined(WINDOWS)) && (!defined(POSIX)) #include "ArduinoLog.h" @@ -40,6 +37,7 @@ inline void debugSendAnsiCode(const __FlashStringHelper* code, Print* _logOutput #endif } +/* void debug_timestamp() { timeval curTime; @@ -51,7 +49,7 @@ void debug_timestamp() // strftime(currentTime, 80, "%Y-%m-%d %H:%M.%S", localtime(&t)); strftime(currentTime, 80, "%H:%M:%S", localtime(&t)); printf("[%s.%03d] ", currentTime, milli); -} +} */ static void debugPrintTimestamp(int level, Print* _logOutput) { /* Print Current Time */ @@ -60,35 +58,34 @@ static void debugPrintTimestamp(int level, Print* _logOutput) int rslt = gettimeofday(&curTime, NULL); time_t t = curTime.tv_sec; tm* timeinfo = localtime(&t); - int milli = curTime.tv_usec / 1000; + (void)rslt; // unused debugSendAnsiCode(F(TERM_COLOR_CYAN), _logOutput); if(timeinfo->tm_year >= 120) { + unsigned long int milli = curTime.tv_usec / 1000; char buffer[24]; - strftime(buffer, sizeof(buffer), "[%b %d %H:%M:%S", timeinfo); // Literal String - // strftime(buffer, sizeof(buffer), "[%H:%M:%S.", timeinfo); // Literal String + // strftime(buffer, sizeof(buffer), "[%b %d %H:%M:%S", timeinfo); // Literal String + strftime(buffer, sizeof(buffer), "[" D_TIMESTAMP, timeinfo); // Literal String #ifdef ARDUINO - _logOutput->printf(PSTR("%s.%03lu]"), buffer, curTime.tv_usec / 1000); + _logOutput->printf(PSTR("%s.%03lu]"), buffer, milli); #else - debug_print(_logOutput, PSTR("%s.%03lu]"), buffer, curTime.tv_usec / 1000); + debug_print(_logOutput, PSTR("%s.%03lu]"), buffer, milli); #endif } else { uint32_t msecs = millis(); #ifdef ARDUINO - _logOutput->printf(PSTR("[%15d.%03d]"), msecs / 1000, msecs % 1000); + _logOutput->printf(PSTR("[" D_TIME_MILLIS ".%03d]"), msecs / 1000, msecs % 1000); #else - debug_print(_logOutput, PSTR("[%15d.%03d]"), msecs / 1000, msecs % 1000); + debug_print(_logOutput, PSTR("[" D_TIME_MILLIS ".%03d]"), msecs / 1000, msecs % 1000); #endif } } /* ===== Default Event Processors ===== */ -// void debugPreSetup(JsonObject settings); -// void debugSetup(); static inline void debug_flush() { @@ -104,7 +101,7 @@ static inline void debug_flush() void debugEverySecond() { // if(debugTelePeriod > 0 && (millis() - debugLastMillis) >= debugTelePeriod * 1000) { - // dispatch_output_statusupdate(NULL, NULL); + // dispatch_statusupdate(NULL, NULL); // debugLastMillis = millis(); // } // printLocalTime(); @@ -123,7 +120,7 @@ void debugStart() #endif if(debugSerialStarted) { - debug_flush; + debug_flush(); // Serial.println(); // Serial.println(debugHaspHeader()); @@ -180,22 +177,29 @@ void debugLvglLogEvent(lv_log_level_t level, const char* file, uint32_t line, co // Send the HASP header and version to the output device specified void debugPrintHaspHeader(Print* output) { - // if(debugAnsiCodes) debug_print(output,TERM_COLOR_YELLOW); - - // debug_newline(output); - // debug_print(output, F("" - // " _____ _____ _____ _____\r\n" - // " | | | _ | __| _ |\r\n" - // " | | |__ | __|\r\n" - // " |__|__|__|__|_____|__|\r\n" - // " Home Automation Switch Plate\r\n" - // " Open Hardware edition v")); - char buffer[32]; - haspGetVersion(buffer, sizeof(buffer)); #ifdef ARDUINO - output->println(buffer); + if(debugAnsiCodes) output->print(TERM_COLOR_YELLOW); + output->println(); + output->print(F("\r\n" + " open____ _____ _____ _____\r\n" + " | | | _ | __| _ |\r\n" + " | | |__ | __|\r\n" + " |__|__|__|__|_____|__|\r\n" + " Home Automation Switch Plate\r\n" + " Open Hardware edition v")); + output->println(haspDevice.get_version()); + output->println(); #else - debug_print(output, buffer); + if(debugAnsiCodes) debug_print(output, TERM_COLOR_YELLOW); + debug_print(output, F("\r\n" + " open____ _____ _____ _____\r\n" + " | | | _ | __| _ |\r\n" + " | | |__ | __|\r\n" + " |__|__|__|__|_____|__|\r\n" + " Home Automation Switch Plate\r\n" + " Open Hardware edition v")); + debug_print(output, haspDevice.get_version()); + debug_newline(output); debug_newline(output); #endif } @@ -234,6 +238,9 @@ void debug_get_tag(uint8_t tag, char* buffer) memcpy_P(buffer, PSTR("HAL "), 5); break; + case TAG_CONS: + memcpy_P(buffer, PSTR("CONS"), 5); + break; case TAG_DEBG: memcpy_P(buffer, PSTR("DBUG"), 5); break; @@ -338,8 +345,6 @@ static void debugPrintLvglMemory(int level, Print* _logOutput) lv_mem_monitor_t mem_mon; lv_mem_monitor(&mem_mon); - if(mem_mon.frag_pct > 20) lv_mem_defrag(); // prevents LED shadow crashing - /* Print LVGL Memory Info */ if(debugAnsiCodes) { if(mem_mon.free_biggest_size > (1024u * 2) && (mem_mon.free_size > 1024u * 2.5) && (mem_mon.frag_pct <= 10)) diff --git a/src/hasp_debug.h b/src/hasp_debug.h index 615308b8..c60ffc13 100644 --- a/src/hasp_debug.h +++ b/src/hasp_debug.h @@ -14,8 +14,7 @@ #include "ArduinoLog.h" /* ===== Default Event Processors ===== */ -void debugPreSetup(JsonObject settings); -void debugSetup(); +void debugSetup(JsonObject settings); /* ===== Special Event Processors ===== */ @@ -85,6 +84,46 @@ void debugStopSyslog(void); std::cout << std::endl; \ fflush(stdout) +/* json keys used in the configfile */ +// const char FP_CONFIG_STARTPAGE[] PROGMEM = "startpage"; +// const char FP_CONFIG_STARTDIM[] PROGMEM = "startdim"; +// const char FP_CONFIG_THEME[] PROGMEM = "theme"; +// const char FP_CONFIG_HUE[] PROGMEM = "hue"; +// const char FP_CONFIG_ZIFONT[] PROGMEM = "font"; +// const char FP_CONFIG_PAGES[] PROGMEM = "pages"; +// const char FP_CONFIG_ENABLE[] PROGMEM = "enable"; +// const char FP_CONFIG_HOST[] PROGMEM = "host"; +// const char FP_CONFIG_PORT[] PROGMEM = "port"; +// const char FP_CONFIG_NAME[] PROGMEM = "name"; +// const char FP_CONFIG_USER[] PROGMEM = "user"; +// const char FP_CONFIG_PASS[] PROGMEM = "pass"; +// const char FP_CONFIG_SSID[] PROGMEM = "ssid"; +// const char FP_CONFIG_GROUP[] PROGMEM = "group"; +// const char FP_CONFIG_BAUD[] PROGMEM = "baud"; +// const char FP_CONFIG_LOG[] PROGMEM = "log"; +// const char FP_CONFIG_PROTOCOL[] PROGMEM = "proto"; +// const char FP_GUI_ROTATION[] PROGMEM = "rotate"; +// const char FP_GUI_INVERT[] PROGMEM = "invert"; +// const char FP_GUI_TICKPERIOD[] PROGMEM = "tick"; +// const char FP_GUI_IDLEPERIOD1[] PROGMEM = "idle1"; +// const char FP_GUI_IDLEPERIOD2[] PROGMEM = "idle2"; +// const char FP_GUI_CALIBRATION[] PROGMEM = "calibration"; +// const char FP_GUI_BACKLIGHTPIN[] PROGMEM = "bckl"; +// const char FP_GUI_POINTER[] PROGMEM = "cursor"; +// const char FP_DEBUG_TELEPERIOD[] PROGMEM = "tele"; +// const char FP_GPIO_CONFIG[] PROGMEM = "config"; + +// const char FP_HASP_CONFIG_FILE[] PROGMEM = "/config.json"; + +// const char FP_WIFI[] PROGMEM = "wifi"; +// const char FP_MQTT[] PROGMEM = "mqtt"; +// const char FP_HTTP[] PROGMEM = "http"; +// const char FP_GPIO[] PROGMEM = "gpio"; +// const char FP_MDNS[] PROGMEM = "mdns"; +// const char FP_HASP[] PROGMEM = "hasp"; +// const char FP_GUI[] PROGMEM = "gui"; +// const char FP_DEBUG[] PROGMEM = "debug"; + #endif #ifdef __cplusplus @@ -93,7 +132,7 @@ extern "C" { // Functions used by ANDROID, WINDOWS and POSSIX void debugLvglLogEvent(lv_log_level_t level, const char* file, uint32_t line, const char* funcname, const char* descr); -void debugLoop(void); +IRAM_ATTR void debugLoop(void); void debugEverySecond(void); void debugStart(void); void debugStop(void); @@ -123,9 +162,10 @@ enum { TAG_EVENT = 8, TAG_DEBG = 10, - TAG_TELN = 11, - TAG_SYSL = 12, - TAG_TASM = 13, + TAG_CONS = 11, + TAG_TELN = 12, + TAG_SYSL = 13, + TAG_TASM = 14, TAG_CONF = 20, TAG_GUI = 21, diff --git a/src/hasp_filesystem.cpp b/src/hasp_filesystem.cpp index 6a014e7d..1d640a01 100644 --- a/src/hasp_filesystem.cpp +++ b/src/hasp_filesystem.cpp @@ -14,56 +14,14 @@ void filesystemInfo() { // Get all information of your SPIFFS -#if 0 +#ifdef ESP8266 FSInfo fs_info; - SPIFFS.info(fs_info); + HASP_FS.info(fs_info); + Log.verbose(TAG_FILE, "Partition size: total: %d, used: %d", fs_info.totalBytes, fs_info.usedBytes); +#endif - Serial.println("File system info."); - - Serial.print("Total space: "); - Serial.print(fs_info.totalBytes); - Serial.println("byte"); - - Serial.print("Total space used: "); - Serial.print(fs_info.usedBytes); - Serial.println("byte"); - - Serial.print("Block size: "); - Serial.print(fs_info.blockSize); - Serial.println("byte"); - - Serial.print("Page size: "); - Serial.print(fs_info.totalBytes); - Serial.println("byte"); - - Serial.print("Max open files: "); - Serial.println(fs_info.maxOpenFiles); - - Serial.print("Max path lenght: "); - Serial.println(fs_info.maxPathLength); - Serial.println("File sistem info."); - - Serial.print("Total space: "); - Serial.print(SPIFFS.totalBytes()); - Serial.println("byte"); - - Serial.print("Total space used: "); - Serial.print(SPIFFS.usedBytes()); - Serial.println("byte"); - - Serial.print("Block size: "); - // Serial.print(SPIFFS); - Serial.println("byte"); - - Serial.print("Page size: "); - Serial.print(SPIFFS.totalBytes()); - Serial.println("byte"); - - Serial.print("Max open files: "); - // Serial.println(SPIFFS.maxOpenFiles()); - - Serial.print("Max path lenght: "); - // Serial.println(SPIFFS.maxPathLength()); +#ifdef ESP32 + Log.verbose(TAG_FILE, "Partition size: total: %d, used: %d", HASP_FS.totalBytes(), HASP_FS.usedBytes()); #endif } @@ -71,9 +29,9 @@ void filesystemList() { #if HASP_USE_SPIFFS > 0 #if defined(ARDUINO_ARCH_ESP8266) - if(!SPIFFS.begin()) { + if(!HASP_FS.begin()) { #else - if(!SPIFFS.begin(true)) { + if(!HASP_FS.begin(true)) { // default vfs path: /littlefs #endif LOG_ERROR(TAG_FILE, F("Flash file system not mouted.")); } else { @@ -81,7 +39,7 @@ void filesystemList() LOG_VERBOSE(TAG_FILE, F("Listing files on the internal flash:")); #if defined(ARDUINO_ARCH_ESP32) - File root = SPIFFS.open("/"); + File root = HASP_FS.open("/"); File file = root.openNextFile(); while(file) { LOG_VERBOSE(TAG_FILE, F(" * %s (%u bytes)"), file.name(), (uint32_t)file.size()); @@ -89,7 +47,7 @@ void filesystemList() } #endif #if defined(ARDUINO_ARCH_ESP8266) - Dir dir = SPIFFS.openDir("/"); + Dir dir = HASP_FS.openDir("/"); while(dir.next()) { LOG_VERBOSE(TAG_FILE, F(" * %s (%u bytes)"), dir.fileName().c_str(), (uint32_t)dir.fileSize()); } diff --git a/src/hasp_gui.cpp b/src/hasp_gui.cpp index f55c14f0..87df5e69 100644 --- a/src/hasp_gui.cpp +++ b/src/hasp_gui.cpp @@ -1,10 +1,8 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "hasp_conf.h" +#include "hasplib.h" -#include "lv_conf.h" -#include "lvgl.h" #include "lv_drv_conf.h" // Filesystem Driver @@ -24,14 +22,12 @@ #include "hasp_gui.h" #include "hasp_oobe.h" -#include "hasplib.h" - //#include "tpcal.h" //#include "Ticker.h" #if HASP_USE_PNGDECODE > 0 -#include "png_decoder.h" +#include "lv_png.h" #endif #define BACKLIGHT_CHANNEL 0 // pwm channel 0-15 @@ -52,7 +48,7 @@ File pFileOut; #define INVERT_COLORS 0 #endif -// static void IRAM_ATTR lv_tick_handler(void); +// HASP_ATTRIBUTE_FAST_MEM static void lv_tick_handler(void); gui_conf_t gui_settings = {.show_pointer = false, .backlight_pin = TFT_BCKL, @@ -60,8 +56,8 @@ gui_conf_t gui_settings = {.show_pointer = false, .invert_display = INVERT_COLORS, .cal_data = {0, 65535, 0, 65535, 0}}; -// static int8_t guiDimLevel = 100; -// bool guiBacklightIsOn; +uint16_t tft_width = TFT_WIDTH; +uint16_t tft_height = TFT_HEIGHT; // #if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) // static Ticker tick; /* timer for interrupt handler */ @@ -76,7 +72,8 @@ gui_conf_t gui_settings = {.show_pointer = false, // { // lv_tick_inc(LVGL_TICK_PERIOD); // } -void gui_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) + +IRAM_ATTR void gui_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) { haspTft.flush_pixels(disp, area, color_p); } @@ -98,20 +95,24 @@ void guiCalibrate(void) #endif } -void guiSetup(void) +void guiSetup() { // Register logger to capture lvgl_init output - LOG_TRACE(TAG_LVGL, F(D_SERVICE_STARTING)); -#if LV_USE_LOG != 0 - lv_log_register_print_cb(debugLvglLogEvent); -#endif + LOG_TRACE(TAG_TFT, F(D_SERVICE_STARTING)); // Initialize the TFT - haspTft.init(240, 320); + haspTft.init(tft_width, tft_height); haspTft.set_rotation(gui_settings.rotation); haspTft.set_invert(gui_settings.invert_display); haspTft.show_info(); + LOG_INFO(TAG_TFT, F(D_SERVICE_STARTED)); + LOG_TRACE(TAG_LVGL, F(D_SERVICE_STARTING)); + +#if LV_USE_LOG != 0 + lv_log_register_print_cb(debugLvglLogEvent); +#endif + /* Create the Virtual Device Buffers */ #if defined(ARDUINO_ARCH_ESP32) @@ -167,13 +168,16 @@ void guiSetup(void) LOG_FATAL(TAG_GUI, F(D_ERROR_OUT_OF_MEMORY)); } + LOG_VERBOSE(TAG_LVGL, F("Version : %u.%u.%u %s"), LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH, + PSTR(LVGL_VERSION_INFO)); + /* Initialize the display driver */ static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.buffer = &disp_buf; disp_drv.flush_cb = gui_flush_cb; - disp_drv.hor_res = TFT_WIDTH; - disp_drv.ver_res = TFT_HEIGHT; + disp_drv.hor_res = tft_width; + disp_drv.ver_res = tft_height; switch(gui_settings.rotation) { case 1: @@ -181,43 +185,52 @@ void guiSetup(void) case 5: case 7: // lv_disp_set_rotation(display, LV_DISP_ROT_90); - disp_drv.hor_res = TFT_HEIGHT; - disp_drv.ver_res = TFT_WIDTH; + disp_drv.hor_res = tft_height; + disp_drv.ver_res = tft_width; break; default: // lv_disp_set_rotation(display, LV_DISP_ROT_NONE); - disp_drv.hor_res = TFT_WIDTH; - disp_drv.ver_res = TFT_HEIGHT; + disp_drv.hor_res = tft_width; + disp_drv.ver_res = tft_height; } lv_disp_t* display = lv_disp_drv_register(&disp_drv); + (void)display; // unused /* Initialize Filesystems */ #if LV_USE_FS_IF != 0 - // _lv_fs_init(); // lvgl File System -- not neaded, it done in lv_init() when LV_USE_FILESYSTEM is set - LOG_VERBOSE(TAG_LVGL, F("Filesystem : Enabled")); + //_lv_fs_init(); // lvgl File System -- not neaded, it done in lv_init() when LV_USE_FILESYSTEM is set + LOG_VERBOSE(TAG_LVGL, F("Filesystem : " D_SETTING_ENABLED)); lv_fs_if_init(); // auxilary file system drivers - filesystem_list_path("S:/fs/"); + filesystem_list_path("L:/"); + + lv_fs_file_t f; + lv_fs_res_t res; + res = lv_fs_open(&f, "L:/config.json", LV_FS_MODE_RD); + if(res == LV_FS_RES_OK) { + LOG_VERBOSE(TAG_HASP, F("TEST Opening config.json OK")); + lv_fs_close(&f); + } else { + LOG_ERROR(TAG_HASP, F("TEST Opening config.json from FS failed %d"), res); + } + #else - LOG_VERBOSE(TAG_LVGL, F("Filesystem : Disabled")); + LOG_VERBOSE(TAG_LVGL, F("Filesystem : " D_SETTING_DISABLED)); #endif /* Initialize PNG decoder */ #if HASP_USE_PNGDECODE > 0 - png_decoder_init(); + lv_png_init(); #endif #ifdef USE_DMA_TO_TFT - LOG_VERBOSE(TAG_GUI, F("DMA : ENABLED")); + LOG_VERBOSE(TAG_GUI, F("DMA : " D_SETTING_ENABLED)); #else - LOG_VERBOSE(TAG_GUI, F("DMA : DISABLED")); + LOG_VERBOSE(TAG_GUI, F("DMA : " D_SETTING_DISABLED)); #endif /* Setup Backlight Control Pin */ haspDevice.set_backlight_pin(gui_settings.backlight_pin); - LOG_VERBOSE(TAG_LVGL, F("Version : %u.%u.%u %s"), LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH, - PSTR(LVGL_VERSION_INFO)); - #ifdef LV_MEM_SIZE LOG_VERBOSE(TAG_LVGL, F("MEM size : %d"), LV_MEM_SIZE); #endif @@ -280,10 +293,13 @@ void guiSetup(void) lv_obj_set_style_local_bg_opa(lv_layer_sys(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); // guiStart(); // Ticker + LOG_INFO(TAG_LVGL, F(D_SERVICE_STARTED)); } -void guiLoop(void) +IRAM_ATTR void guiLoop(void) { + lv_task_handler(); // process animations + #if defined(STM32F4xx) // tick.update(); #endif @@ -578,7 +594,7 @@ void guiTakeScreenshot(const char* pFileName) pFileOut.close(); } else { - LOG_WARNING(TAG_GUI, F("%s cannot be opened"), pFileName); + LOG_WARNING(TAG_GUI, F(D_FILE_SAVE_FAILED), pFileName); } } #endif diff --git a/src/hasp_gui.h b/src/hasp_gui.h index 3e33d068..0c8a25d4 100644 --- a/src/hasp_gui.h +++ b/src/hasp_gui.h @@ -4,8 +4,7 @@ #ifndef HASP_GUI_H #define HASP_GUI_H -#include "ArduinoJson.h" -#include "lvgl.h" +#include "hasplib.h" struct gui_conf_t { @@ -18,7 +17,7 @@ struct gui_conf_t /* ===== Default Event Processors ===== */ void guiSetup(void); -void guiLoop(void); +IRAM_ATTR void guiLoop(void); void guiEverySecond(void); void guiStart(void); void guiStop(void); diff --git a/src/hasp_oobe.cpp b/src/hasp_oobe.cpp index 306a0135..6a298840 100644 --- a/src/hasp_oobe.cpp +++ b/src/hasp_oobe.cpp @@ -3,12 +3,7 @@ #if HASP_USE_CONFIG > 0 -#include "hasp_conf.h" - -#include "lvgl.h" -#if LVGL_VERSION_MAJOR != 7 -#include "../lv_components.h" -#endif +#include "hasplib.h" #include "hasp_gui.h" #include "hasp_config.h" @@ -30,7 +25,7 @@ static lv_obj_t* oobepage[2]; static lv_obj_t* oobekb; lv_obj_t* pwd_ta; -static inline void oobeSetPage(uint8_t pageid) +static void oobeSetPage(uint8_t pageid) { lv_scr_load(oobepage[pageid]); lv_obj_invalidate(lv_disp_get_layer_sys(NULL)); @@ -57,8 +52,8 @@ static void kb_event_cb(lv_obj_t* event_kb, lv_event_t event) { if(event == LV_EVENT_APPLY) { StaticJsonDocument<256> settings; - char ssid[32] = ""; - char pass[32] = ""; + char ssid[64] = ""; + char pass[64] = ""; lv_obj_t* obj; obj = hasp_find_obj_from_parent_id(oobepage[1], (uint8_t)10); diff --git a/src/hasp_png.cpp b/src/hasp_png.cpp new file mode 100644 index 00000000..ef9517a7 --- /dev/null +++ b/src/hasp_png.cpp @@ -0,0 +1,43 @@ + +/* MIT License - Copyright (c) 2019-2021 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifdef LODEPNG_NO_COMPILE_ALLOCATORS + +#include +#include "hasplib.h" +#include "hasp_png.h" + +void* lodepng_malloc(size_t size) +{ +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif + +#ifdef ESP32 + return psramFound() ? ps_malloc(size) : malloc(size); +#else + return malloc(size); +#endif +} + +/* NOTE: when realloc returns NULL, it leaves the original memory untouched */ +void* lodepng_realloc(void* ptr, size_t new_size) +{ +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif + +#ifdef ESP32 + return psramFound() ? ps_realloc(ptr, new_size) : realloc(ptr, new_size); +#else + return realloc(ptr, new_size); +#endif +} + +void lodepng_free(void* ptr) +{ + free(ptr); +} + +#endif // LODEPNG_NO_COMPILE_ALLOCATORS \ No newline at end of file diff --git a/src/hasp_png.h b/src/hasp_png.h new file mode 100644 index 00000000..0fa8b8fb --- /dev/null +++ b/src/hasp_png.h @@ -0,0 +1,25 @@ +/* MIT License - Copyright (c) 2019-2021 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifndef HASP_PNG_H +#define HASP_PNG_H + +#include + +#ifdef LODEPNG_NO_COMPILE_ALLOCATORS + +#ifdef __cplusplus +extern "C" { +#endif + +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); + +#ifdef __cplusplus +} +#endif + +#endif // LODEPNG_NO_COMPILE_ALLOCATORS + +#endif // HASP_PNG_H \ No newline at end of file diff --git a/src/hasplib.h b/src/hasplib.h index 6a447243..70cd3bfa 100644 --- a/src/hasplib.h +++ b/src/hasplib.h @@ -1,5 +1,17 @@ +#ifdef ARDUINO +#include +#endif + +#include +#include +#include +#include + #include "hasp_conf.h" +#include "lv_conf.h" +#include "lvgl.h" + #include "hasp/hasp.h" #include "hasp/hasp_attribute.h" #include "hasp/hasp_dispatch.h" @@ -9,4 +21,6 @@ #include "hasp/hasp_parser.h" #include "hasp/hasp_lvfs.h" -#include "hasp/lv_theme_hasp.h" \ No newline at end of file +#include "hasp/lv_theme_hasp.h" + +#include "ArduinoJson.h" diff --git a/src/lang/en_US.h b/src/lang/en_US.h index 2c99afd1..38df2de4 100644 --- a/src/lang/en_US.h +++ b/src/lang/en_US.h @@ -4,6 +4,8 @@ #define D_USERNAME "Username:" #define D_PASSWORD "Password:" #define D_SSID "Ssid:" +#define D_YES "Yes" // New +#define D_NO "No" // New #define D_ERROR_OUT_OF_MEMORY "Out of memory" #define D_ERROR_UNKNOWN "Unkown error" @@ -15,10 +17,16 @@ #define D_FILE_LOADING "Loading %s" #define D_FILE_LOADED "Loaded %s" #define D_FILE_LOAD_FAILED "Failed to load %s" - #define D_FILE_SAVING "Saving %s" #define D_FILE_SAVED "Saved %s" #define D_FILE_SAVE_FAILED "Failed to save %s" +#define D_FILE_NOT_FOUND "File not found" // new +#define D_FILE_SIZE_BYTES "bytes" // new +#define D_FILE_SIZE_KILOBYTES "KiB" // new +#define D_FILE_SIZE_MEGABYTES "MiB" // new +#define D_FILE_SIZE_GIGABYTES "GiB" // new +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "." // new, decimal comma or point #define D_SERVICE_STARTING "Starting..." #define D_SERVICE_STARTED "Started" @@ -28,6 +36,9 @@ #define D_SERVICE_CONNECTED "Connected" #define D_SERVICE_DISCONNECTED "Disconnected" +#define D_SETTING_ENABLED "Enabled" // New +#define D_SETTING_DISABLED "Disabled" // New + #define D_NETWORK_IP_ADDRESS_RECEIVED "Received IP address %s" #define D_NETWORK_ONLINE "online" #define D_NETWORK_OFFLINE "offline" @@ -47,7 +58,7 @@ #define D_MQTT_SUBSCRIBED "Subscribed to %s" #define D_MQTT_NOT_SUBSCRIBED "Failed to subscribe to %s" #define D_MQTT_HA_AUTO_DISCOVERY "Register HA auto-discovery" -#define D_MQTT_PAYLOAD_TOO_LONG "Payload too long (%d bytes)" +#define D_MQTT_PAYLOAD_TOO_LONG "Payload too long (%u bytes)" #define D_TELNET_CLOSING_CONNECTION "Closing session from %s" #define D_TELNET_CLIENT_LOGIN_FROM "Client login from %s" @@ -58,7 +69,6 @@ #define D_TELNET_STARTED "Telnet console started" #define D_TELNET_FAILED "Failed to start telnet console" #define D_TELNET_CLIENT_CONNECTED "Client connected" -#define D_TELNET_CLIENT_NOT_CONNECTED "Client NOT connected" #define D_TELNET_CLIENT_REJECTED "Client rejected" #define D_HASP_INVALID_PAGE "Invalid page %u" @@ -70,13 +80,15 @@ #define D_OBJECT_UNKNOWN "Unknown object" #define D_OBJECT_MISMATCH "Objects DO NOT match!" #define D_OBJECT_LOST "Lost object!" -#define D_OBJECT_CREATE_FAILED "Object %u failed" +#define D_OBJECT_CREATE_FAILED "Failed to create object id %u" #define D_OBJECT_PAGE_UNKNOWN "Page ID %u not defined" #define D_OBJECT_EVENT_UNKNOWN "Unknown Event %d" #define D_ATTRIBUTE_UNKNOWN "Unknown property %s" #define D_ATTRIBUTE_READ_ONLY "%s is read-only" #define D_ATTRIBUTE_PAGE_METHOD_INVALID "Unable to call %s on a page" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" #define D_OOBE_SSID_VALIDATED "SSID %s validated" #define D_OOBE_AUTO_CALIBRATE "Auto calibrate enabled" @@ -87,7 +99,7 @@ #define D_DISPATCH_REBOOT "Rebooting the MCU now!" #define D_JSON_FAILED "JSON parsing failed:" -#define D_JSONL_FAILED "JSONL parsing failed at line %d" +#define D_JSONL_FAILED "JSONL parsing failed at line %u" #define D_JSONL_SUCCEEDED "Jsonl fully parsed" #define D_OTA_CHECK_UPDATE "Checking updates URL: %s" @@ -97,6 +109,8 @@ #define D_OTA_UPDATE_COMPLETE "OTA Update complete" #define D_OTA_UPDATE_APPLY "Applying Firmware & Reboot" #define D_OTA_UPDATE_FAILED "OTA Update failed" +#define D_OTA_UPDATING_FIRMWARE "Updating firmware..." +#define D_OTA_UPDATING_FILESYSTEM "Updating filesystem..." #define D_HTTP_HASP_DESIGN "HASP Design" #define D_HTTP_INFORMATION "Information" @@ -118,18 +132,85 @@ #define D_HTTP_NEXT_PAGE "Next Page" #define D_HTTP_CALIBRATE "Calibrate" #define D_HTTP_SCREENSHOT "Screenshot" -#define D_HTTP_FILE_BROWSER "File Browser" +#define D_HTTP_FILE_BROWSER "File Editor" #define D_HTTP_FIRMWARE_UPGRADE "Firmware Upgrade" #define D_HTTP_UPDATE_FIRMWARE "Update Firmware" #define D_HTTP_FACTORY_RESET "Factory Reset" #define D_HTTP_MAIN_MENU "Main Menu" #define D_HTTP_REBOOT "Restart" #define D_HTTP_CONFIGURATION "Configuration" +#define D_HTTP_SENDING_PAGE "Sent %S page to %s" // New +#define D_HTTP_FOOTER "by Francis Van Roie" + +#define D_INFO_VERSION "Version" +#define D_INFO_BUILD_DATETIME "Build DateTime" +#define D_INFO_UPTIME "Uptime" +#define D_INFO_FREE_HEAP "Free Heap" +#define D_INFO_FREE_BLOCK "Free Block" +#define D_INFO_DEVICE_MEMORY "Device Memory" +#define D_INFO_LVGL_MEMORY "LVGL Memory" +#define D_INFO_TOTAL_MEMORY "Total" +#define D_INFO_FREE_MEMORY "Free" +#define D_INFO_FRAGMENTATION "Fragmentation" +#define D_INFO_PSRAM_FREE "PSRam Free" +#define D_INFO_PSRAM_SIZE "PSRam Size" +#define D_INFO_FLASH_SIZE "Flash Size" +#define D_INFO_SKETCH_USED "Program Size Used" +#define D_INFO_SKETCH_FREE "Program Size Free" +#define D_INFO_MODULE "Module" +#define D_INFO_MODEL "Model" +#define D_INFO_FREQUENCY "Frequency" +#define D_INFO_CORE_VERSION "Core Version" +#define D_INFO_RESET_REASON "Reset Reason" +#define D_INFO_STATUS "Status" +#define D_INFO_SERVER "Server" +#define D_INFO_USERNAME "Username" +#define D_INFO_CLIENTID "Client ID" +#define D_INFO_CONNECTED "Connected" +#define D_INFO_DISCONNECTED "Disconnected" +#define D_INFO_RECEIVED "Received" +#define D_INFO_PUBLISHED "Published" +#define D_INFO_FAILED "Failed" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "Wifi" +#define D_INFO_LINK_SPEED "Link Speed" +#define D_INFO_FULL_DUPLEX "Full Duplex" +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Signal Strength" +#define D_INFO_IP_ADDRESS "IP Address" +#define D_INFO_MAC_ADDRESS "MAC Address" +#define D_INFO_GATEWAY "Gateway" +#define D_INFO_DNS_SERVER "DNS Server" #define D_OOBE_MSG "Tap the screen to setup WiFi or connect to this Access Point:" #define D_OOBE_SCAN_TO_CONNECT "Scan to connect" #define D_WIFI_CONNECTING_TO "Connecting to %s" #define D_WIFI_CONNECTED_TO "Connected to %s, requesting IP..." +#define D_WIFI_RSSI_EXCELLENT "Excellent" +#define D_WIFI_RSSI_GOOD "Good" +#define D_WIFI_RSSI_FAIR "Fair" +#define D_WIFI_RSSI_WEAK "Weak" +#define D_WIFI_RSSI_BAD "Very bad" + +// new +#define D_GPIO_SWITCH "Switch" +#define D_GPIO_BUTTON "Push Button" +#define D_GPIO_TOUCH "Capacitive Touch" // new +#define D_GPIO_LED "Led" +#define D_GPIO_LED_R "Mood Red" +#define D_GPIO_LED_G "Mood Green" +#define D_GPIO_LED_B "Mood Blue" +#define D_GPIO_POWER_RELAY "Power Relay" // new +#define D_GPIO_LIGHT_RELAY "Light Relay" // new +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Serial Dimmer" +#define D_GPIO_UNKNOWN "Unknown" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Group" +#define D_GPIO_GROUP_NONE "None" +#define D_GPIO_STATE_NORMAL "Normal" // new +#define D_GPIO_STATE_INVERTED "Inverted" // new #endif \ No newline at end of file diff --git a/src/lang/es_ES.h b/src/lang/es_ES.h new file mode 100644 index 00000000..adc2b0b9 --- /dev/null +++ b/src/lang/es_ES.h @@ -0,0 +1,216 @@ +#ifndef HASP_LANG_ES_ES_H +#define HASP_LANG_ES_ES_H + +#define D_USERNAME "Usuario:" +#define D_PASSWORD "Contraseña:" +#define D_SSID "Ssid:" +#define D_YES "Si" // New +#define D_NO "No" // New + +#define D_ERROR_OUT_OF_MEMORY "Memory llena" +#define D_ERROR_UNKNOWN "Error desconocido" + +#define D_CONFIG_NOT_CHANGED "No hay cambios en la configuración" +#define D_CONFIG_CHANGED "Configuración cambiada" +#define D_CONFIG_LOADED "Configuración cargada" + +#define D_FILE_LOADING "Cargando %s" +#define D_FILE_LOADED "%s cargado" +#define D_FILE_LOAD_FAILED "No se pudo cargar %s" +#define D_FILE_SAVING "Guardando %s" +#define D_FILE_SAVED "%s guardado" +#define D_FILE_SAVE_FAILED "No se pudo guardar %s" +#define D_FILE_NOT_FOUND "Archivo no encontrado" +#define D_FILE_SIZE_BYTES "bytes" // new +#define D_FILE_SIZE_KILOBYTES "KiB" // new +#define D_FILE_SIZE_MEGABYTES "MiB" // new +#define D_FILE_SIZE_GIGABYTES "GiB" // new +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "," // new, decimal comma or point + +#define D_SERVICE_STARTING "Inicializando..." +#define D_SERVICE_STARTED "Inicializado" +#define D_SERVICE_START_FAILED "No se pudo arrancar" +#define D_SERVICE_STOPPED "Parado" +#define D_SERVICE_DISABLED "Deshabilitado" +#define D_SERVICE_CONNECTED "Conectado" +#define D_SERVICE_DISCONNECTED "Desconectado" + +#define D_SETTING_ENABLED "habilitado" // New +#define D_SETTING_DISABLED "Deshabilitado" // New + +#define D_NETWORK_IP_ADDRESS_RECEIVED "Se recibió la dirección IP: %s" +#define D_NETWORK_ONLINE "en linea" +#define D_NETWORK_OFFLINE "fuera de línea" +#define D_NETWORK_CONNECTION_FAILED "Falló la conexión" + +#define D_MQTT_DEFAULT_NAME "placa_%s" +#define D_MQTT_CONNECTING "Conectando..." +#define D_MQTT_CONNECTED "Conectado al Broker %s con el clientID %s" +#define D_MQTT_NOT_CONNECTED "No hay conexión ???" +#define D_MQTT_DISCONNECTING "Desconectando..." +#define D_MQTT_DISCONNECTED "Desconectado" +#define D_MQTT_RECONNECTING "Desconectado del broker, reconectando..." +#define D_MQTT_NOT_CONFIGURED "No se ha configurado el Broker" +#define D_MQTT_STARTED "Arrancando: %d bytes" +#define D_MQTT_FAILED "Falló:" +#define D_MQTT_INVALID_TOPIC "El mensaje tiene un tópico inválido" +#define D_MQTT_SUBSCRIBED "Subscrito a %s" +#define D_MQTT_NOT_SUBSCRIBED "No se pudo subscribir a %s" +#define D_MQTT_HA_AUTO_DISCOVERY "Registrando auto-descubrimiento en HA" +#define D_MQTT_PAYLOAD_TOO_LONG "Los datos enviados son demasiado largos(%u bytes)" + +#define D_TELNET_CLOSING_CONNECTION "Cerrando sesión de %s" +#define D_TELNET_CLIENT_LOGIN_FROM "Se ha firmado el cliente %s" +#define D_TELNET_CLIENT_CONNECT_FROM "Se ha conectado el cliente %s" +#define D_TELNET_AUTHENTICATION_FAILED "Falló la autorización!" +#define D_TELNET_INCORRECT_LOGIN_ATTEMPT "Intento de conexión incorrecta desde %s" +#define D_TELNET_STARTED "Console Telnet arrancada" +#define D_TELNET_FAILED "Falló el arranque de la consola Telnet" +#define D_TELNET_CLIENT_CONNECTED "Cliente conectado" +#define D_TELNET_CLIENT_NOT_CONNECTED "Cliente NO conectado" +#define D_TELNET_CLIENT_REJECTED "Cliente rechazado" + +#define D_HASP_INVALID_PAGE "Página inválida %u" +#define D_HASP_INVALID_LAYER "No se puede borrar una capa del sistema" +#define D_HASP_CHANGE_PAGE "Cambiando a página %u" +#define D_HASP_CLEAR_PAGE "Limpiando página %u" + +#define D_OBJECT_DELETED "Objeto borrado" +#define D_OBJECT_UNKNOWN "Objeto desconocido" +#define D_OBJECT_MISMATCH "Los objetos NO SON IGUALES!" +#define D_OBJECT_LOST "Objeto perdido!" +#define D_OBJECT_CREATE_FAILED "No se pudo crear objeto %u" +#define D_OBJECT_PAGE_UNKNOWN "La página ID %u no está definida" +#define D_OBJECT_EVENT_UNKNOWN "NO se conoce el evento %d " + +#define D_ATTRIBUTE_UNKNOWN "Propiedad %s desconocida" +#define D_ATTRIBUTE_READ_ONLY "%s es solo lectura" +#define D_ATTRIBUTE_PAGE_METHOD_INVALID "No se puede llamar %s en una página" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" // new + +#define D_OOBE_SSID_VALIDATED "SSID %s validado" +#define D_OOBE_AUTO_CALIBRATE "Auto calibración hablitada" +#define D_OOBE_CALIBRATED "Ya se ha calibrado" + +#define D_DISPATCH_COMMAND_NOT_FOUND "No se encontró el comando '%s'" +#define D_DISPATCH_INVALID_PAGE "Página inválida %s" +#define D_DISPATCH_REBOOT "Reiniciando microprocesador!" + +#define D_JSON_FAILED "No se pudo analizar JSON:" +#define D_JSONL_FAILED "El análisis del JSONL falló en la línea %u" +#define D_JSONL_SUCCEEDED "JSONL analizado" + +#define D_OTA_CHECK_UPDATE "Buscando actualización en URL: %s" +#define D_OTA_CHECK_COMPLETE "Verificación de actualizacion completa" +#define D_OTA_CHECK_FAILED "Falló la verificación de actualización: %s" +#define D_OTA_UPDATE_FIRMWARE "Actualización de firmware OTA" +#define D_OTA_UPDATE_COMPLETE "Actualización OTA completada" +#define D_OTA_UPDATE_APPLY "Aplicando el nuevo firmware y reinicio" +#define D_OTA_UPDATE_FAILED "La actualización OTA falló" +#define D_OTA_UPDATING_FIRMWARE "Actualizando el firmware..." +#define D_OTA_UPDATING_FILESYSTEM "Actualizando el sistema de archivos..." + +#define D_HTTP_HASP_DESIGN "Diseño de HASP" +#define D_HTTP_INFORMATION "Información" +#define D_HTTP_HTTP_SETTINGS "Ajustes HTTP" +#define D_HTTP_WIFI_SETTINGS "Ajustes Wifi" +#define D_HTTP_MQTT_SETTINGS "Ajustes MQTT" +#define D_HTTP_GPIO_SETTINGS "Ajustes GPIO" +#define D_HTTP_MDNS_SETTINGS "Ajustes mDNS" +#define D_HTTP_TELNET_SETTINGS "Ajustes Telnet" +#define D_HTTP_DEBUG_SETTINGS "Ajustes de depuración" +#define D_HTTP_GUI_SETTINGS "Ajustes de Pantalla" +#define D_HTTP_SAVE_SETTINGS "Guardar configuración" +#define D_HTTP_UPLOAD_FILE "Cargar archivo" +#define D_HTTP_ERASE_DEVICE "Borrar configuración" +#define D_HTTP_ADD_GPIO "Agragar un nuevo pin" +#define D_HTTP_BACK "Atrás" +#define D_HTTP_REFRESH "Refrescar" +#define D_HTTP_PREV_PAGE "Página Previa" +#define D_HTTP_NEXT_PAGE "Siguiente Página" +#define D_HTTP_CALIBRATE "Calibrar" +#define D_HTTP_SCREENSHOT "Imagen de Pantalla" +#define D_HTTP_FILE_BROWSER "Editor de Archivos" +#define D_HTTP_FIRMWARE_UPGRADE "Actualización de firmware" +#define D_HTTP_UPDATE_FIRMWARE "Actualizar firmware" +#define D_HTTP_FACTORY_RESET "Restaurar conf de fábrica" +#define D_HTTP_MAIN_MENU "Menú principal" +#define D_HTTP_REBOOT "Reiniciar" +#define D_HTTP_CONFIGURATION "Configuración" +#define D_HTTP_SENDING_PAGE "Se envió pagina %S a %s" // New +#define D_HTTP_FOOTER "por Francis Van Roie" + +#define D_INFO_VERSION "Versión" +#define D_INFO_BUILD_DATETIME "Fecha de compilación" +#define D_INFO_UPTIME "Tiempo activo" +#define D_INFO_FREE_HEAP "Heap libre" +#define D_INFO_FREE_BLOCK "Bloques libres" +#define D_INFO_DEVICE_MEMORY "Memoria de dispositivo" +#define D_INFO_LVGL_MEMORY "Memoria LVGL" +#define D_INFO_TOTAL_MEMORY "Total" +#define D_INFO_FREE_MEMORY "Libre" +#define D_INFO_FRAGMENTATION "Fragmentación" +#define D_INFO_PSRAM_FREE "PSRam libre" +#define D_INFO_PSRAM_SIZE "Tamaño PSRam " +#define D_INFO_FLASH_SIZE "Tamaño Flash" +#define D_INFO_SKETCH_USED "Memoria programa usada" +#define D_INFO_SKETCH_FREE "Memoria Programa libre" +#define D_INFO_MODULE "Módulo" +#define D_INFO_MODEL "Modelo" +#define D_INFO_FREQUENCY "Frecuencia" +#define D_INFO_CORE_VERSION "Versión del núcleo" +#define D_INFO_RESET_REASON "Razón de ultimo Reset" +#define D_INFO_STATUS "Estado" +#define D_INFO_SERVER "Servidor" +#define D_INFO_USERNAME "Nombre de usuario" +#define D_INFO_CLIENTID "ID de Cliente" +#define D_INFO_CONNECTED "Connectado" +#define D_INFO_DISCONNECTED "Desconectado" +#define D_INFO_RECEIVED "Recivido" +#define D_INFO_PUBLISHED "Publicado" +#define D_INFO_FAILED "Fallado" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "Wifi" +#define D_INFO_LINK_SPEED "Velocidad de enlace" +#define D_INFO_FULL_DUPLEX "Full Duplex" +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Potencia de señal" +#define D_INFO_IP_ADDRESS "Dirección IP" +#define D_INFO_MAC_ADDRESS "Dirección MAC" +#define D_INFO_GATEWAY "Gateway" +#define D_INFO_DNS_SERVER "Servidor DNS" + +#define D_OOBE_MSG "Toque la pantalla para ajustar WiFi o conectarse a un punto de acceso" +#define D_OOBE_SCAN_TO_CONNECT "Scanee para conectar" + +#define D_WIFI_CONNECTING_TO "Connectando a %s" +#define D_WIFI_CONNECTED_TO "Connectado a %s, pidiendo IP..." +#define D_WIFI_RSSI_EXCELLENT "Excellente" +#define D_WIFI_RSSI_GOOD "Buena" +#define D_WIFI_RSSI_FAIR "Pasable" +#define D_WIFI_RSSI_WEAK "Débil" +#define D_WIFI_RSSI_BAD "Muy baka" + +// new +#define D_GPIO_SWITCH "Switch" +#define D_GPIO_BUTTON "Botón" +#define D_GPIO_TOUCH "Capacitive Touch" // new +#define D_GPIO_LED "DEL" +#define D_GPIO_LED_R "Ánimo Red" +#define D_GPIO_LED_G "Ánimo Green" +#define D_GPIO_LED_B "Ánimo Blue" +#define D_GPIO_POWER_RELAY "Power Relé" // new +#define D_GPIO_LIGHT_RELAY "Light Relé" // new +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Atenuador serial" +#define D_GPIO_UNKNOWN "Desconocido" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Grupo" +#define D_GPIO_GROUP_NONE "Ninguno" +#define D_GPIO_STATE_NORMAL "Normal" // new +#define D_GPIO_STATE_INVERTED "Inverted" // new + +#endif \ No newline at end of file diff --git a/src/lang/fr_FR.h b/src/lang/fr_FR.h new file mode 100644 index 00000000..749e4a6d --- /dev/null +++ b/src/lang/fr_FR.h @@ -0,0 +1,215 @@ +#ifndef HASP_LANG_FR_FR_H +#define HASP_LANG_FR_FR_H + +#define D_USERNAME "Utilisateur:" +#define D_PASSWORD "Mot de passe:" +#define D_SSID "Ssid:" +#define D_YES "Oui" +#define D_NO "Non" + +#define D_ERROR_OUT_OF_MEMORY "Mémoire insuffisante " +#define D_ERROR_UNKNOWN "Erreur inconnue" + +#define D_CONFIG_NOT_CHANGED "Paramètres pas modifiés" +#define D_CONFIG_CHANGED "Paramètres modifiés" +#define D_CONFIG_LOADED "Paramètres chargés" + +#define D_FILE_LOADING "Charger %s" +#define D_FILE_LOADED "Chargé %s" +#define D_FILE_LOAD_FAILED "Échec du chargement %s" +#define D_FILE_SAVING "Enregistrer %s" +#define D_FILE_SAVED "Enregistré %s" +#define D_FILE_SAVE_FAILED "Échec de l'enregistrement %s" +#define D_FILE_NOT_FOUND "Fichier non trouvé" +#define D_FILE_SIZE_BYTES "octets" +#define D_FILE_SIZE_KILOBYTES "Kio" +#define D_FILE_SIZE_MEGABYTES "Mio" +#define D_FILE_SIZE_GIGABYTES "Gio" +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "," // new, decimal comma or point + +#define D_SERVICE_STARTING "Démarer..." +#define D_SERVICE_STARTED "Démaré" +#define D_SERVICE_START_FAILED "Échec du démarrage" +#define D_SERVICE_STOPPED "Arrêté" +#define D_SERVICE_DISABLED "Désactivé" +#define D_SERVICE_CONNECTED "Connecté" +#define D_SERVICE_DISCONNECTED "Débranché" + +#define D_SETTING_ENABLED "Activé" +#define D_SETTING_DISABLED "Désactivé" + +#define D_NETWORK_IP_ADDRESS_RECEIVED "Adresse IP reçue %s" +#define D_NETWORK_ONLINE "en ligne" +#define D_NETWORK_OFFLINE "déconnecté" +#define D_NETWORK_CONNECTION_FAILED "Échec de la connexion " + +#define D_MQTT_DEFAULT_NAME "plaque_%s" +#define D_MQTT_CONNECTING "Connexion..." +#define D_MQTT_CONNECTED "Connecté au broker %s avec ID client %s" +#define D_MQTT_NOT_CONNECTED "Pas connecté ???" +#define D_MQTT_DISCONNECTING "Déconnexion..." +#define D_MQTT_DISCONNECTED "Débranché" +#define D_MQTT_RECONNECTING "Déconnecté du broker, reconnexion..." +#define D_MQTT_NOT_CONFIGURED "Broker non configuré" +#define D_MQTT_STARTED "Démarré: %d octets " +#define D_MQTT_FAILED "Manqué:" +#define D_MQTT_INVALID_TOPIC "Message avec sujet non valide " +#define D_MQTT_SUBSCRIBED "Abonné à %s" +#define D_MQTT_NOT_SUBSCRIBED "Échec de s'abonner à %s" +#define D_MQTT_HA_AUTO_DISCOVERY "Enregistrer la détection automatique HA" +#define D_MQTT_PAYLOAD_TOO_LONG "Charge utile trop long (%u octets) " + +#define D_TELNET_CLOSING_CONNECTION "Clôture de la session %s" +#define D_TELNET_CLIENT_LOGIN_FROM "Connexion client depuis %s" +#define D_TELNET_CLIENT_CONNECT_FROM "Client connecté depuis %s" +#define D_TELNET_CLIENT_NOT_CONNECTED "Client NON connecté" +#define D_TELNET_AUTHENTICATION_FAILED "Échec de l'autorisation!" +#define D_TELNET_INCORRECT_LOGIN_ATTEMPT "Tentative incorrecte de %s" +#define D_TELNET_STARTED "Console Telnet démarré" +#define D_TELNET_FAILED "Échec du démarrage de la console telnet" +#define D_TELNET_CLIENT_CONNECTED "Client connecté" +#define D_TELNET_CLIENT_REJECTED "Client rejeté" + +#define D_HASP_INVALID_PAGE "Page non valide %u" +#define D_HASP_INVALID_LAYER "Impossible d'effacer la couche système" +#define D_HASP_CHANGE_PAGE "Changement de page %u" +#define D_HASP_CLEAR_PAGE "Effacement de la page %u" + +#define D_OBJECT_DELETED "Objet supprimé" +#define D_OBJECT_UNKNOWN "Objet inconnu" +#define D_OBJECT_MISMATCH "Objets ne correspondent PAS!" +#define D_OBJECT_LOST "Objet perdu!" +#define D_OBJECT_CREATE_FAILED "Échec de la création d'objet %u" +#define D_OBJECT_PAGE_UNKNOWN "ID de page %u non défini" +#define D_OBJECT_EVENT_UNKNOWN "Inconnu Event %d" + +#define D_ATTRIBUTE_UNKNOWN "Unknown property %s" +#define D_ATTRIBUTE_READ_ONLY "%s is read-only" +#define D_ATTRIBUTE_PAGE_METHOD_INVALID "Unable to call %s on a page" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" // new + +#define D_OOBE_SSID_VALIDATED "SSID %s validated" +#define D_OOBE_AUTO_CALIBRATE "Auto calibrate enabled" +#define D_OOBE_CALIBRATED "Already calibrated" + +#define D_DISPATCH_COMMAND_NOT_FOUND "Command '%s' not found" +#define D_DISPATCH_INVALID_PAGE "Invalid page %s" +#define D_DISPATCH_REBOOT "Rebooting the MCU now!" + +#define D_JSON_FAILED "JSON parsing failed:" +#define D_JSONL_FAILED "JSONL parsing failed at line %u" +#define D_JSONL_SUCCEEDED "Jsonl fully parsed" + +#define D_OTA_CHECK_UPDATE "Checking updates URL: %s" +#define D_OTA_CHECK_COMPLETE "Update check complete" +#define D_OTA_CHECK_FAILED "Update check failed: %s" +#define D_OTA_UPDATE_FIRMWARE "OTA Firmware Update" +#define D_OTA_UPDATE_COMPLETE "OTA Update complete" +#define D_OTA_UPDATE_APPLY "Applying Firmware & Reboot" +#define D_OTA_UPDATE_FAILED "OTA Update failed" +#define D_OTA_UPDATING_FIRMWARE "Updating firmware..." +#define D_OTA_UPDATING_FILESYSTEM "Updating filesystem..." + +#define D_HTTP_HASP_DESIGN "Conception HASP" +#define D_HTTP_INFORMATION "Information" +#define D_HTTP_HTTP_SETTINGS "Paramètres HTTP" +#define D_HTTP_WIFI_SETTINGS "Paramètres Wifi" +#define D_HTTP_MQTT_SETTINGS "Paramètres MQTT" +#define D_HTTP_GPIO_SETTINGS "Paramètres GPIO" +#define D_HTTP_MDNS_SETTINGS "Paramètres mDNS" +#define D_HTTP_TELNET_SETTINGS "Paramètres Telnet" +#define D_HTTP_DEBUG_SETTINGS "Paramètres de débogage" +#define D_HTTP_GUI_SETTINGS "Paramètres d'affichage" +#define D_HTTP_SAVE_SETTINGS "Enregistrer les paramètres" +#define D_HTTP_UPLOAD_FILE "Télécharger le fichier" +#define D_HTTP_ERASE_DEVICE "Réinitialiser tous les paramètres" +#define D_HTTP_ADD_GPIO "Ajouter une nouvelle épingles" +#define D_HTTP_BACK "Retour" +#define D_HTTP_REFRESH "Actualiser" +#define D_HTTP_PREV_PAGE "Page précédente" +#define D_HTTP_NEXT_PAGE "Page suivante" +#define D_HTTP_CALIBRATE "Calibrer" +#define D_HTTP_SCREENSHOT "Capture d'écran" +#define D_HTTP_FILE_BROWSER "Éditeur de fichiers" +#define D_HTTP_FIRMWARE_UPGRADE "Mise à jour du micrologiciel" +#define D_HTTP_UPDATE_FIRMWARE "Mettre à jour le micrologiciel" +#define D_HTTP_FACTORY_RESET "Paramètres d'usine" +#define D_HTTP_MAIN_MENU "Menu principal" +#define D_HTTP_REBOOT "Redémarrer" +#define D_HTTP_CONFIGURATION "Configuration" +#define D_HTTP_SENDING_PAGE "La page %S a été envoyée à %s" +#define D_HTTP_FOOTER "par Francis Van Roie" + +#define D_INFO_VERSION "Version" +#define D_INFO_BUILD_DATETIME "Date/heure de compilation" +#define D_INFO_UPTIME "Disponibilité" +#define D_INFO_FREE_HEAP "Tas libre" +#define D_INFO_FREE_BLOCK "Blocage libre" +#define D_INFO_DEVICE_MEMORY "Mémoire de l'appareil" +#define D_INFO_LVGL_MEMORY "Mémoire LVGL" +#define D_INFO_TOTAL_MEMORY "Total" +#define D_INFO_FREE_MEMORY "Libre" +#define D_INFO_FRAGMENTATION "Fragmentation" +#define D_INFO_PSRAM_FREE "PSRam libre" +#define D_INFO_PSRAM_SIZE "Taille PSRam" +#define D_INFO_FLASH_SIZE "Taille du flash" +#define D_INFO_SKETCH_USED "Taille utilisée du programme" +#define D_INFO_SKETCH_FREE "Taille libre du programme" +#define D_INFO_MODULE "Module" +#define D_INFO_MODEL "Modèle" +#define D_INFO_FREQUENCY "Fréquence" +#define D_INFO_CORE_VERSION "Version principale" +#define D_INFO_RESET_REASON "Raison de la réinitialisation" +#define D_INFO_STATUS "Statut" +#define D_INFO_SERVER "Serveur" +#define D_INFO_USERNAME "Nom d'utilisateur" +#define D_INFO_CLIENTID "ID client" +#define D_INFO_CONNECTED "Connecté" +#define D_INFO_DISCONNECTED "Déconnecté" +#define D_INFO_RECEIVED "Reçu" +#define D_INFO_PUBLISHED "Publié" +#define D_INFO_FAILED "Échec" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "Wifi" +#define D_INFO_LINK_SPEED "Vitesse de liaison" +#define D_INFO_FULL_DUPLEX "Duplex intégral" +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Force du signal" +#define D_INFO_IP_ADDRESS "Adresse IP" +#define D_INFO_MAC_ADDRESS "Adresse MAC" +#define D_INFO_GATEWAY "Passerelle" +#define D_INFO_DNS_SERVER "Serveur DNS" + +#define D_OOBE_MSG "Touchez l'écran pour configurer le WiFi ou branchez ce point d'accès:" +#define D_OOBE_SCAN_TO_CONNECT "Scanner pour se connecter" + +#define D_WIFI_CONNECTING_TO "Connexion à %s" +#define D_WIFI_CONNECTED_TO "Connecté à %s, demande d'IP..." +#define D_WIFI_RSSI_EXCELLENT "Excellent" +#define D_WIFI_RSSI_GOOD "Bon" +#define D_WIFI_RSSI_FAIR "Juste" +#define D_WIFI_RSSI_WEAK "Faible" +#define D_WIFI_RSSI_BAD "Très mauvais" + +#define D_GPIO_SWITCH "Interrupteur" +#define D_GPIO_BUTTON "Bouton" +#define D_GPIO_TOUCH "Touche Capacitive" +#define D_GPIO_LED "Led" +#define D_GPIO_LED_R "Humeur Rouge" +#define D_GPIO_LED_G "Humeur Vert" +#define D_GPIO_LED_B "Humeur Bleu" +#define D_GPIO_POWER_RELAY "Relais Electrique" +#define D_GPIO_LIGHT_RELAY "Relais de Lumière" +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Gradateur Série" +#define D_GPIO_UNKNOWN "Inconnu" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Groupe" +#define D_GPIO_GROUP_NONE "Aucun" +#define D_GPIO_STATE_NORMAL "Normal" +#define D_GPIO_STATE_INVERTED "Inverse" + +#endif \ No newline at end of file diff --git a/src/lang/hu_HU.h b/src/lang/hu_HU.h index 445f1230..cd4fb20e 100644 --- a/src/lang/hu_HU.h +++ b/src/lang/hu_HU.h @@ -4,6 +4,8 @@ #define D_USERNAME "Felhasználónév:" #define D_PASSWORD "Jelszó:" #define D_SSID "SSID:" +#define D_YES "Igen" +#define D_NO "Nem" #define D_ERROR_OUT_OF_MEMORY "Elfogyott a memória" #define D_ERROR_UNKNOWN "Ismeretlen hiba" @@ -15,10 +17,16 @@ #define D_FILE_LOADING "%s betöltése" #define D_FILE_LOADED "%s betöltve" #define D_FILE_LOAD_FAILED "%s betöltése nem sikerült" - +#define D_FILE_NOT_FOUND "File not found" // new #define D_FILE_SAVING "%s mentése" #define D_FILE_SAVED "%s mentve" #define D_FILE_SAVE_FAILED "%s mentése meghiúsult" +#define D_FILE_SIZE_BYTES "bytes" // new +#define D_FILE_SIZE_KILOBYTES "KiB" // new +#define D_FILE_SIZE_MEGABYTES "MiB" // new +#define D_FILE_SIZE_GIGABYTES "GiB" // new +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "," // new, decimal comma or point #define D_SERVICE_STARTING "Indítás..." #define D_SERVICE_STARTED "Elindítva" @@ -28,6 +36,9 @@ #define D_SERVICE_CONNECTED "Csatlakoztatva" #define D_SERVICE_DISCONNECTED "Szétkapcsolva" +#define D_SETTING_ENABLED "Engedélyezve" +#define D_SETTING_DISABLED "Letiltva" + #define D_NETWORK_IP_ADDRESS_RECEIVED "Beállított IP-cím: %s" #define D_NETWORK_ONLINE "online" #define D_NETWORK_OFFLINE "offline" @@ -47,7 +58,7 @@ #define D_MQTT_SUBSCRIBED "Feliratkozva: %s" #define D_MQTT_NOT_SUBSCRIBED "Nem sikerült feliratkozni: %s" #define D_MQTT_HA_AUTO_DISCOVERY "Regisztrálás HA automatikus felfedezésre" -#define D_MQTT_PAYLOAD_TOO_LONG "$$$Payload too long (%d bytes)" +#define D_MQTT_PAYLOAD_TOO_LONG "Túl hosszú payload (%u bájt)" #define D_TELNET_CLOSING_CONNECTION "Munkamenet befejezése %s-el" #define D_TELNET_CLIENT_LOGIN_FROM "Kliens bejelentkezés innen: %s" @@ -58,7 +69,6 @@ #define D_TELNET_STARTED "Telnet konzol elindítva" #define D_TELNET_FAILED "Telnet konzol elindítása meghiúsult" #define D_TELNET_CLIENT_CONNECTED "Kliens csatlakozva" -#define D_TELNET_CLIENT_NOT_CONNECTED "Kliens NEM csatlakozik" #define D_TELNET_CLIENT_REJECTED "Kliens elutasítva" #define D_HASP_INVALID_PAGE "Érvénytelen oldal: %u" @@ -77,6 +87,8 @@ #define D_ATTRIBUTE_UNKNOWN "Ismeretlen tulajdonság: %s" #define D_ATTRIBUTE_READ_ONLY "%s csak olvasható" #define D_ATTRIBUTE_PAGE_METHOD_INVALID "Nem lehet meghívni %s-t egy oldalon" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" // new #define D_OOBE_SSID_VALIDATED "%s SSID érvényes" #define D_OOBE_AUTO_CALIBRATE "Automatikus kalibrálás engedélyezve" @@ -87,7 +99,7 @@ #define D_DISPATCH_REBOOT "Az MCU most újraindul!" #define D_JSON_FAILED "JSON elemzése nem sikerült:" -#define D_JSONL_FAILED "JSONL elemzése meghiúsult a %d vonalnál" +#define D_JSONL_FAILED "JSONL elemzése meghiúsult a %u vonalnál" #define D_JSONL_SUCCEEDED "JSONL teljes körűen elemezve" #define D_OTA_CHECK_UPDATE "A frissítések ellenőrzése az URL-en: %s" @@ -97,6 +109,8 @@ #define D_OTA_UPDATE_COMPLETE "Az OTA frissítés elkészült" #define D_OTA_UPDATE_APPLY "Firmware alkalmazása és újraindítás" #define D_OTA_UPDATE_FAILED "Az OTA frissítés meghiúsult" +#define D_OTA_UPDATING_FIRMWARE "Firmware frissítés..." +#define D_OTA_UPDATING_FILESYSTEM "Fájlrendszer frissítés..." #define D_HTTP_HASP_DESIGN "Képernyő dizájn" #define D_HTTP_INFORMATION "Információk" @@ -118,18 +132,85 @@ #define D_HTTP_NEXT_PAGE "Következő oldal" #define D_HTTP_CALIBRATE "Kalibrálás" #define D_HTTP_SCREENSHOT "Képernyőkép" -#define D_HTTP_FILE_BROWSER "Fájl böngésző" +#define D_HTTP_FILE_BROWSER "Fájlkezelő" #define D_HTTP_FIRMWARE_UPGRADE "Firmware frissítés" #define D_HTTP_UPDATE_FIRMWARE "Firmware frissítése" #define D_HTTP_FACTORY_RESET "Gyári beállítások visszaállítása" #define D_HTTP_MAIN_MENU "Főmenü" #define D_HTTP_REBOOT "Újraindítás" #define D_HTTP_CONFIGURATION "Beállítások" +#define D_HTTP_SENDING_PAGE "%S oldal küldése %s-re" +#define D_HTTP_FOOTER "készítette: Francis Van Roie" + +#define D_INFO_VERSION "Verziószám" +#define D_INFO_BUILD_DATETIME "Build időpontja" +#define D_INFO_UPTIME "Uptime" +#define D_INFO_FREE_HEAP "Szabad Heap" +#define D_INFO_FREE_BLOCK "Szabad Blokk" +#define D_INFO_DEVICE_MEMORY "Eszköz Memória" +#define D_INFO_LVGL_MEMORY "LVGL Memória" +#define D_INFO_TOTAL_MEMORY "Összesen" +#define D_INFO_FREE_MEMORY "Szabad" +#define D_INFO_FRAGMENTATION "Fragmentáció" +#define D_INFO_PSRAM_FREE "PSRam szabad" +#define D_INFO_PSRAM_SIZE "PSRam méret" +#define D_INFO_FLASH_SIZE "Flash méret" +#define D_INFO_SKETCH_USED "Program használatban" +#define D_INFO_SKETCH_FREE "Program szabad" +#define D_INFO_MODULE "Modul" +#define D_INFO_MODEL "Modell" +#define D_INFO_FREQUENCY "Frekvencia" +#define D_INFO_CORE_VERSION "Core Verzió" +#define D_INFO_RESET_REASON "Újraindulás oka" +#define D_INFO_STATUS "Státusz" +#define D_INFO_SERVER "Szerver" +#define D_INFO_USERNAME "Felhasználónév" +#define D_INFO_CLIENTID "Kliens ID" +#define D_INFO_CONNECTED "Csatlakoztatva" +#define D_INFO_DISCONNECTED "Szétkapcsolva" +#define D_INFO_RECEIVED "Fogadott" +#define D_INFO_PUBLISHED "Küldött" +#define D_INFO_FAILED "Sikertelen" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "WiFi" +#define D_INFO_LINK_SPEED "Linksebesség" +#define D_INFO_FULL_DUPLEX "Full Duplex" // new +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Jelerősség" +#define D_INFO_IP_ADDRESS "IP cím" +#define D_INFO_MAC_ADDRESS "MAC cím" +#define D_INFO_GATEWAY "Átjáró" +#define D_INFO_DNS_SERVER "DNS szerver" #define D_OOBE_MSG "Koppintson a képernyőre a WiFi beállításához, vagy csatlakozzon az alábbi Access Point-hoz:" #define D_OOBE_SCAN_TO_CONNECT "Szkennelje le a csatlakozáshoz:" -#define D_WIFI_CONNECTING_TO "$$$Connecting to %s" -#define D_WIFI_CONNECTED_TO "$$$Connected to %s, requesting IP..." +#define D_WIFI_CONNECTING_TO "Csatlakozás %s-hez" +#define D_WIFI_CONNECTED_TO "Csatlakozva %s-hez, IP cím kérése..." +#define D_WIFI_RSSI_EXCELLENT "Kiváló" +#define D_WIFI_RSSI_GOOD "Jó" +#define D_WIFI_RSSI_FAIR "Elfogadható" +#define D_WIFI_RSSI_WEAK "Gyenge" +#define D_WIFI_RSSI_BAD "Rossz" + +// new +#define D_GPIO_SWITCH "Switch" +#define D_GPIO_BUTTON "Button" +#define D_GPIO_TOUCH "Capacitive Touch" // new +#define D_GPIO_LED "Led" +#define D_GPIO_LED_R "Mood Red" +#define D_GPIO_LED_G "Mood Green" +#define D_GPIO_LED_B "Mood Blue" +#define D_GPIO_POWER_RELAY "Power Relay" // new +#define D_GPIO_LIGHT_RELAY "Light Relay" // new +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Serial Dimmer" +#define D_GPIO_UNKNOWN "Unknown" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Group" +#define D_GPIO_GROUP_NONE "None" +#define D_GPIO_STATE_NORMAL "Normal" // new +#define D_GPIO_STATE_INVERTED "Inverted" // new #endif diff --git a/src/lang/lang.h b/src/lang/lang.h index 74b95923..20411ecd 100644 --- a/src/lang/lang.h +++ b/src/lang/lang.h @@ -2,17 +2,21 @@ #define HASP_LANG_H #ifndef HASP_LANGUAGE - #include "en_US.h" +#include "en_US.h" #else - #define QUOTEME(x) QUOTEME_1(x) - #define QUOTEME_1(x) #x - #define INCLUDE_FILE(x) QUOTEME(x.h) - #include INCLUDE_FILE(HASP_LANGUAGE) +#define QUOTEME(x) QUOTEME_1(x) +#define QUOTEME_1(x) #x +#define INCLUDE_FILE(x) QUOTEME(x.h) +#include INCLUDE_FILE(HASP_LANGUAGE) #endif // language independent defines #define D_PASSWORD_MASK "********" #define D_BULLET " * " #define D_MANUFACTURER "openHASP" +#define D_BACK_ICON "↩ " + +#define D_TIMESTAMP "%H:%M:%S" // Used when reference time is set from NTP +#define D_TIME_MILLIS "%8d" // Used when no reference clock could be set #endif \ No newline at end of file diff --git a/src/lang/nl_NL.h b/src/lang/nl_NL.h index bf32659a..12a600cd 100644 --- a/src/lang/nl_NL.h +++ b/src/lang/nl_NL.h @@ -1,9 +1,11 @@ -#ifndef HASP_LANG_EN_US_H -#define HASP_LANG_EN_US_H +#ifndef HASP_LANG_NL_NL_H +#define HASP_LANG_NL_NL_H #define D_USERNAME "Gebruikersnaam:" #define D_PASSWORD "Wachtwoord:" #define D_SSID "Ssid:" +#define D_YES "Ja" +#define D_NO "Nee" #define D_ERROR_OUT_OF_MEMORY "Geen geheugen bechikbaar" #define D_ERROR_UNKNOWN "Onbekende fout" @@ -15,10 +17,19 @@ #define D_FILE_LOADING "%s laden..." #define D_FILE_LOADED "%s geladen" #define D_FILE_LOAD_FAILED "%s laden mislukt" - #define D_FILE_SAVING "%s bewaren..." #define D_FILE_SAVED "%s bewaard" #define D_FILE_SAVE_FAILED "%s bewaren mislukt" +#define D_FILE_NOT_FOUND "Bestand niet gevonden" +#define D_FILE_SIZE_BYTES "bytes" +#define D_FILE_SIZE_KILOBYTES "KiB" +#define D_FILE_SIZE_MEGABYTES "MiB" +#define D_FILE_SIZE_GIGABYTES "GiB" +#define D_FILE_SIZE_DIVIDER 1024 // kibi or kilo bytes +#define D_DECIMAL_POINT "," // decimal comma or point + +#define D_SETTING_ENABLED "Ingeschakeld" +#define D_SETTING_DISABLED "Uitgeschakeld" #define D_SERVICE_STARTING "Starten..." #define D_SERVICE_STARTED "Gestart" @@ -47,17 +58,17 @@ #define D_MQTT_SUBSCRIBED "Ingeschreven op %s" #define D_MQTT_NOT_SUBSCRIBED "Inschrijving op %s mislukt" #define D_MQTT_HA_AUTO_DISCOVERY "Registeren HA auto-configuratie" -#define D_MQTT_PAYLOAD_TOO_LONG "Payload is te lang (%d bytes)" +#define D_MQTT_PAYLOAD_TOO_LONG "Payload is te lang (%u bytes)" #define D_TELNET_CLOSING_CONNECTION "Sessie sluiten van %s" #define D_TELNET_CLIENT_LOGIN_FROM "Client aangemeld van %s" #define D_TELNET_CLIENT_CONNECT_FROM "Client verbonden van %s" +#define D_TELNET_CLIENT_NOT_CONNECTED "Client NIET verbonden" #define D_TELNET_AUTHENTICATION_FAILED "Autorisatie mislukt!" #define D_TELNET_INCORRECT_LOGIN_ATTEMPT "Aanmelding van %s mislukt" #define D_TELNET_STARTED "Telnet console gestart" #define D_TELNET_FAILED "Telnet console starten is mislukt" #define D_TELNET_CLIENT_CONNECTED "Client verbonden" -#define D_TELNET_CLIENT_NOT_CONNECTED "Client NIET verbonden" #define D_TELNET_CLIENT_REJECTED "Client geweigerd" #define D_HASP_INVALID_PAGE "Ongeldige pagina %u" @@ -69,13 +80,15 @@ #define D_OBJECT_UNKNOWN "Onbekend object" #define D_OBJECT_MISMATCH "Objecten komen niet overeen!" #define D_OBJECT_LOST "Object kwijt!" -#define D_OBJECT_CREATE_FAILED "Object %u mislukt" +#define D_OBJECT_CREATE_FAILED "Object id %u aanmaken mislukt" #define D_OBJECT_PAGE_UNKNOWN "Paginga %u niet gedefinieerd" #define D_OBJECT_EVENT_UNKNOWN "Onbekend Event %d" #define D_ATTRIBUTE_UNKNOWN "Onbekend attribuut %s" #define D_ATTRIBUTE_READ_ONLY "%s is alleen-lezen" #define D_ATTRIBUTE_PAGE_METHOD_INVALID "%s is ongeldig voor een pagina" +#define D_ATTRIBUTE_ALIGN_INVALID "Ongeldig align attribuut: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Ongeldige kleur: %s" // new #define D_OOBE_SSID_VALIDATED "SSID %s gevalideerd" #define D_OOBE_AUTO_CALIBRATE "Auto calibratie actief" @@ -86,7 +99,7 @@ #define D_DISPATCH_REBOOT "De MCU wordt herstart!" #define D_JSON_FAILED "JSON verwerking mislukt:" -#define D_JSONL_FAILED "JSONL verwerking mislukt op lijn %d" +#define D_JSONL_FAILED "JSONL verwerking mislukt op lijn %u" #define D_JSONL_SUCCEEDED "Jsonl volledig verwerkt" #define D_OTA_CHECK_UPDATE "Controle update URL: %s" @@ -96,6 +109,8 @@ #define D_OTA_UPDATE_COMPLETE "OTA Firmware bijgewerkt" #define D_OTA_UPDATE_APPLY "Firmware Schrijven & Herstart" #define D_OTA_UPDATE_FAILED "OTA Update mislukt" +#define D_OTA_UPDATING_FIRMWARE "Firmware bijwerken..." +#define D_OTA_UPDATING_FILESYSTEM "Bestandsysteem bijwerken..." #define D_HTTP_HASP_DESIGN "HASP Ontwerp" #define D_HTTP_INFORMATION "Informatie" @@ -117,18 +132,84 @@ #define D_HTTP_NEXT_PAGE "Volgende Pagina" #define D_HTTP_CALIBRATE "Calibratie" #define D_HTTP_SCREENSHOT "Schermafbeelding" -#define D_HTTP_FILE_BROWSER "Bestandsverkenner" +#define D_HTTP_FILE_BROWSER "Bestandseditor" #define D_HTTP_FIRMWARE_UPGRADE "Firmware Upgrade" #define D_HTTP_UPDATE_FIRMWARE "Firmware Bijwerken" #define D_HTTP_FACTORY_RESET "Fabrieksinstellingen" #define D_HTTP_MAIN_MENU "Hoofdmenu" #define D_HTTP_REBOOT "Herstarten" #define D_HTTP_CONFIGURATION "Configuratie" +#define D_HTTP_SENDING_PAGE "Pagina %S verstuurd naar %s" // New +#define D_HTTP_FOOTER "door Francis Van Roie" + +#define D_INFO_VERSION "Versie" +#define D_INFO_BUILD_DATETIME "Gecompileerd" +#define D_INFO_UPTIME "Opgestart" +#define D_INFO_FREE_HEAP "Vrije Heap" +#define D_INFO_FREE_BLOCK "Vrije Blok" +#define D_INFO_DEVICE_MEMORY "Algemeen Geheugen" +#define D_INFO_LVGL_MEMORY "LVGL Geheugen" +#define D_INFO_TOTAL_MEMORY "Totaal" +#define D_INFO_FREE_MEMORY "Vrij" +#define D_INFO_FRAGMENTATION "Fragmentatie" +#define D_INFO_PSRAM_FREE "PSRam Vrij" +#define D_INFO_PSRAM_SIZE "PSRam Grootte" +#define D_INFO_FLASH_SIZE "Flash Grootte" +#define D_INFO_SKETCH_USED "Programma Grootte" +#define D_INFO_SKETCH_FREE "Programma Vrij" +#define D_INFO_MODULE "Module" +#define D_INFO_MODEL "Model" +#define D_INFO_FREQUENCY "Frequentie" +#define D_INFO_CORE_VERSION "ESP-IDF Versie" +#define D_INFO_RESET_REASON "Reden Herstart" +#define D_INFO_STATUS "Status" +#define D_INFO_SERVER "Server" +#define D_INFO_USERNAME "Gerbuiker" +#define D_INFO_CLIENTID "Client ID" +#define D_INFO_CONNECTED "Verbonden" +#define D_INFO_DISCONNECTED "Verbroken" +#define D_INFO_RECEIVED "Ontvangen" +#define D_INFO_PUBLISHED "Gepubliceerd" +#define D_INFO_FAILED "Mislukt" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "Wifi" +#define D_INFO_LINK_SPEED "Snelheid" +#define D_INFO_FULL_DUPLEX "Full Duplex" +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Signaalsterkte" +#define D_INFO_IP_ADDRESS "IP Adres" +#define D_INFO_MAC_ADDRESS "Fysiek adres (MAC)" +#define D_INFO_GATEWAY "Gateway" +#define D_INFO_DNS_SERVER "DNS Server" #define D_OOBE_MSG "Raak het scherm aan om WiFi in te stellen of meld je aan op AP:" #define D_OOBE_SCAN_TO_CONNECT "Scan code" #define D_WIFI_CONNECTING_TO "Verbinden met %s" #define D_WIFI_CONNECTED_TO "Verbonden met %s, IP aanvragen..." +#define D_WIFI_RSSI_EXCELLENT "Uitstekend" +#define D_WIFI_RSSI_GOOD "Goed" +#define D_WIFI_RSSI_FAIR "Redelijk" +#define D_WIFI_RSSI_WEAK "Zwak" +#define D_WIFI_RSSI_BAD "Zeer Slecht" + +#define D_GPIO_SWITCH "Schakelaar" +#define D_GPIO_BUTTON "Drukknop" +#define D_GPIO_TOUCH "Aanraakknop" +#define D_GPIO_LED "Led" +#define D_GPIO_LED_R "Sfeer Rood" +#define D_GPIO_LED_G "Sfeer Groen" +#define D_GPIO_LED_B "Sfeer Blauw" +#define D_GPIO_POWER_RELAY "Stroomrelais" +#define D_GPIO_LIGHT_RELAY "Licht Relais" +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Seriële Dimmer" +#define D_GPIO_UNKNOWN "Onbekend" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Groep" +#define D_GPIO_GROUP_NONE "Geen" +#define D_GPIO_STATE_NORMAL "Normaal" +#define D_GPIO_STATE_INVERTED "Geïnverteerd" #endif \ No newline at end of file diff --git a/src/lang/pt_BR.h b/src/lang/pt_BR.h new file mode 100644 index 00000000..61d297ee --- /dev/null +++ b/src/lang/pt_BR.h @@ -0,0 +1,16 @@ +#ifndef HASP_LANG_PT_BR_H +#define HASP_LANG_PT_BR_H + +#include "pt_PT.h" // Common language file + +// Overrides +#undef D_USERNAME +#define D_USERNAME "Usuário:" + +#undef D_PASSWORD +#define D_PASSWORD "Senha:" + +#undef D_NETWORK_CONNECTION_FAILED +#define D_NETWORK_CONNECTION_FAILED "Falhou a conexão" + +#endif \ No newline at end of file diff --git a/src/lang/pt_PT.h b/src/lang/pt_PT.h index 48f59ec5..b9747bbb 100644 --- a/src/lang/pt_PT.h +++ b/src/lang/pt_PT.h @@ -1,205 +1,216 @@ -#ifndef HASP_LANG_PT_PT_H -#define HASP_LANG_PT_PT_H - -#define D_USERNAME "Utilizador:" -#define D_PASSWORD "Palavra-passe:" -#define D_SSID "SSID:" -#define D_YES "Sim" // new -#define D_NO "Não" // new - -#define D_ERROR_OUT_OF_MEMORY "Memória Cheia" -#define D_ERROR_UNKNOWN "Erro desconhecido" - -#define D_CONFIG_NOT_CHANGED "Sem alterações na configuração" -#define D_CONFIG_CHANGED "Configuração alterada" -#define D_CONFIG_LOADED "Configuração carregada" - -#define D_FILE_LOADING "A carregar %s" -#define D_FILE_LOADED "%s carregado" -#define D_FILE_LOAD_FAILED "Não foi possível carregar %s" - -#define D_FILE_SAVING "A guardar %s" -#define D_FILE_SAVED "%s guardado" -#define D_FILE_SAVE_FAILED "Não foi possível guardar %s" -#define D_FILE_NOT_FOUND "Ficheiro não encontrado" // new - -#define D_SERVICE_STARTING "A inicializar..." -#define D_SERVICE_STARTED "Iniciado" -#define D_SERVICE_START_FAILED "Não foi possível iniciar" -#define D_SERVICE_STOPPED "Parado" -#define D_SERVICE_DISABLED "Desativado" -#define D_SERVICE_CONNECTED "Ligado" -#define D_SERVICE_DISCONNECTED "Desligado" - -#define D_SETTING_ENABLED "Ativado" // New -#define D_SETTING_DISABLED "Desativado" // New - -#define D_NETWORK_IP_ADDRESS_RECEIVED "Foi atribuído endereço IP: %s" -#define D_NETWORK_ONLINE "Online" -#define D_NETWORK_OFFLINE "Offline" -#define D_NETWORK_CONNECTION_FAILED "Falhou a ligação" - -#define D_MQTT_DEFAULT_NAME "placa_%s" -#define D_MQTT_CONNECTING "A ligar..." -#define D_MQTT_CONNECTED "Ligado ao broker %s com clientID %s" -#define D_MQTT_NOT_CONNECTED "Não há ligação ???" -#define D_MQTT_DISCONNECTING "A desligar..." -#define D_MQTT_DISCONNECTED "Desligado" -#define D_MQTT_RECONNECTING "Desligado do broker, a religar..." -#define D_MQTT_NOT_CONFIGURED "O Broker não foi configurado" -#define D_MQTT_STARTED "A iniciar: %d bytes" -#define D_MQTT_FAILED "Falhou:" -#define D_MQTT_INVALID_TOPIC "A mensagem tem um tópico inválido" -#define D_MQTT_SUBSCRIBED "Subscrito a %s" -#define D_MQTT_NOT_SUBSCRIBED "Não foi possível subscrever %s" -#define D_MQTT_HA_AUTO_DISCOVERY "A registar auto-descoberta no HA" -#define D_MQTT_PAYLOAD_TOO_LONG "Os dados são demasiado grandes(%u bytes)" - -#define D_TELNET_CLOSING_CONNECTION "A fechar a ligação de %s" -#define D_TELNET_CLIENT_LOGIN_FROM "Foi feito login ao cliente %s" -#define D_TELNET_CLIENT_CONNECT_FROM "Foi conectado ao cliente %s" -#define D_TELNET_AUTHENTICATION_FAILED "Falhou a autorização!" -#define D_TELNET_INCORRECT_LOGIN_ATTEMPT "Tentativa de ligação incorreta desde %s" -#define D_TELNET_STARTED "Consola inicializada" -#define D_TELNET_FAILED "Falhou inicialização da consola" -#define D_TELNET_CLIENT_CONNECTED "Cliente ligado" -#define D_TELNET_CLIENT_NOT_CONNECTED "Cliente não ligado" -#define D_TELNET_CLIENT_REJECTED "Cliente rejeitado" - -#define D_HASP_INVALID_PAGE "Página inválida %u" -#define D_HASP_INVALID_LAYER "Não foi possível eliminar a camada de sistema" -#define D_HASP_CHANGE_PAGE "A alterar a página %u" -#define D_HASP_CLEAR_PAGE "A limpar página %u" - -#define D_OBJECT_DELETED "Objeto eliminado" -#define D_OBJECT_UNKNOWN "Objeto desconhecido" -#define D_OBJECT_MISMATCH "Os objetos não são iguais!" -#define D_OBJECT_LOST "Objeto perdido!" -#define D_OBJECT_CREATE_FAILED "Não foi possível criar o objeto %u" -#define D_OBJECT_PAGE_UNKNOWN "A página ID %u não está definida" -#define D_OBJECT_EVENT_UNKNOWN "Não se conhece o evento %d " - -#define D_ATTRIBUTE_UNKNOWN "Propriedade %s desconhecida" -#define D_ATTRIBUTE_READ_ONLY "%s é de leitura apenas" -#define D_ATTRIBUTE_PAGE_METHOD_INVALID "Não foi possível chamar %s numa página" - -#define D_OOBE_SSID_VALIDATED "SSID %s válido" -#define D_OOBE_AUTO_CALIBRATE "Auto calibração ativada" -#define D_OOBE_CALIBRATED "já foi calibrado" - -#define D_DISPATCH_COMMAND_NOT_FOUND "Não se encontrou o comando '%s'" -#define D_DISPATCH_INVALID_PAGE "Página inválida %s" -#define D_DISPATCH_REBOOT "A reiniciar dispositivo!" - -#define D_JSON_FAILED "Não foi possível analisar o JSON:" -#define D_JSONL_FAILED "A análise do JSONL falhou na linha %u" -#define D_JSONL_SUCCEEDED "JSONL analisado" - -#define D_OTA_CHECK_UPDATE "A procurar por atualização no URL: %s" -#define D_OTA_CHECK_COMPLETE "Verificação da atualização completa" -#define D_OTA_CHECK_FAILED "Falhou a verificação da atualização: %s" -#define D_OTA_UPDATE_FIRMWARE "Atualização de firmware OTA" -#define D_OTA_UPDATE_COMPLETE "Atualização OTA completa" -#define D_OTA_UPDATE_APPLY "A aplicar o novo firmware e reiniciar" -#define D_OTA_UPDATE_FAILED "A atualização OTA falhou" -#define D_OTA_UPDATING_FIRMWARE "A atualizar o firmware..." -#define D_OTA_UPDATING_FILESYSTEM "A atualizar o sistema de ficheiros..." - -#define D_HTTP_HASP_DESIGN "Design do HASP" -#define D_HTTP_INFORMATION "Informação" -#define D_HTTP_HTTP_SETTINGS "Configurar HTTP" -#define D_HTTP_WIFI_SETTINGS "Configurar Wifi" -#define D_HTTP_MQTT_SETTINGS "Configurar MQTT" -#define D_HTTP_GPIO_SETTINGS "Configurar GPIO" -#define D_HTTP_MDNS_SETTINGS "Configurar mDNS" -#define D_HTTP_TELNET_SETTINGS "Configurar Telnet" -#define D_HTTP_DEBUG_SETTINGS "Configurar debug" -#define D_HTTP_GUI_SETTINGS "Configurar GUI" -#define D_HTTP_SAVE_SETTINGS "Guardar Configuração" -#define D_HTTP_UPLOAD_FILE "Carregar ficheiro" -#define D_HTTP_ERASE_DEVICE "Eliminar Configuração" -#define D_HTTP_ADD_GPIO "Adicionar novo pino" -#define D_HTTP_BACK "Retroceder" -#define D_HTTP_REFRESH "Atualizar" -#define D_HTTP_PREV_PAGE "Página Anterior" -#define D_HTTP_NEXT_PAGE "Página Seguinte" -#define D_HTTP_CALIBRATE "Calibrar" -#define D_HTTP_SCREENSHOT "Screenshot" -#define D_HTTP_FILE_BROWSER "Editor de ficheiros" -#define D_HTTP_FIRMWARE_UPGRADE "Atualização do firmware" -#define D_HTTP_UPDATE_FIRMWARE "Atualizar o firmware" -#define D_HTTP_FACTORY_RESET "Repor configuração de fábrica" -#define D_HTTP_MAIN_MENU "Menu Principal" -#define D_HTTP_REBOOT "Reiniciar" -#define D_HTTP_CONFIGURATION "Configuração" -#define D_HTTP_SENDING_PAGE "Foi enviado página %S a %s" // New -#define D_HTTP_FOOTER "por Francis Van Roie" - -#define D_INFO_VERSION "Versão" -#define D_INFO_BUILD_DATETIME "Compilado a" -#define D_INFO_UPTIME "Tempo ativo" -#define D_INFO_FREE_HEAP "Heap livre" -#define D_INFO_FREE_BLOCK "Blocos livres" -#define D_INFO_DEVICE_MEMORY "Memória do dispositivo" -#define D_INFO_LVGL_MEMORY "Memoria LVGL" -#define D_INFO_TOTAL_MEMORY "Total" -#define D_INFO_FREE_MEMORY "Livre" -#define D_INFO_FRAGMENTATION "Fragmentação" -#define D_INFO_PSRAM_FREE "PSRam livre" -#define D_INFO_PSRAM_SIZE "Tamanho PSRam " -#define D_INFO_FLASH_SIZE "Tamanho Flash" -#define D_INFO_SKETCH_USED "Memória programa usada" -#define D_INFO_SKETCH_FREE "Memória programa livre" -#define D_INFO_MODULE "Módulo" -#define D_INFO_MODEL "Modelo" -#define D_INFO_FREQUENCY "Frequência" -#define D_INFO_CORE_VERSION "Versão do core" -#define D_INFO_RESET_REASON "Razão do último Reset" -#define D_INFO_STATUS "Estado" -#define D_INFO_SERVER "Servidor" -#define D_INFO_USERNAME "Nome do utilizador" -#define D_INFO_CLIENTID "ID do Cliente" -#define D_INFO_CONNECTED "Ligado" -#define D_INFO_DISCONNECTED "Desligado" -#define D_INFO_RECEIVED "Recebido" -#define D_INFO_PUBLISHED "Publicado" -#define D_INFO_FAILED "Em falha" -#define D_INFO_ETHERNET "Ethernet" -#define D_INFO_WIFI "Wifi" -#define D_INFO_LINK_SPEED "Link Speed" -#define D_INFO_FULL_DUPLEX "Full Duplex" -#define D_INFO_SSID "SSID" -#define D_INFO_RSSI "Potência do sinal" -#define D_INFO_IP_ADDRESS "Endereço IP" -#define D_INFO_MAC_ADDRESS "Endereço MAC" -#define D_INFO_GATEWAY "Gateway" -#define D_INFO_DNS_SERVER "Servidor DNS" - -#define D_OOBE_MSG "Toque no ecrã para configurar WiFi ou para se ligar a um access point" -#define D_OOBE_SCAN_TO_CONNECT "Procurar rede" - -#define D_WIFI_CONNECTING_TO "A ligar a %s" -#define D_WIFI_CONNECTED_TO "Ligado a %s, a pedir IP..." -#define D_WIFI_RSSI_EXCELLENT "Excelente" -#define D_WIFI_RSSI_GOOD "Bom" -#define D_WIFI_RSSI_FAIR "Decente" -#define D_WIFI_RSSI_WEAK "Fraco" -#define D_WIFI_RSSI_BAD "Muito baixo" - -// new -#define D_GPIO_SWITCH "Interruptor" -#define D_GPIO_BUTTON "Botão" -#define D_GPIO_LED "LED" -#define D_GPIO_LED_R "LED Red" -#define D_GPIO_LED_G "LED Green" -#define D_GPIO_LED_B "LED Blue" -#define D_GPIO_RELAY "Relé" -#define D_GPIO_PWM "PWM" -#define D_GPIO_DAC "DAC" -#define D_GPIO_SERIAL_DIMMER "Dimmer serial" -#define D_GPIO_UNKNOWN "Desconhecido" -#define D_GPIO_PIN "Pin" -#define D_GPIO_GROUP "Grupo" -#define D_GPIO_GROUP_NONE "Nenhum" - -#endif \ No newline at end of file +#ifndef HASP_LANG_PT_PT_H +#define HASP_LANG_PT_PT_H + +#define D_USERNAME "Utilizador:" +#define D_PASSWORD "Palavra-passe:" +#define D_SSID "SSID:" +#define D_YES "Sim" +#define D_NO "Não" + +#define D_ERROR_OUT_OF_MEMORY "Memória Cheia" +#define D_ERROR_UNKNOWN "Erro desconhecido" + +#define D_CONFIG_NOT_CHANGED "Sem alterações na configuração" +#define D_CONFIG_CHANGED "Configuração alterada" +#define D_CONFIG_LOADED "Configuração carregada" + +#define D_FILE_LOADING "A carregar %s" +#define D_FILE_LOADED "%s carregado" +#define D_FILE_LOAD_FAILED "Não foi possível carregar %s" + +#define D_FILE_SAVING "A guardar %s" +#define D_FILE_SAVED "%s guardado" +#define D_FILE_SAVE_FAILED "Não foi possível guardar %s" +#define D_FILE_NOT_FOUND "Ficheiro não encontrado" +#define D_FILE_SIZE_BYTES "bytes" // new +#define D_FILE_SIZE_KILOBYTES "KiB" // new +#define D_FILE_SIZE_MEGABYTES "MiB" // new +#define D_FILE_SIZE_GIGABYTES "GiB" // new +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "." // new, decimal comma or point + +#define D_SERVICE_STARTING "A inicializar..." +#define D_SERVICE_STARTED "Iniciado" +#define D_SERVICE_START_FAILED "Não foi possível iniciar" +#define D_SERVICE_STOPPED "Parado" +#define D_SERVICE_DISABLED "Desativado" +#define D_SERVICE_CONNECTED "Ligado" +#define D_SERVICE_DISCONNECTED "Desligado" + +#define D_SETTING_ENABLED "Ativado" +#define D_SETTING_DISABLED "Desativado" + +#define D_NETWORK_IP_ADDRESS_RECEIVED "Foi atribuído endereço IP: %s" +#define D_NETWORK_ONLINE "Online" +#define D_NETWORK_OFFLINE "Offline" +#define D_NETWORK_CONNECTION_FAILED "Falhou a ligação" + +#define D_MQTT_DEFAULT_NAME "placa_%s" +#define D_MQTT_CONNECTING "A ligar..." +#define D_MQTT_CONNECTED "Ligado ao broker %s com clientID %s" +#define D_MQTT_NOT_CONNECTED "Não há ligação ???" +#define D_MQTT_DISCONNECTING "A desligar..." +#define D_MQTT_DISCONNECTED "Desligado" +#define D_MQTT_RECONNECTING "Desligado do broker, a religar..." +#define D_MQTT_NOT_CONFIGURED "O Broker não foi configurado" +#define D_MQTT_STARTED "A iniciar: %d bytes" +#define D_MQTT_FAILED "Falhou:" +#define D_MQTT_INVALID_TOPIC "A mensagem tem um tópico inválido" +#define D_MQTT_SUBSCRIBED "Subscrito a %s" +#define D_MQTT_NOT_SUBSCRIBED "Não foi possível subscrever %s" +#define D_MQTT_HA_AUTO_DISCOVERY "A registar auto-descoberta no HA" +#define D_MQTT_PAYLOAD_TOO_LONG "Os dados são demasiado grandes(%u bytes)" + +#define D_TELNET_CLOSING_CONNECTION "A fechar a ligação de %s" +#define D_TELNET_CLIENT_LOGIN_FROM "Foi feito login ao cliente %s" +#define D_TELNET_CLIENT_CONNECT_FROM "Foi conectado ao cliente %s" +#define D_TELNET_AUTHENTICATION_FAILED "Falhou a autorização!" +#define D_TELNET_INCORRECT_LOGIN_ATTEMPT "Tentativa de ligação incorreta desde %s" +#define D_TELNET_STARTED "Consola inicializada" +#define D_TELNET_FAILED "Falhou inicialização da consola" +#define D_TELNET_CLIENT_CONNECTED "Cliente ligado" +#define D_TELNET_CLIENT_NOT_CONNECTED "Cliente não ligado" +#define D_TELNET_CLIENT_REJECTED "Cliente rejeitado" + +#define D_HASP_INVALID_PAGE "Página inválida %u" +#define D_HASP_INVALID_LAYER "Não foi possível eliminar a camada de sistema" +#define D_HASP_CHANGE_PAGE "A alterar a página %u" +#define D_HASP_CLEAR_PAGE "A limpar página %u" + +#define D_OBJECT_DELETED "Objeto eliminado" +#define D_OBJECT_UNKNOWN "Objeto desconhecido" +#define D_OBJECT_MISMATCH "Os objetos não são iguais!" +#define D_OBJECT_LOST "Objeto perdido!" +#define D_OBJECT_CREATE_FAILED "Não foi possível criar o objeto %u" +#define D_OBJECT_PAGE_UNKNOWN "A página ID %u não está definida" +#define D_OBJECT_EVENT_UNKNOWN "Não se conhece o evento %d " + +#define D_ATTRIBUTE_UNKNOWN "Propriedade %s desconhecida" +#define D_ATTRIBUTE_READ_ONLY "%s é de leitura apenas" +#define D_ATTRIBUTE_PAGE_METHOD_INVALID "Não foi possível chamar %s numa página" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" // new + +#define D_OOBE_SSID_VALIDATED "SSID %s válido" +#define D_OOBE_AUTO_CALIBRATE "Auto calibração ativada" +#define D_OOBE_CALIBRATED "já foi calibrado" + +#define D_DISPATCH_COMMAND_NOT_FOUND "Não se encontrou o comando '%s'" +#define D_DISPATCH_INVALID_PAGE "Página inválida %s" +#define D_DISPATCH_REBOOT "A reiniciar dispositivo!" + +#define D_JSON_FAILED "Não foi possível analisar o JSON:" +#define D_JSONL_FAILED "A análise do JSONL falhou na linha %u" +#define D_JSONL_SUCCEEDED "JSONL analisado" + +#define D_OTA_CHECK_UPDATE "A procurar por atualização no URL: %s" +#define D_OTA_CHECK_COMPLETE "Verificação da atualização completa" +#define D_OTA_CHECK_FAILED "Falhou a verificação da atualização: %s" +#define D_OTA_UPDATE_FIRMWARE "Atualização de firmware OTA" +#define D_OTA_UPDATE_COMPLETE "Atualização OTA completa" +#define D_OTA_UPDATE_APPLY "A aplicar o novo firmware e reiniciar" +#define D_OTA_UPDATE_FAILED "A atualização OTA falhou" +#define D_OTA_UPDATING_FIRMWARE "A atualizar o firmware..." +#define D_OTA_UPDATING_FILESYSTEM "A atualizar o sistema de ficheiros..." + +#define D_HTTP_HASP_DESIGN "Design do HASP" +#define D_HTTP_INFORMATION "Informação" +#define D_HTTP_HTTP_SETTINGS "Configurar HTTP" +#define D_HTTP_WIFI_SETTINGS "Configurar Wifi" +#define D_HTTP_MQTT_SETTINGS "Configurar MQTT" +#define D_HTTP_GPIO_SETTINGS "Configurar GPIO" +#define D_HTTP_MDNS_SETTINGS "Configurar mDNS" +#define D_HTTP_TELNET_SETTINGS "Configurar Telnet" +#define D_HTTP_DEBUG_SETTINGS "Configurar debug" +#define D_HTTP_GUI_SETTINGS "Configurar GUI" +#define D_HTTP_SAVE_SETTINGS "Guardar Configuração" +#define D_HTTP_UPLOAD_FILE "Carregar ficheiro" +#define D_HTTP_ERASE_DEVICE "Eliminar Configuração" +#define D_HTTP_ADD_GPIO "Adicionar novo pino" +#define D_HTTP_BACK "Retroceder" +#define D_HTTP_REFRESH "Atualizar" +#define D_HTTP_PREV_PAGE "Página Anterior" +#define D_HTTP_NEXT_PAGE "Página Seguinte" +#define D_HTTP_CALIBRATE "Calibrar" +#define D_HTTP_SCREENSHOT "Screenshot" +#define D_HTTP_FILE_BROWSER "Editor de ficheiros" +#define D_HTTP_FIRMWARE_UPGRADE "Atualização do firmware" +#define D_HTTP_UPDATE_FIRMWARE "Atualizar o firmware" +#define D_HTTP_FACTORY_RESET "Repor configuração de fábrica" +#define D_HTTP_MAIN_MENU "Menu Principal" +#define D_HTTP_REBOOT "Reiniciar" +#define D_HTTP_CONFIGURATION "Configuração" +#define D_HTTP_SENDING_PAGE "Foi enviado página %S a %s" +#define D_HTTP_FOOTER "por Francis Van Roie" + +#define D_INFO_VERSION "Versão" +#define D_INFO_BUILD_DATETIME "Compilado a" +#define D_INFO_UPTIME "Tempo ativo" +#define D_INFO_FREE_HEAP "Heap livre" +#define D_INFO_FREE_BLOCK "Blocos livres" +#define D_INFO_DEVICE_MEMORY "Memória do dispositivo" +#define D_INFO_LVGL_MEMORY "Memória LVGL" +#define D_INFO_TOTAL_MEMORY "Total" +#define D_INFO_FREE_MEMORY "Livre" +#define D_INFO_FRAGMENTATION "Fragmentação" +#define D_INFO_PSRAM_FREE "PSRam livre" +#define D_INFO_PSRAM_SIZE "Tamanho PSRam " +#define D_INFO_FLASH_SIZE "Tamanho Flash" +#define D_INFO_SKETCH_USED "Memória programa usada" +#define D_INFO_SKETCH_FREE "Memória programa livre" +#define D_INFO_MODULE "Módulo" +#define D_INFO_MODEL "Modelo" +#define D_INFO_FREQUENCY "Frequência" +#define D_INFO_CORE_VERSION "Versão do core" +#define D_INFO_RESET_REASON "Razão do último Reset" +#define D_INFO_STATUS "Estado" +#define D_INFO_SERVER "Servidor" +#define D_INFO_USERNAME "Nome do utilizador" +#define D_INFO_CLIENTID "ID do Cliente" +#define D_INFO_CONNECTED "Ligado" +#define D_INFO_DISCONNECTED "Desligado" +#define D_INFO_RECEIVED "Recebido" +#define D_INFO_PUBLISHED "Publicado" +#define D_INFO_FAILED "Em falha" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "Wifi" +#define D_INFO_LINK_SPEED "Link Speed" +#define D_INFO_FULL_DUPLEX "Full Duplex" +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Potência do sinal" +#define D_INFO_IP_ADDRESS "Endereço IP" +#define D_INFO_MAC_ADDRESS "Endereço MAC" +#define D_INFO_GATEWAY "Gateway" +#define D_INFO_DNS_SERVER "Servidor DNS" + +#define D_OOBE_MSG "Toque no ecrã para configurar WiFi ou para se ligar a um access point" +#define D_OOBE_SCAN_TO_CONNECT "Procurar rede" + +#define D_WIFI_CONNECTING_TO "A ligar a %s" +#define D_WIFI_CONNECTED_TO "Ligado a %s, a pedir IP..." +#define D_WIFI_RSSI_EXCELLENT "Excelente" +#define D_WIFI_RSSI_GOOD "Bom" +#define D_WIFI_RSSI_FAIR "Decente" +#define D_WIFI_RSSI_WEAK "Fraco" +#define D_WIFI_RSSI_BAD "Muito baixo" + +#define D_GPIO_SWITCH "Interruptor" +#define D_GPIO_BUTTON "Botão" +#define D_GPIO_TOUCH "Capacitive Touch" // new +#define D_GPIO_LED "LED" +#define D_GPIO_LED_R "LED Red" +#define D_GPIO_LED_G "LED Green" +#define D_GPIO_LED_B "LED Blue" +#define D_GPIO_POWER_RELAY "Power Relé" // new +#define D_GPIO_LIGHT_RELAY "Light Relé" // new +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Dimmer serial" +#define D_GPIO_UNKNOWN "Desconhecido" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Grupo" +#define D_GPIO_GROUP_NONE "Nenhum" +#define D_GPIO_STATE_NORMAL "Normal" // new +#define D_GPIO_STATE_INVERTED "Inverted" // new + +#endif diff --git a/src/lang/ro_RO.h b/src/lang/ro_RO.h index ee7e4c51..abb91b15 100644 --- a/src/lang/ro_RO.h +++ b/src/lang/ro_RO.h @@ -4,6 +4,8 @@ #define D_USERNAME "Nume de utilizator:" #define D_PASSWORD "Parola:" #define D_SSID "SSID:" +#define D_YES "Da" +#define D_NO "Nu" #define D_ERROR_OUT_OF_MEMORY "Memorie epuizată" #define D_ERROR_UNKNOWN "Eroare necunoscută" @@ -15,10 +17,19 @@ #define D_FILE_LOADING "Se încarcă %s" #define D_FILE_LOADED "S-a încărcat %s" #define D_FILE_LOAD_FAILED "Încărcarea %s a eșuat" - #define D_FILE_SAVING "Se salvează %s" #define D_FILE_SAVED "S-a salvat %s" #define D_FILE_SAVE_FAILED "Salvarea %s a eșuat" +#define D_FILE_NOT_FOUND "File not found" // new +#define D_FILE_SIZE_BYTES "bytes" // new +#define D_FILE_SIZE_KILOBYTES "KiB" // new +#define D_FILE_SIZE_MEGABYTES "MiB" // new +#define D_FILE_SIZE_GIGABYTES "GiB" // new +#define D_FILE_SIZE_DIVIDER 1024 // new, kibi or kilo bytes +#define D_DECIMAL_POINT "," // new, decimal comma or point + +#define D_SETTING_ENABLED "Activ" +#define D_SETTING_DISABLED "Inactiv" #define D_SERVICE_STARTING "Pornire..." #define D_SERVICE_STARTED "Pornit" @@ -47,7 +58,7 @@ #define D_MQTT_SUBSCRIBED "Abonat la %s" #define D_MQTT_NOT_SUBSCRIBED "Abonarea la %s a eșuat" #define D_MQTT_HA_AUTO_DISCOVERY "Înregistrare la auto-descoperire în HA" -#define D_MQTT_PAYLOAD_TOO_LONG "$$$Payload too long (%d bytes)" +#define D_MQTT_PAYLOAD_TOO_LONG "Payload prea lung (%u baiți)" #define D_TELNET_CLOSING_CONNECTION "Terminarea sesiunii de la %s" #define D_TELNET_CLIENT_LOGIN_FROM "Conectare client de la %s" @@ -58,7 +69,6 @@ #define D_TELNET_STARTED "Consola Telnet pornită" #define D_TELNET_FAILED "Nu s-a putut porni consola Telnet" #define D_TELNET_CLIENT_CONNECTED "Client conectat" -#define D_TELNET_CLIENT_NOT_CONNECTED "Client NU este conectat" #define D_TELNET_CLIENT_REJECTED "Client respins" #define D_HASP_INVALID_PAGE "Pagina invalidă: %u" @@ -77,6 +87,8 @@ #define D_ATTRIBUTE_UNKNOWN "Proprietate necunoscută: %s" #define D_ATTRIBUTE_READ_ONLY "%s este numai în citire" #define D_ATTRIBUTE_PAGE_METHOD_INVALID "Nu se poate apela %s pe o pagină" +#define D_ATTRIBUTE_ALIGN_INVALID "Invalid align property: %s" // new +#define D_ATTRIBUTE_COLOR_INVALID "Invalid color property: %s" // new #define D_OOBE_SSID_VALIDATED "SSID %s validat" #define D_OOBE_AUTO_CALIBRATE "Calibrarea automată este activă" @@ -87,7 +99,7 @@ #define D_DISPATCH_REBOOT "MCU-ul repornește acum!" #define D_JSON_FAILED "Analiza JSON a eșuat:" -#define D_JSONL_FAILED "Analiza JSONL a eșuat la linia %d" +#define D_JSONL_FAILED "Analiza JSONL a eșuat la linia %u" #define D_JSONL_SUCCEEDED "Analiza JSONL completă" #define D_OTA_CHECK_UPDATE "Verificare la URL-ul actualizărilor: %s" @@ -97,6 +109,8 @@ #define D_OTA_UPDATE_COMPLETE "Actualizare prin OTA finalizată" #define D_OTA_UPDATE_APPLY "Aplicarea firmware-ului și repornire" #define D_OTA_UPDATE_FAILED "Actualizarea prin OTA a eșuat" +#define D_OTA_UPDATING_FIRMWARE "Actualizare firmware..." +#define D_OTA_UPDATING_FILESYSTEM "Actualizare sistem fișiere..." #define D_HTTP_HASP_DESIGN "Desenul ecranului" #define D_HTTP_INFORMATION "Informații" @@ -111,7 +125,7 @@ #define D_HTTP_SAVE_SETTINGS "Salvarea setărilor" #define D_HTTP_UPLOAD_FILE "Încărcare fișier" #define D_HTTP_ERASE_DEVICE "Resetarea tuturor setărilor" -#define D_HTTP_ADD_GPIO "Adăugați un pin nou" +#define D_HTTP_ADD_GPIO "Adăugare pin" #define D_HTTP_BACK "Înapoi" #define D_HTTP_REFRESH "Reîmprospătare" #define D_HTTP_PREV_PAGE "Pagina anterioară" @@ -125,11 +139,78 @@ #define D_HTTP_MAIN_MENU "Meniu principal" #define D_HTTP_REBOOT "Repornire" #define D_HTTP_CONFIGURATION "Setări" +#define D_HTTP_SENDING_PAGE "Pagina %S trimisă la %s" +#define D_HTTP_FOOTER "de Francis Van Roie" + +#define D_INFO_VERSION "Versiune" +#define D_INFO_BUILD_DATETIME "Data Build-ului" +#define D_INFO_UPTIME "Uptime" +#define D_INFO_FREE_HEAP "Heap liber" +#define D_INFO_FREE_BLOCK "Bloc liber" +#define D_INFO_DEVICE_MEMORY "Memorie dispozitiv" +#define D_INFO_LVGL_MEMORY "Memorie LVGL" +#define D_INFO_TOTAL_MEMORY "Total" +#define D_INFO_FREE_MEMORY "Liber" +#define D_INFO_FRAGMENTATION "Fragmentație" +#define D_INFO_PSRAM_FREE "PSRam liber" +#define D_INFO_PSRAM_SIZE "PSRam mărime" +#define D_INFO_FLASH_SIZE "Flash mărime" +#define D_INFO_SKETCH_USED "Mărime program folosită" +#define D_INFO_SKETCH_FREE "Mărime program liberă" +#define D_INFO_MODULE "Modul" +#define D_INFO_MODEL "Model" +#define D_INFO_FREQUENCY "Frecvență" +#define D_INFO_CORE_VERSION "Versiune Core" +#define D_INFO_RESET_REASON "Motiv reset" +#define D_INFO_STATUS "Stare" +#define D_INFO_SERVER "Server" +#define D_INFO_USERNAME "Utilizatur" +#define D_INFO_CLIENTID "ID Client" +#define D_INFO_CONNECTED "Conectat" +#define D_INFO_DISCONNECTED "Deconectat" +#define D_INFO_RECEIVED "Primite" +#define D_INFO_PUBLISHED "Trimise" +#define D_INFO_FAILED "Eșuate" +#define D_INFO_ETHERNET "Ethernet" +#define D_INFO_WIFI "WiFi" +#define D_INFO_LINK_SPEED "Viteză Link" +#define D_INFO_FULL_DUPLEX "Full Duplex" // new +#define D_INFO_SSID "SSID" +#define D_INFO_RSSI "Putere semnal" +#define D_INFO_IP_ADDRESS "Addresa IP" +#define D_INFO_MAC_ADDRESS "Addresa MAC" +#define D_INFO_GATEWAY "Gateway" +#define D_INFO_DNS_SERVER "Server DNS" #define D_OOBE_MSG "Atingeți ecranul pentru a configura WiFi sau conectați-vă la acest punct de acces:" #define D_OOBE_SCAN_TO_CONNECT "Scanați pentru a vă conecta:" -#define D_WIFI_CONNECTING_TO "$$$Connecting to %s" -#define D_WIFI_CONNECTED_TO "$$$Connected to %s, requesting IP..." +#define D_WIFI_CONNECTING_TO "Conectare la %s" +#define D_WIFI_CONNECTED_TO "Conectat la %s, cerere IP..." +#define D_WIFI_RSSI_EXCELLENT "Excelentă" +#define D_WIFI_RSSI_GOOD "Bună" +#define D_WIFI_RSSI_FAIR "Acceptabilă" +#define D_WIFI_RSSI_WEAK "Slabă" +#define D_WIFI_RSSI_BAD "Foarte slabă" + +// new +#define D_GPIO_SWITCH "Switch" +#define D_GPIO_BUTTON "Button" +#define D_GPIO_TOUCH "Capacitive Touch" // new +#define D_GPIO_LED "Led" +#define D_GPIO_LED_R "Mood Red" +#define D_GPIO_LED_G "Mood Green" +#define D_GPIO_LED_B "Mood Blue" +#define D_GPIO_POWER_RELAY "Power Relay" // new +#define D_GPIO_LIGHT_RELAY "Light Relay" // new +#define D_GPIO_PWM "PWM" +#define D_GPIO_DAC "DAC" +#define D_GPIO_SERIAL_DIMMER "Serial Dimmer" +#define D_GPIO_UNKNOWN "Unknown" +#define D_GPIO_PIN "Pin" +#define D_GPIO_GROUP "Group" +#define D_GPIO_GROUP_NONE "None" +#define D_GPIO_STATE_NORMAL "Normal" // new +#define D_GPIO_STATE_INVERTED "Inverted" // new #endif diff --git a/src/log/hasp_debug.cpp b/src/log/hasp_debug.cpp index 672d53ba..3208e9b1 100644 --- a/src/log/hasp_debug.cpp +++ b/src/log/hasp_debug.cpp @@ -39,10 +39,6 @@ #include "hasp/hasp_dispatch.h" #include "hasp/hasp.h" -// #ifdef USE_CONFIG_OVERRIDE -// #include "user_config_override.h" -// #endif - #ifndef SERIAL_SPEED #define SERIAL_SPEED 115200 #endif @@ -66,7 +62,6 @@ // static String debugOutput((char *)0); // static StringStream debugStream((String &)debugOutput); -// extern char mqttNodeName[16]; // const char* syslogAppName = APP_NAME; char debugSyslogHost[32] = SYSLOG_SERVER; uint16_t debugSyslogPort = SYSLOG_PORT; @@ -92,9 +87,6 @@ uint16_t debugSerialBaud = SERIAL_SPEED / 10; // Multiplied by 10 extern bool debugSerialStarted; extern bool debugAnsiCodes; -ConsoleInput debugConsole(&Serial, HASP_CONSOLE_BUFFER); - -unsigned long debugLastMillis = 0; extern dispatch_conf_t dispatch_setings; // #if HASP_USE_SYSLOG > 0 @@ -106,14 +98,6 @@ extern dispatch_conf_t dispatch_setings; // } // #endif -void debugSetup() -{ - // memset(serialInputBuffer, 0, sizeof(serialInputBuffer)); - // serialInputIndex = 0; - LOG_TRACE(TAG_DEBG, F(D_SERVICE_STARTING)); // Starting console - debugConsole.setLineCallback(dispatch_text_line); -} - void debugStartSyslog() { @@ -293,13 +277,14 @@ void debugPrintSuffix(uint8_t tag, int level, Print* _logOutput) _logOutput->println(); if(_logOutput == &Serial) { - debugConsole.update(); + console_update_prompt(); } else { _logOutput->print("hasp > "); } } -void debugPreSetup(JsonObject settings) +// Do NOT call Log function before debugSetup is called +void debugSetup(JsonObject settings) { Log.begin(LOG_LEVEL_WARNING, true); Log.setPrefix(debugPrintPrefix); // Uncomment to get timestamps as prefix @@ -328,31 +313,13 @@ void debugPreSetup(JsonObject settings) debugPrintHaspHeader(&Serial); Serial.flush(); - LOG_INFO(TAG_DEBG, F(D_SERVICE_STARTED " @ %u baud"), baudrate); + LOG_INFO(TAG_DEBG, F(D_SERVICE_STARTED " @ %u Bps"), baudrate); LOG_INFO(TAG_DEBG, F("Environment: " PIOENV)); } } -void debugLoop(void) -{ - int16_t keypress; - do { - switch(keypress = debugConsole.readKey()) { - - case ConsoleInput::KEY_PAGE_UP: - dispatch_page_next(LV_SCR_LOAD_ANIM_NONE); - break; - - case ConsoleInput::KEY_PAGE_DOWN: - dispatch_page_prev(LV_SCR_LOAD_ANIM_NONE); - break; - - case(ConsoleInput::KEY_FN)...(ConsoleInput::KEY_FN + 12): - dispatch_set_page(keypress - ConsoleInput::KEY_FN, LV_SCR_LOAD_ANIM_NONE); - break; - } - } while(keypress != 0); -} +IRAM_ATTR void debugLoop(void) +{} void printLocalTime() { @@ -386,7 +353,7 @@ void printLocalTime() } else { Serial.printf("%s ", sntp.toString().c_str()); } - Serial.printf("IPv6: %s Reachability: %o\n", sntp.isV6() ? "Yes" : "No", sntp_getreachability(i)); + Serial.printf("IPv6: %s Reachability: %o\n", sntp.isV6() ? D_YES : D_NO, sntp_getreachability(i)); } } #endif diff --git a/src/main_arduino copy.cpp b/src/main_arduino copy.cpp index 9f93e141..eef0a3f5 100644 --- a/src/main_arduino copy.cpp +++ b/src/main_arduino copy.cpp @@ -54,12 +54,11 @@ void setup() * Read & Apply User Configuration ***************************/ #if HASP_USE_CONFIG > 0 - configSetup(); // also runs debugPreSetup(), debugSetup() and debugStart() + configSetup(); // also runs debugSetup() and debugStart() #endif dispatchSetup(); guiSetup(); - debugSetup(); // Init the console #if HASP_USE_GPIO > 0 gpioSetup(); @@ -105,7 +104,7 @@ void setup() #endif mainLastLoopTime = millis() - 1000; // reset loop counter - delay(250); + delay(20); guiStart(); } diff --git a/src/main_arduino.cpp b/src/main_arduino.cpp index c863a28a..292030cf 100644 --- a/src/main_arduino.cpp +++ b/src/main_arduino.cpp @@ -3,9 +3,12 @@ #if !(defined(WINDOWS) || defined(POSIX)) -#include -#include "lvgl.h" -#include "hasp_conf.h" // load first +#include "hasplib.h" +#include "hasp_oobe.h" +#include "sys/net/hasp_network.h" +#include "dev/device.h" +#include "drv/hasp_drv_touch.h" +#include "ArduinoLog.h" #if HASP_USE_CONFIG > 0 #include "hasp_debug.h" @@ -16,18 +19,10 @@ #include "hasp_gui.h" #endif -#include "hasp_oobe.h" - -#include "hasp/hasp_dispatch.h" -#include "hasp/hasp.h" - -#include "sys/net/hasp_network.h" - -#include "dev/device.h" - bool isConnected; uint8_t mainLoopCounter = 0; unsigned long mainLastLoopTime = 0; +uint8_t statLoopCounter = 0; void setup() { @@ -54,12 +49,11 @@ void setup() * Read & Apply User Configuration ***************************/ #if HASP_USE_CONFIG > 0 - configSetup(); // also runs debugPreSetup(), debugSetup() and debugStart() + configSetup(); // also runs debugSetup() and debugStart() #endif + dispatchSetup(); // before hasp and oobe, asap after logging starts guiSetup(); - debugSetup(); // Init the console - dispatchSetup(); // for hasp and oobe #if HASP_USE_CONFIG > 0 if(!oobeSetup()) @@ -68,10 +62,6 @@ void setup() haspSetup(); } -#if HASP_USE_GPIO > 0 - gpioSetup(); -#endif - /**************************** * Apply User Configuration ***************************/ @@ -80,6 +70,10 @@ void setup() mqttSetup(); // Load Hostname before starting WiFi #endif +#if HASP_USE_GPIO > 0 + gpioSetup(); +#endif + #if HASP_USE_WIFI > 0 || HASP_USE_ETHERNET > 0 networkSetup(); #endif @@ -96,6 +90,10 @@ void setup() httpSetup(); #endif +#if HASP_USE_CONSOLE > 0 + consoleSetup(); +#endif + #if HASP_USE_TELNET > 0 telnetSetup(); #endif @@ -104,15 +102,16 @@ void setup() slaveSetup(); #endif - mainLastLoopTime = millis() - 1000; // reset loop counter - delay(250); + mainLastLoopTime = -1000; // reset loop counter + delay(20); // guiStart(); } -void loop() +IRAM_ATTR void loop() { guiLoop(); - haspLoop(); + // haspLoop(); + networkLoop(); #if HASP_USE_GPIO > 0 @@ -123,48 +122,67 @@ void loop() mqttLoop(); #endif // MQTT - debugLoop(); // Console - haspDevice.loop(); + // haspDevice.loop(); +#if HASP_USE_CONSOLE > 0 + // debugLoop(); + consoleLoop(); +#endif + + statLoopCounter++; /* Timer Loop */ if(millis() - mainLastLoopTime >= 1000) { + mainLastLoopTime += 1000; /* Runs Every Second */ - haspEverySecond(); // sleep timer - debugEverySecond(); // statusupdate + haspEverySecond(); // sleep timer & statusupdate - /* Runs Every 5 Seconds */ - if(mainLoopCounter == 0 || mainLoopCounter == 5) { - isConnected = networkEvery5Seconds(); // Check connection - -#if HASP_USE_HTTP > 0 - // httpEvery5Seconds(); +#if HASP_USE_TELNET > 0 + telnetEverySecond(); #endif + // debugEverySecond(); + + switch(++mainLoopCounter) { + case 1: + haspDevice.loop_5s(); + break; + + case 2: +#if HASP_USE_HTTP > 0 + // httpEvery5Seconds(); +#endif + break; + + case 3: +#if HASP_USE_GPIO > 0 + // gpioEvery5Seconds(); +#endif + break; + + case 4: + isConnected = networkEvery5Seconds(); // Check connection + #if HASP_USE_MQTT > 0 - mqttEvery5Seconds(isConnected); + mqttEvery5Seconds(isConnected); #endif + break; -#if HASP_USE_GPIO > 0 - // gpioEvery5Seconds(); -#endif - - haspDevice.loop_5s(); + case 5: + mainLoopCounter = 0; + // if(statLoopCounter) + // LOG_VERBOSE(TAG_MAIN, F("%d millis per loop, %d counted"), 5000 / statLoopCounter, + // statLoopCounter); + // statLoopCounter = 0; + break; } - - /* Reset loop counter every 10 seconds */ - if(mainLoopCounter >= 9) { - mainLoopCounter = 0; - } else { - mainLoopCounter++; - } - mainLastLoopTime += 1000; } #ifdef ARDUINO_ARCH_ESP8266 - delay(2); + delay(2); // ms #else - delay(6); + delay(2); // ms + // delay((lv_task_get_idle() >> 5) + 3); // 2..5 ms #endif } diff --git a/src/main_sdl2.cpp b/src/main_sdl2.cpp index 0c24ea37..cb85b61a 100644 --- a/src/main_sdl2.cpp +++ b/src/main_sdl2.cpp @@ -25,7 +25,7 @@ #include "hasp_conf.h" #include "lvgl.h" -#include "app_hal.h" +// #include "app_hal.h" #include "display/monitor.h" #include "hasp_debug.h" @@ -42,6 +42,9 @@ bool isRunning = 1; uint8_t mainLoopCounter = 0; unsigned long mainLastLoopTime = 0; +extern uint16_t tft_width; +extern uint16_t tft_height; + #if defined(WINDOWS) // https://gist.github.com/kingseva/a918ec66079a9475f19642ec31276a21 void BindStdHandlesToConsole() @@ -120,8 +123,6 @@ void setup() // hal_setup(); guiSetup(); - // debugSetup(); // Init the console - printf("%s %d\n", __FILE__, __LINE__); dispatchSetup(); // for hasp and oobe haspSetup(); @@ -132,9 +133,14 @@ void setup() mqttStart(); #endif - mainLastLoopTime = millis() - 1000; // reset loop counter - delay(250); +#if HASP_USE_GPIO > 0 printf("%s %d\n", __FILE__, __LINE__); + gpioSetup(); +#endif + + mainLastLoopTime = millis() - 1000; // reset loop counter + printf("%s %d\n", __FILE__, __LINE__); + // delay(250); } void loop() @@ -146,6 +152,10 @@ void loop() haspDevice.loop(); guiLoop(); +#if HASP_USE_GPIO > 0 + gpioLoop(); +#endif + /* Timer Loop */ if(millis() - mainLastLoopTime >= 1000) { /* Runs Every Second */ @@ -230,19 +240,27 @@ int main(int argc, char* argv[]) // Change to preferences dir std::cout << "\nCommand-line arguments:\n"; for(count = 0; count < argc; count++) - std::cout << " argv[" << count << "] " << argv[count] << "\n" << std::endl << std::flush; + std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; for(count = 0; count < argc; count++) { if(argv[count][0] == '-') { if(strncmp(argv[count], "--help", 6) == 0 || strncmp(argv[count], "-h", 2) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << "\n" << std::endl << std::flush; - fflush(stdout); - exit(0); + showhelp = true; } - if(strncmp(argv[count], "--name", 6) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << "\n" << std::endl << std::flush; + if(strncmp(argv[count], "--width", 7) == 0 || strncmp(argv[count], "-x", 2) == 0) { + int w = atoi(argv[count + 1]); + if(w > 0) tft_width = w; + } + + if(strncmp(argv[count], "--height", 8) == 0 || strncmp(argv[count], "-y", 2) == 0) { + int h = atoi(argv[count + 1]); + if(h > 0) tft_height = h; + } + + if(strncmp(argv[count], "--name", 6) == 0 || strncmp(argv[count], "-n", 2) == 0) { + std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; fflush(stdout); if(count + 1 < argc) { haspDevice.set_hostname(argv[count + 1]); @@ -257,24 +275,29 @@ int main(int argc, char* argv[]) usage("openHASP"); #if defined(WINDOWS) - WriteConsole(std_out, "bye", 3, NULL, NULL); - + WriteConsole(std_out, "bye\n", 3, NULL, NULL); + std::cout << std::endl << std::flush; + fflush(stdout); FreeConsole(); + exit(0); #endif return 0; } // printf("%s %d\n", __FILE__, __LINE__); // fflush(stdout); - printf("pre setup\n"); - setup(); - printf("to loop\n"); + debugPrintHaspHeader(stdout); + LOG_NOTICE(TAG_MAIN, "resolution %d x %d", tft_width, tft_height); + LOG_NOTICE(TAG_MAIN, "pre setup"); + + setup(); + + LOG_TRACE(TAG_MAIN, "loop started"); while(isRunning) { loop(); - // std::cout << "HSetup OK\n"; } - printf("endrunning\n"); + LOG_TRACE(TAG_MAIN, "main loop completed"); return 0; } diff --git a/src/mqtt/hasp_mqtt.h b/src/mqtt/hasp_mqtt.h index 29fe1434..52e08715 100644 --- a/src/mqtt/hasp_mqtt.h +++ b/src/mqtt/hasp_mqtt.h @@ -5,9 +5,7 @@ #define HASP_MQTT_H #include -#include "ArduinoJson.h" - -#include "hasp_conf.h" +#include "hasplib.h" // #if defined(WINDOWS) || defined(POSIX) // #define __FlashStringHelper char @@ -23,24 +21,67 @@ typedef enum { } hasp_mqtt_error_t; void mqttSetup(); -void mqttLoop(); +IRAM_ATTR void mqttLoop(); void mqttEvery5Seconds(bool wifiIsConnected); void mqttStart(); void mqttStop(); int mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload); int mqtt_send_state(const char* subtopic, const char* payload); +int mqtt_send_discovery(const char* payload, size_t len); int mqttPublish(const char* topic, const char* payload, size_t len, bool retain); bool mqttIsConnected(); +void mqtt_get_info(JsonDocument& doc); #if HASP_USE_CONFIG > 0 bool mqttGetConfig(const JsonObject& settings); bool mqttSetConfig(const JsonObject& settings); #endif -// #ifndef WINDOWS -// String mqttGetNodename(void); -// #endif - +#ifndef MQTT_PREFIX +#define MQTT_PREFIX "hasp" #endif + +#ifndef MQTT_TOPIC_STATE +#define MQTT_TOPIC_STATE "state" +#endif + +#ifndef MQTT_TOPIC_COMMAND +#define MQTT_TOPIC_COMMAND "command" +#endif + +#ifndef MQTT_TOPIC_DISCOVERY +#define MQTT_TOPIC_DISCOVERY "discovery" +#endif + +#ifndef MQTT_TOPIC_BROADCAST +#define MQTT_TOPIC_BROADCAST "broadcast" +#endif + +#define MQTT_TOPIC_LWT "LWT" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// These defaults may be overwritten with values saved by the web interface + +#ifndef MQTT_GROUPNAME +#define MQTT_GROUPNAME "plates"; +#endif + +#ifndef MQTT_HOST +#define MQTT_HOST ""; +#endif + +#ifndef MQTT_PORT +#define MQTT_PORT 1883; +#endif + +#ifndef MQTT_USER +#define MQTT_USER ""; +#endif + +#ifndef MQTT_PASSW +#define MQTT_PASSW ""; +#endif + +#endif // HASP_MQTT_H \ No newline at end of file diff --git a/src/mqtt/hasp_mqtt_ha.cpp b/src/mqtt/hasp_mqtt_ha.cpp index 03260e02..190105f8 100644 --- a/src/mqtt/hasp_mqtt_ha.cpp +++ b/src/mqtt/hasp_mqtt_ha.cpp @@ -17,8 +17,8 @@ #define RETAINED true // extern char mqttNodeName[16]; -extern char mqttNodeTopic[24]; -extern char mqttGroupTopic[24]; +extern std::string mqttNodeTopic; +extern std::string mqttGroupTopic; extern bool mqttEnabled; extern bool mqttHAautodiscover; @@ -75,10 +75,7 @@ void mqtt_ha_add_device_ids(JsonDocument& doc) ids.add(haspDevice.get_hostname()); ids.add(HASP_MAC_ADDRESS_STR); - char buffer[32]; - haspGetVersion(buffer, sizeof(buffer)); - device[F("sw")] = buffer; - + device[F("sw")] = haspDevice.get_version(); device[FPSTR(FP_MQTT_HA_NAME)] = haspDevice.get_hostname(); device[FPSTR(FP_MQTT_HA_MODEL)] = F(PIOENV); device[FPSTR(FP_MQTT_HA_MANUFACTURER)] = F(D_MANUFACTURER); @@ -106,7 +103,7 @@ void mqtt_ha_register_button(uint8_t page, uint8_t id) char buffer[128]; snprintf_P(buffer, sizeof(buffer), PSTR(HASP_OBJECT_NOTATION), page, id); doc[F("stype")] = buffer; // subtype = "p0b0" - snprintf_P(buffer, sizeof(buffer), PSTR("~state/" HASP_OBJECT_NOTATION), page, id); + snprintf_P(buffer, sizeof(buffer), PSTR("~" MQTT_TOPIC_STATE "/" HASP_OBJECT_NOTATION), page, id); doc[F("t")] = buffer; // topic doc[F("atype")] = "trigger"; // automation_type @@ -148,7 +145,7 @@ void mqtt_ha_register_switch(uint8_t page, uint8_t id) char buffer[128]; snprintf_P(buffer, sizeof(buffer), PSTR(HASP_OBJECT_NOTATION), page, id); doc[F("stype")] = buffer; // subtype = "p0b0" - snprintf_P(buffer, sizeof(buffer), PSTR("~state/" HASP_OBJECT_NOTATION), page, id); + snprintf_P(buffer, sizeof(buffer), PSTR("~" MQTT_TOPIC_STATE "/" HASP_OBJECT_NOTATION), page, id); doc[F("t")] = buffer; // topic doc[F("atype")] = F("binary_sensor"); // automation_type @@ -169,7 +166,7 @@ void mqtt_ha_register_connectivity() // start from static keys and values that do not change deserializeJson(doc, F("{\"device_class\":\"connectivity\",\"stat_t\":\"~LWT\",\"pl_on\":\"online\",\"pl_off\":" - "\"offline\",\"json_attr_t\":\"~state/statusupdate\"}")); + "\"offline\",\"json_attr_t\":\"~" MQTT_TOPIC_STATE "/statusupdate\"}")); mqtt_ha_add_device_ids(doc); mqtt_ha_add_unique_id(doc, item); @@ -187,13 +184,13 @@ void mqtt_ha_register_backlight() // start from static keys and values that do not change deserializeJson(doc, F("{" - "\"cmd_t\":\"~command/light\"," - "\"stat_t\":\"~state/light\"," + "\"cmd_t\":\"~" MQTT_TOPIC_COMMAND "/light\"," + "\"stat_t\":\"~" MQTT_TOPIC_STATE "/light\"," "\"pl_on\":\"on\"," "\"pl_off\":\"off\"," "\"avty_t\":\"~LWT\"," - "\"bri_stat_t\":\"~state/dim\"," - "\"bri_cmd_t\":\"~command/dim\"," + "\"bri_stat_t\":\"~" MQTT_TOPIC_STATE "/dim\"," + "\"bri_cmd_t\":\"~" MQTT_TOPIC_COMMAND "/dim\"," "\"bri_scl\":100}")); mqtt_ha_add_device_ids(doc); mqtt_ha_add_unique_id(doc, item); @@ -215,8 +212,8 @@ void mqtt_ha_register_moodlight() // start from static keys and values that do not change deserializeJson(doc, F("{" - "\"cmd_t\":\"~command/moodlight\"," - "\"stat_t\":\"~state/moodlight\"," + "\"cmd_t\":\"~" MQTT_TOPIC_COMMAND "/moodlight\"," + "\"stat_t\":\"~" MQTT_TOPIC_STATE "/moodlight\"," "\"platform\":\"mqtt\"," "\"schema\":\"json\"," "\"rgb\":true," @@ -225,12 +222,12 @@ void mqtt_ha_register_moodlight() /* deserializeJson(doc, F("{" "\"cmd_t\":\"~command/moodlight\"," - // "\"stat_t\":\"~state/moodlight\"," + // "\"stat_t\":\"~" MQTT_TOPIC_STATE "/moodlight\"," "\"avty_t\":\"~LWT\"," - "\"bri_stat_t\":\"~state/moodlight/dim\"," + "\"bri_stat_t\":\"~" MQTT_TOPIC_STATE "/moodlight/dim\"," "\"bri_cmd_t\":\"~command/moodlight/dim\"," "\"bri_scl\":100," - "\"rgb_stat_t\":\"~state/moodlight/rgb\"," + "\"rgb_stat_t\":\"~" MQTT_TOPIC_STATE "/moodlight/rgb\"," "\"rgb_cmd_t\":\"~command/moodlight/rgb\"}")); */ mqtt_ha_add_device_ids(doc); @@ -256,7 +253,8 @@ void mqtt_ha_register_idle() snprintf_P(item, sizeof(item), PSTR("idle")); // start from static keys and values that do not change - deserializeJson(doc, F("{\"stat_t\":\"~state/idle\",\"avty_t\":\"~LWT\",\"json_attr_t\":\"~state/statusupdate\"}")); + deserializeJson(doc, F("{\"stat_t\":\"~" MQTT_TOPIC_STATE + "/idle\",\"avty_t\":\"~LWT\",\"json_attr_t\":\"~" MQTT_TOPIC_STATE "/statusupdate\"}")); mqtt_ha_add_device_ids(doc); mqtt_ha_add_unique_id(doc, item); @@ -273,7 +271,8 @@ void mqtt_ha_register_activepage() snprintf_P(item, sizeof(item), PSTR("page")); // start from static keys and values that do not change - deserializeJson(doc, F("{\"cmd_t\":\"~command/page\",\"stat_t\":\"~state/page\",\"avty_t\":\"~LWT\"}")); + deserializeJson(doc, F("{\"cmd_t\":\"~" MQTT_TOPIC_COMMAND "/page\",\"stat_t\":\"~" MQTT_TOPIC_STATE + "/page\",\"avty_t\":\"~LWT\"}")); mqtt_ha_add_device_ids(doc); mqtt_ha_add_unique_id(doc, item); diff --git a/src/mqtt/hasp_mqtt_paho_async.cpp b/src/mqtt/hasp_mqtt_paho_async.cpp index 166ff099..609af505 100644 --- a/src/mqtt/hasp_mqtt_paho_async.cpp +++ b/src/mqtt/hasp_mqtt_paho_async.cpp @@ -63,36 +63,6 @@ const char* mqttGroupTopic = TOPIC; bool mqttEnabled = false; bool mqttHAautodiscover = true; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// These defaults may be overwritten with values saved by the web interface -#ifndef MQTT_HOST -#define MQTT_HOST ""; -#endif - -#ifndef MQTT_PORT -#define MQTT_PORT 1883; -#endif - -#ifndef MQTT_USER -#define MQTT_USER ""; -#endif - -#ifndef MQTT_PASSW -#define MQTT_PASSW ""; -#endif -#ifndef MQTT_NODENAME -#define MQTT_NODENAME ""; -#endif -#ifndef MQTT_GROUPNAME -#define MQTT_GROUPNAME ""; -#endif - -#ifndef MQTT_PREFIX -#define MQTT_PREFIX "hasp" -#endif - -#define LWT_TOPIC "LWT" - std::recursive_mutex dispatch_mtx; std::recursive_mutex publish_mtx; @@ -123,10 +93,10 @@ void connlost(void* context, char* cause) } // Receive incoming messages -static void mqtt_message_cb(char* topic, char* payload, unsigned int length) +static void mqtt_message_cb(char* topic, char* payload, size_t length) { // Handle incoming commands from MQTT if(length + 1 >= MQTT_MAX_PACKET_SIZE) { - LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), length); + LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), (uint32_t)length); return; } else { payload[length] = '\0'; @@ -164,12 +134,12 @@ static void mqtt_message_cb(char* topic, char* payload, unsigned int length) } // catch a dangling LWT from a previous connection if it appears - if(!strcmp_P(topic, PSTR(LWT_TOPIC))) { // endsWith LWT + if(!strcmp_P(topic, PSTR(MQTT_TOPIC_LWT))) { // endsWith LWT if(!strcasecmp_P((char*)payload, PSTR("offline"))) { { char msg[8]; char tmp_topic[strlen(mqttNodeTopic) + 8]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_LWT), mqttNodeTopic); snprintf_P(msg, sizeof(msg), PSTR("online")); // /*bool res =*/mqttClient.publish(tmp_topic, msg, true); @@ -254,12 +224,12 @@ void onConnect(void* context, MQTTAsync_successData* response) printf("Successful connection\n"); - mqtt_subscribe(context, TOPIC "command/#"); - mqtt_subscribe(context, TOPIC "command"); + mqtt_subscribe(context, TOPIC MQTT_TOPIC_COMMAND "/#"); + mqtt_subscribe(context, TOPIC MQTT_TOPIC_COMMAND); mqtt_subscribe(context, TOPIC "light"); mqtt_subscribe(context, TOPIC "dim"); - mqttPublish(TOPIC LWT_TOPIC, "online", false); + mqttPublish(TOPIC MQTT_TOPIC_LWT, "online", false); mqtt_send_object_state(0, 0, "connected"); std::cout << std::endl; @@ -340,15 +310,15 @@ bool mqttIsConnected() void mqtt_send_state(const __FlashStringHelper* subtopic, const char* payload) { char tmp_topic[strlen(mqttNodeTopic) + 20]; - printf(("%sstate/%s\n"), mqttNodeTopic, subtopic); - snprintf_P(tmp_topic, sizeof(tmp_topic), ("%sstate/%s"), mqttNodeTopic, subtopic); + printf(("%s" MQTT_TOPIC_STATE "/%s\n"), mqttNodeTopic, subtopic); + snprintf_P(tmp_topic, sizeof(tmp_topic), ("%s" MQTT_TOPIC_STATE "/%s"), mqttNodeTopic, subtopic); mqttPublish(tmp_topic, payload, false); } void mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload) { char tmp_topic[strlen(mqttNodeTopic) + 20]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/p%ub%u"), mqttNodeTopic, pageid, btnid); + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_STATE "/p%ub%u"), mqttNodeTopic, pageid, btnid); mqttPublish(tmp_topic, payload, false); } @@ -430,7 +400,7 @@ void mqttStop() void mqttSetup(){}; -void mqttLoop(){}; +IRAM_ATTR void mqttLoop(){}; void mqttEvery5Seconds(bool wifiIsConnected){}; diff --git a/src/mqtt/hasp_mqtt_paho_single.cpp b/src/mqtt/hasp_mqtt_paho_single.cpp index 3c1f1841..7ec6409e 100644 --- a/src/mqtt/hasp_mqtt_paho_single.cpp +++ b/src/mqtt/hasp_mqtt_paho_single.cpp @@ -3,9 +3,7 @@ /* Single threaded synchronous paho client */ -#include - -#include "hasp_conf.h" +#include "hasplib.h" #if HASP_USE_MQTT > 0 #ifdef USE_PAHO @@ -50,55 +48,26 @@ #include #endif -#define ADDRESS "10.1.0.208:1883" -#define CLIENTID "test1123" -#define TOPIC "hasp/plate35/" +// #define ADDRESS "10.1.0.208:1883" +// #define CLIENTID "test1123" +// #define TOPIC "hasp/plate35/" #define QOS 1 #define TIMEOUT 1000L -char mqttNodeTopic[24] = "hasp/plate35/"; -const char* mqttGroupTopic = "hasp/plates/"; -// char mqttNodeTopic[24]; -// char mqttGroupTopic[24]; +std::string mqttNodeTopic; +std::string mqttGroupTopic; +std::string mqttLwtTopic; bool mqttEnabled = false; bool mqttHAautodiscover = true; +uint32_t mqttPublishCount; +uint32_t mqttReceiveCount; +uint32_t mqttFailedCount; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// These defaults may be overwritten with values saved by the web interface -#ifndef MQTT_HOST -#define MQTT_HOST ""; -#endif - -#ifndef MQTT_PORT -#define MQTT_PORT 1883; -#endif - -#ifndef MQTT_USER -#define MQTT_USER ""; -#endif - -#ifndef MQTT_PASSW -#define MQTT_PASSW ""; -#endif -#ifndef MQTT_NODENAME -#define MQTT_NODENAME ""; -#endif -#ifndef MQTT_GROUPNAME -#define MQTT_GROUPNAME ""; -#endif - -#ifndef MQTT_PREFIX -#define MQTT_PREFIX "hasp" -#endif - -#define LWT_TOPIC "LWT" - -char mqttServer[16] = MQTT_HOST; -char mqttUser[23] = MQTT_USER; -char mqttPassword[32] = MQTT_PASSW; -// char mqttNodeName[16] = MQTT_NODENAME; -char mqttGroupName[16] = MQTT_GROUPNAME; -uint16_t mqttPort = MQTT_PORT; +std::string mqttServer = MQTT_HOST; +std::string mqttUser = MQTT_USER; +std::string mqttPassword = MQTT_PASSW; +std::string mqttGroupName = MQTT_GROUPNAME; +uint16_t mqttPort = MQTT_PORT; MQTTClient mqtt_client; @@ -120,29 +89,41 @@ void connlost(void* context, char* cause) } // Receive incoming messages -static void mqtt_message_cb(char* topic, char* payload, unsigned int length) +static void mqtt_message_cb(char* topic, char* payload, size_t length) { // Handle incoming commands from MQTT if(length + 1 >= MQTT_MAX_PACKET_SIZE) { - LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), length); + mqttFailedCount++; + LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), (uint32_t)length); return; } else { + mqttReceiveCount++; payload[length] = '\0'; } LOG_TRACE(TAG_MQTT_RCV, F("%s = %s"), topic, (char*)payload); - if(topic == strstr(topic, mqttNodeTopic)) { // startsWith mqttNodeTopic + if(topic == strstr(topic, mqttNodeTopic.c_str())) { // startsWith mqttNodeTopic // Node topic - topic += strlen(mqttNodeTopic); // shorten topic + topic += mqttNodeTopic.length(); // shorten topic - } else if(topic == strstr(topic, mqttGroupTopic)) { // startsWith mqttGroupTopic + } else if(topic == strstr(topic, mqttGroupTopic.c_str())) { // startsWith mqttGroupTopic // Group topic - topic += strlen(mqttGroupTopic); // shorten topic - dispatch_topic_payload(topic, (const char*)payload); + topic += mqttGroupTopic.length(); // shorten topic + dispatch_topic_payload(topic, (const char*)payload, length > 0); return; +#ifdef HASP_USE_BROADCAST + } else if(topic == strstr_P(topic, PSTR(MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST + "/"))) { // /" MQTT_TOPIC_BROADCAST "/ discovery topic + + // /" MQTT_TOPIC_BROADCAST "/ topic + topic += strlen(MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST "/"); // shorten topic + dispatch_topic_payload(topic, (const char*)payload, length > 0); + return; +#endif + #ifdef HASP_USE_HA } else if(topic == strstr_P(topic, PSTR("homeassistant/status"))) { // HA discovery topic if(mqttHAautodiscover && !strcasecmp_P((char*)payload, PSTR("online"))) { @@ -159,31 +140,24 @@ static void mqtt_message_cb(char* topic, char* payload, unsigned int length) } // catch a dangling LWT from a previous connection if it appears - if(!strcmp_P(topic, PSTR(LWT_TOPIC))) { // endsWith LWT + if(!strcmp_P(topic, PSTR(MQTT_TOPIC_LWT))) { // endsWith LWT if(!strcasecmp_P((char*)payload, PSTR("offline"))) { { char msg[8]; - char tmp_topic[strlen(mqttNodeTopic) + 8]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); snprintf_P(msg, sizeof(msg), PSTR("online")); - - // /*bool res =*/mqttClient.publish(tmp_topic, msg, true); - mqttPublish(tmp_topic, msg, strlen(msg), true); + mqttPublish(mqttLwtTopic.c_str(), msg, strlen(msg), true); } } else { // LOG_TRACE(TAG_MQTT, F("ignoring LWT = online")); } } else { - dispatch_topic_payload(topic, (const char*)payload); + dispatch_topic_payload(topic, (const char*)payload, length > 0); } } -int msgarrvd(void* context, char* topicName, int topicLen, MQTTClient_message* message) +int mqtt_message_arrived(void* context, char* topicName, int topicLen, MQTTClient_message* message) { - // printf("MQT RCV >> "); - // printf("%s => %.*s (%d)\n", topicName, message->payloadlen, (char *)message->payload, message->payloadlen); - char msg[message->payloadlen + 1]; memcpy(msg, (char*)message->payload, message->payloadlen); msg[message->payloadlen] = '\0'; @@ -200,10 +174,10 @@ void mqtt_subscribe(void* context, const char* topic) MQTTClient client = (MQTTClient)context; int rc; - printf("Subscribing to topic %s\n", topic); - //\nfor client %s using QoS%d\n\n", topic, CLIENTID, QOS); if((rc = MQTTClient_subscribe(client, topic, QOS)) != MQTTCLIENT_SUCCESS) { - printf("Failed to start subscribe, return code %d\n", rc); + LOG_WARNING(TAG_MQTT, D_BULLET D_MQTT_NOT_SUBSCRIBED, topic); // error code rc + } else { + LOG_VERBOSE(TAG_MQTT, D_BULLET D_MQTT_SUBSCRIBED, topic); } } @@ -211,7 +185,12 @@ void mqtt_subscribe(void* context, const char* topic) int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) { - if(!mqttIsConnected()) return MQTT_ERR_NO_CONN; + if(!mqttEnabled) return MQTT_ERR_DISABLED; + + if(!mqttIsConnected()) { + mqttFailedCount++; + return MQTT_ERR_NO_CONN; + } MQTTClient_message pubmsg = MQTTClient_message_initializer; MQTTClient_deliveryToken token; @@ -222,41 +201,46 @@ int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) pubmsg.retained = retain; MQTTClient_publishMessage(mqtt_client, topic, &pubmsg, &token); - int rc = MQTTClient_waitForCompletion(mqtt_client, token, TIMEOUT); + int rc = MQTTClient_waitForCompletion(mqtt_client, token, TIMEOUT); // time to wait in milliseconds if(rc != MQTTCLIENT_SUCCESS) { + mqttFailedCount++; LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " '%s' => %s"), topic, payload); return MQTT_ERR_PUB_FAIL; } else { // LOG_TRACE(TAG_MQTT_PUB, F("'%s' => %s OK"), topic, payload); + mqttPublishCount++; return MQTT_ERR_OK; } } -// static bool mqttPublish(const char* topic, const char* payload, bool retain) -// { -// return mqttPublish(topic, payload, strlen(payload), retain); -// } - /* ===== Public HASP MQTT functions ===== */ bool mqttIsConnected() { - return connected == 1; + return MQTTClient_isConnected(mqtt_client); } int mqtt_send_state(const __FlashStringHelper* subtopic, const char* payload) { - char tmp_topic[strlen(mqttNodeTopic) + 20]; - // printf(("%sstate/%s\n"), mqttNodeTopic, subtopic); - snprintf_P(tmp_topic, sizeof(tmp_topic), ("%sstate/%s"), mqttNodeTopic, subtopic); + char tmp_topic[mqttNodeTopic.length() + 20]; + // printf(("%s" MQTT_TOPIC_STATE "/%s\n"), mqttNodeTopic, subtopic); + snprintf_P(tmp_topic, sizeof(tmp_topic), ("%s" MQTT_TOPIC_STATE "/%s"), mqttNodeTopic.c_str(), subtopic); return mqttPublish(tmp_topic, payload, strlen(payload), false); } +int mqtt_send_discovery(const char* payload, size_t len) +{ + char tmp_topic[20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR(MQTT_PREFIX "/" MQTT_TOPIC_DISCOVERY)); + return mqttPublish(tmp_topic, payload, len, false); +} + int mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload) { - char tmp_topic[strlen(mqttNodeTopic) + 20]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/p%ub%u"), mqttNodeTopic, pageid, btnid); + char tmp_topic[mqttNodeTopic.length() + 20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_STATE "/p%ub%u"), mqttNodeTopic.c_str(), pageid, + btnid); return mqttPublish(tmp_topic, payload, strlen(payload), false); } @@ -264,35 +248,47 @@ static void onConnect(void* context) { MQTTClient client = (MQTTClient)context; connected = 1; + std::string topic; - printf("Successful connection\n"); + LOG_VERBOSE(TAG_MQTT, D_MQTT_CONNECTED, mqttServer.c_str(), haspDevice.get_hostname()); - mqtt_subscribe(mqtt_client, TOPIC "command/#"); - // mqtt_subscribe(mqtt_client, TOPIC "command"); - mqtt_subscribe(mqtt_client, TOPIC "light/#"); - mqtt_subscribe(mqtt_client, TOPIC "brightness/#"); - mqtt_subscribe(mqtt_client, "hass/status"); + topic = mqttGroupTopic + MQTT_TOPIC_COMMAND "/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); + + topic = mqttNodeTopic + MQTT_TOPIC_COMMAND "/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); + + topic = mqttGroupTopic + "config/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); + + topic = mqttNodeTopic + "config/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); + +#ifdef HASP_USE_BROADCAST + topic = MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST "/" MQTT_TOPIC_COMMAND "/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); +#endif /* Home Assistant auto-configuration */ #ifdef HASP_USE_HA - if(mqttHAautodiscover) mqtt_subscribe(mqtt_client, "homeassistant/status"); + topic = "homeassistant/status"; + mqtt_subscribe(mqtt_client, topic.c_str()); #endif - mqttPublish(TOPIC LWT_TOPIC, "online", 6, true); - - mqtt_send_object_state(0, 0, "connected"); - std::cout << std::endl; + mqttPublish(mqttLwtTopic.c_str(), "online", 6, true); } void mqttStart() { + printf("%s %d\n", __FILE__, __LINE__); MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; MQTTClient_willOptions will_opts = MQTTClient_willOptions_initializer; int rc; int ch; - if((rc = MQTTClient_create(&mqtt_client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != - MQTTCLIENT_SUCCESS) { + printf("%s %d\n", __FILE__, __LINE__); + if((rc = MQTTClient_create(&mqtt_client, mqttServer.c_str(), haspDevice.get_hostname(), MQTTCLIENT_PERSISTENCE_NONE, + NULL)) != MQTTCLIENT_SUCCESS) { printf("Failed to create client, return code %d\n", rc); rc = EXIT_FAILURE; return; @@ -304,26 +300,39 @@ void mqttStart() // return; // } - conn_opts.will = &will_opts; - conn_opts.will->message = "offline"; - conn_opts.will->qos = 1; - conn_opts.will->retained = 1; - conn_opts.will->topicName = "hasp/plate35/LWT"; + printf("%s %d\n", __FILE__, __LINE__); + mqttEnabled = mqttServer.length() > 0 && mqttPort > 0; - conn_opts.keepAliveInterval = 20; - conn_opts.cleansession = 1; + if(mqttEnabled) { + conn_opts.will = &will_opts; + conn_opts.will->message = "offline"; + conn_opts.will->qos = 1; + conn_opts.will->retained = 1; + conn_opts.will->topicName = mqttLwtTopic.c_str(); - conn_opts.username = "hasp"; - conn_opts.password = "hasp"; + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.connectTimeout = 2; // seconds + conn_opts.retryInterval = 0; // no retry - if((rc = MQTTClient_connect(mqtt_client, &conn_opts)) != MQTTCLIENT_SUCCESS) { - printf("Failed to start connect, return code %d\n", rc); - rc = EXIT_FAILURE; - // goto destroy_exit; + conn_opts.username = mqttUser.c_str(); + conn_opts.password = mqttPassword.c_str(); + + printf("%s %d\n", __FILE__, __LINE__); + if((rc = MQTTClient_connect(mqtt_client, &conn_opts)) != MQTTCLIENT_SUCCESS) { + printf("Failed to connect, return code %d\n", rc); + rc = EXIT_FAILURE; + // goto destroy_exit; + } else { + onConnect(&mqtt_client); + } } else { - onConnect(&mqtt_client); + rc = EXIT_FAILURE; + printf("Mqtt server not configured\n"); } + printf("%s %d\n", __FILE__, __LINE__); + // while (!subscribed && !finished) // #if defined(_WIN32) // Sleep(100); @@ -342,7 +351,7 @@ void mqttStop() // disc_opts.onSuccess = onDisconnect; // disc_opts.onFailure = onDisconnectFailure; if((rc = MQTTClient_disconnect(mqtt_client, 1000)) != MQTTCLIENT_SUCCESS) { - printf("Failed to start disconnect, return code %d\n", rc); + printf("Failed to disconnect, return code %d\n", rc); rc = EXIT_FAILURE; // goto destroy_exit; } @@ -361,19 +370,59 @@ void mqttStop() // return rc; } -void mqttSetup(){}; - -char* topicName; -int topicLen; -MQTTClient_message* message; -void mqttLoop() +void mqttSetup() { + printf("%s %d\n", __FILE__, __LINE__); + mqttNodeTopic = MQTT_PREFIX; + mqttNodeTopic += "/"; + mqttNodeTopic += haspDevice.get_hostname(); + mqttNodeTopic += "/"; + + printf("%s %d\n", __FILE__, __LINE__); + mqttGroupTopic = MQTT_PREFIX; + mqttGroupTopic += "/"; + mqttGroupTopic += mqttGroupName; + mqttGroupTopic += "/"; + + printf("%s %d\n", __FILE__, __LINE__); + mqttLwtTopic = mqttNodeTopic; + mqttLwtTopic += MQTT_TOPIC_LWT; + + printf("%s %d\n", __FILE__, __LINE__); +} + +IRAM_ATTR void mqttLoop() +{ + int topicLen; + char* topicName; // Freed by msgarrvd + MQTTClient_message* message; // Freed by msgarrvd int rc = MQTTClient_receive(mqtt_client, &topicName, &topicLen, &message, 4); - if(rc == MQTTCLIENT_SUCCESS && message) msgarrvd(mqtt_client, topicName, topicLen, message); + if(rc == MQTTCLIENT_SUCCESS && message) mqtt_message_arrived(mqtt_client, topicName, topicLen, message); }; void mqttEvery5Seconds(bool wifiIsConnected){}; +void mqtt_get_info(JsonDocument& doc) +{ + char mqttClientId[64]; + + JsonObject info = doc.createNestedObject(F("MQTT")); + info[F(D_INFO_SERVER)] = mqttServer; + info[F(D_INFO_USERNAME)] = mqttUser; + info[F(D_INFO_CLIENTID)] = haspDevice.get_hostname(); + + if(mqttIsConnected()) { // Check MQTT connection + info[F(D_INFO_STATUS)] = F(D_INFO_CONNECTED); + } else { + info[F(D_INFO_STATUS)] = F("" D_INFO_DISCONNECTED ", return code: "); + // +String(mqttClient.returnCode()); + } + + info[F(D_INFO_RECEIVED)] = mqttReceiveCount; + info[F(D_INFO_PUBLISHED)] = mqttPublishCount; + info[F(D_INFO_FAILED)] = mqttFailedCount; +} + #endif // USE_PAHO #endif // USE_MQTT diff --git a/src/mqtt/hasp_mqtt_pubsubclient.cpp b/src/mqtt/hasp_mqtt_pubsubclient.cpp index 17eaa93f..93da72f6 100644 --- a/src/mqtt/hasp_mqtt_pubsubclient.cpp +++ b/src/mqtt/hasp_mqtt_pubsubclient.cpp @@ -50,60 +50,35 @@ char mqttNodeTopic[24]; char mqttGroupTopic[24]; bool mqttEnabled = false; bool mqttHAautodiscover = true; +uint32_t mqttPublishCount; +uint32_t mqttReceiveCount; +uint32_t mqttFailedCount; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// These defaults may be overwritten with values saved by the web interface -#ifndef MQTT_HOST -#define MQTT_HOST ""; -#endif - -#ifndef MQTT_PORT -#define MQTT_PORT 1883; -#endif - -#ifndef MQTT_USER -#define MQTT_USER ""; -#endif - -#ifndef MQTT_PASSW -#define MQTT_PASSW ""; -#endif -#ifndef MQTT_NODENAME -#define MQTT_NODENAME ""; -#endif -#ifndef MQTT_GROUPNAME -#define MQTT_GROUPNAME ""; -#endif - -#ifndef MQTT_PREFIX -#define MQTT_PREFIX "hasp" -#endif - -#define LWT_TOPIC "LWT" - -// char mqttServer[16] = MQTT_HOST; -// char mqttUser[23] = MQTT_USER; -// char mqttPassword[32] = MQTT_PASSW; -// char mqttGroupName[16] = MQTT_GROUPNAME; -std::string mqttServer = MQTT_HOST; -std::string mqttUser = MQTT_USER; -std::string mqttPassword = MQTT_PASSW; -std::string mqttGroupName = MQTT_GROUPNAME; - -uint16_t mqttPort = MQTT_PORT; +char mqttServer[16] = MQTT_HOST; +char mqttUser[23] = MQTT_USER; +char mqttPassword[32] = MQTT_PASSW; +// char mqttNodeName[16] = MQTT_NODENAME; +char mqttGroupName[16] = MQTT_GROUPNAME; +uint16_t mqttPort = MQTT_PORT; PubSubClient mqttClient(mqttNetworkClient); int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) { if(!mqttEnabled) return MQTT_ERR_DISABLED; - if(!mqttClient.connected()) return MQTT_ERR_NO_CONN; + + if(!mqttClient.connected()) { + mqttFailedCount++; + return MQTT_ERR_NO_CONN; + } if(mqttClient.beginPublish(topic, len, retain)) { + mqttPublishCount++; mqttClient.write((uint8_t*)payload, len); mqttClient.endPublish(); return MQTT_ERR_OK; } + mqttFailedCount++; return MQTT_ERR_PUB_FAIL; } @@ -125,8 +100,8 @@ bool mqtt_send_lwt(bool online) char tmp_payload[8]; char tmp_topic[strlen(mqttNodeTopic) + 4]; strncpy(tmp_topic, mqttNodeTopic, sizeof(tmp_topic)); - strncat_P(tmp_topic, PSTR(LWT_TOPIC), sizeof(tmp_topic)); - // snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); + strncat_P(tmp_topic, PSTR(MQTT_TOPIC_LWT), sizeof(tmp_topic)); + // snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_LWT), mqttNodeTopic); size_t len = snprintf_P(tmp_payload, sizeof(tmp_payload), online ? PSTR("online") : PSTR("offline")); bool res = mqttPublish(tmp_topic, tmp_payload, len, true); @@ -137,25 +112,35 @@ bool mqtt_send_lwt(bool online) int mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload) { char tmp_topic[strlen(mqttNodeTopic) + 16]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/" HASP_OBJECT_NOTATION), mqttNodeTopic, pageid, btnid); + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_STATE "/" HASP_OBJECT_NOTATION), mqttNodeTopic, + pageid, btnid); return mqttPublish(tmp_topic, payload, false); } int mqtt_send_state(const char* subtopic, const char* payload) { char tmp_topic[strlen(mqttNodeTopic) + 20]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/%s"), mqttNodeTopic, subtopic); + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_STATE "/%s"), mqttNodeTopic, subtopic); return mqttPublish(tmp_topic, payload, false); } +int mqtt_send_discovery(const char* payload, size_t len) +{ + char tmp_topic[20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR(MQTT_PREFIX "/" MQTT_TOPIC_DISCOVERY)); + return mqttPublish(tmp_topic, payload, len, false); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Receive incoming messages static void mqtt_message_cb(char* topic, byte* payload, unsigned int length) { // Handle incoming commands from MQTT if(length + 1 >= mqttClient.getBufferSize()) { - LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), length); + mqttFailedCount++; + LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), (uint32_t)length); return; } else { + mqttReceiveCount++; payload[length] = '\0'; } @@ -170,9 +155,18 @@ static void mqtt_message_cb(char* topic, byte* payload, unsigned int length) // Group topic topic += strlen(mqttGroupTopic); // shorten topic - dispatch_topic_payload(topic, (const char*)payload); + dispatch_topic_payload(topic, (const char*)payload, length > 0); return; +#ifdef HASP_USE_BROADCAST + } else if(topic == strstr_P(topic, PSTR(MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST "/"))) { // broadcast topic + + // Broadcast topic + topic += strlen_P(PSTR(MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST "/")); // shorten topic + dispatch_topic_payload(topic, (const char*)payload, length > 0); + return; +#endif + #ifdef HASP_USE_HA } else if(topic == strstr_P(topic, PSTR("homeassistant/status"))) { // HA discovery topic if(mqttHAautodiscover && !strcasecmp_P((char*)payload, PSTR("online"))) { @@ -189,34 +183,36 @@ static void mqtt_message_cb(char* topic, byte* payload, unsigned int length) } // catch a dangling LWT from a previous connection if it appears - if(!strcmp_P(topic, PSTR(LWT_TOPIC))) { // endsWith LWT - if(!strcasecmp_P((char*)payload, PSTR("offline"))) { - { - char msg[8]; - char tmp_topic[strlen(mqttNodeTopic) + 8]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); - snprintf_P(msg, sizeof(msg), PSTR("online")); + /* if(!strcmp_P(topic, PSTR(MQTT_TOPIC_LWT))) { // endsWith LWT + if(!strcasecmp_P((char*)payload, PSTR("offline"))) { + { + char msg[8]; + char tmp_topic[strlen(mqttNodeTopic) + 8]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_LWT), mqttNodeTopic); + snprintf_P(msg, sizeof(msg), PSTR("online")); - // /*bool res =*/mqttClient.publish(tmp_topic, msg, true); - mqttPublish(tmp_topic, msg, true); - } - - } else { - // LOG_TRACE(TAG_MQTT, F("ignoring LWT = online")); - } - } else { - dispatch_topic_payload(topic, (const char*)payload); + // bool res = + mqttClient.publish(tmp_topic, msg, true); + mqttPublish(tmp_topic, msg, true); + } + } + else + { + // LOG_TRACE(TAG_MQTT, F("ignoring LWT = online")); + } + } + else */ + { + dispatch_topic_payload(topic, (const char*)payload, length > 0); } } -static void mqttSubscribeTo(const __FlashStringHelper* format, const char* data) +static void mqttSubscribeTo(const char* topic) { - char tmp_topic[strlen_P((PGM_P)format) + 2 + strlen(data)]; - snprintf_P(tmp_topic, sizeof(tmp_topic), (PGM_P)format, data); - if(mqttClient.subscribe(tmp_topic)) { - LOG_VERBOSE(TAG_MQTT, F(D_BULLET D_MQTT_SUBSCRIBED), tmp_topic); + if(mqttClient.subscribe(topic)) { + LOG_VERBOSE(TAG_MQTT, F(D_BULLET D_MQTT_SUBSCRIBED), topic); } else { - LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_SUBSCRIBED), tmp_topic); + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_SUBSCRIBED), topic); } } @@ -228,7 +224,7 @@ void mqttStart() static uint8_t mqttReconnectCount = 0; // bool mqttFirstConnect = true; - mqttClient.setServer(mqttServer.c_str(), 1883); + mqttClient.setServer(mqttServer, 1883); // mqttClient.setSocketTimeout(10); //in seconds /* Construct unique Client ID*/ @@ -241,13 +237,12 @@ void mqttStart() } // Attempt to connect and set LWT and Clean Session - snprintf_P(buffer, sizeof(buffer), PSTR("%s" LWT_TOPIC), mqttNodeTopic); // lastWillTopic - snprintf_P(lastWillPayload, sizeof(lastWillPayload), PSTR("offline")); // lastWillPayload + snprintf_P(buffer, sizeof(buffer), PSTR("%s" MQTT_TOPIC_LWT), mqttNodeTopic); // lastWillTopic + snprintf_P(lastWillPayload, sizeof(lastWillPayload), PSTR("offline")); // lastWillPayload haspProgressMsg(F(D_MQTT_CONNECTING)); haspProgressVal(mqttReconnectCount * 5); - if(!mqttClient.connect(mqttClientId, mqttUser.c_str(), mqttPassword.c_str(), buffer, 0, true, lastWillPayload, - true)) { + if(!mqttClient.connect(mqttClientId, mqttUser, mqttPassword, buffer, 0, true, lastWillPayload, true)) { // Retry until we give up and restart after connectTimeout seconds mqttReconnectCount++; @@ -292,19 +287,22 @@ void mqttStart() return; } - LOG_INFO(TAG_MQTT, F(D_MQTT_CONNECTED), mqttServer.c_str(), mqttClientId); + LOG_INFO(TAG_MQTT, F(D_MQTT_CONNECTED), mqttServer, mqttClientId); // Subscribe to our incoming topics - const __FlashStringHelper* F_topic; - F_topic = F("%scommand/#"); - mqttSubscribeTo(F_topic, mqttGroupTopic); - mqttSubscribeTo(F_topic, mqttNodeTopic); - F_topic = F("%sconfig/#"); - mqttSubscribeTo(F_topic, mqttGroupTopic); - mqttSubscribeTo(F_topic, mqttNodeTopic); - // mqttSubscribeTo(F("%slight/#"), mqttNodeTopic); - // mqttSubscribeTo(F("%sbrightness/#"), mqttNodeTopic); - // mqttSubscribeTo(F("%s"LWT_TOPIC), mqttNodeTopic); + char topic[64]; + snprintf_P(topic, sizeof(topic), PSTR("%s" MQTT_TOPIC_COMMAND "/#"), mqttGroupTopic); + mqttSubscribeTo(topic); + snprintf_P(topic, sizeof(topic), PSTR("%s" MQTT_TOPIC_COMMAND "/#"), mqttNodeTopic); + mqttSubscribeTo(topic); + // F_topic = F("%sconfig/#"); + // mqttSubscribeTo(F_topic, mqttGroupTopic); + // mqttSubscribeTo(F("%s"MQTT_TOPIC_LWT), mqttNodeTopic); + +#ifdef HASP_USE_BROADCAST + snprintf_P(topic, sizeof(topic), PSTR(MQTT_PREFIX "/" MQTT_TOPIC_BROADCAST "/" MQTT_TOPIC_COMMAND "/#")); + mqttSubscribeTo(topic); +#endif /* Home Assistant auto-configuration */ #ifdef HASP_USE_HA @@ -328,9 +326,9 @@ void mqttStart() void mqttSetup() { - mqttEnabled = mqttServer.length() > 0 && mqttPort > 0; + mqttEnabled = strlen(mqttServer) > 0 && mqttPort > 0; if(mqttEnabled) { - mqttClient.setServer(mqttServer.c_str(), mqttPort); + mqttClient.setServer(mqttServer, mqttPort); mqttClient.setCallback(mqtt_message_cb); // if(!mqttClient.setBufferSize(1024)) { // LOG_ERROR(TAG_MQTT, F("Buffer allocation failed")); @@ -342,9 +340,10 @@ void mqttSetup() } } -void mqttLoop(void) +IRAM_ATTR void mqttLoop(void) { - if(mqttEnabled) mqttClient.loop(); + // if(mqttEnabled) + mqttClient.loop(); } void mqttEvery5Seconds(bool networkIsConnected) @@ -370,6 +369,33 @@ void mqttStop() } } +void mqtt_get_info(JsonDocument& doc) +{ + char mqttClientId[64]; + String mac((char*)0); + mac.reserve(64); + + JsonObject info = doc.createNestedObject(F("MQTT")); + info[F(D_INFO_SERVER)] = mqttServer; + info[F(D_INFO_USERNAME)] = mqttUser; + + mac = halGetMacAddress(3, ""); + mac.toLowerCase(); + snprintf_P(mqttClientId, sizeof(mqttClientId), PSTR("%s-%s"), haspDevice.get_hostname(), mac.c_str()); + info[F(D_INFO_CLIENTID)] = mqttClientId; + + if(mqttIsConnected()) { // Check MQTT connection + info[F(D_INFO_STATUS)] = F(D_INFO_CONNECTED); + } else { + info[F(D_INFO_STATUS)] = F("" D_INFO_DISCONNECTED ", return code: "); + // +String(mqttClient.returnCode()); + } + + info[F(D_INFO_RECEIVED)] = mqttReceiveCount; + info[F(D_INFO_PUBLISHED)] = mqttPublishCount; + info[F(D_INFO_FAILED)] = mqttFailedCount; +} + #if HASP_USE_CONFIG > 0 bool mqttGetConfig(const JsonObject& settings) { @@ -378,19 +404,19 @@ bool mqttGetConfig(const JsonObject& settings) if(strcmp(haspDevice.get_hostname(), settings[FPSTR(FP_CONFIG_NAME)].as().c_str()) != 0) changed = true; settings[FPSTR(FP_CONFIG_NAME)] = haspDevice.get_hostname(); - if(mqttGroupName != settings[FPSTR(FP_CONFIG_GROUP)].as()) changed = true; + if(strcmp(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)].as().c_str()) != 0) changed = true; settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName; - if(mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as()) changed = true; + if(strcmp(mqttServer, settings[FPSTR(FP_CONFIG_HOST)].as().c_str()) != 0) changed = true; settings[FPSTR(FP_CONFIG_HOST)] = mqttServer; if(mqttPort != settings[FPSTR(FP_CONFIG_PORT)].as()) changed = true; settings[FPSTR(FP_CONFIG_PORT)] = mqttPort; - if(mqttUser != settings[FPSTR(FP_CONFIG_USER)].as().c_str()) changed = true; + if(strcmp(mqttUser, settings[FPSTR(FP_CONFIG_USER)].as().c_str()) != 0) changed = true; settings[FPSTR(FP_CONFIG_USER)] = mqttUser; - if(mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as().c_str()) changed = true; + if(strcmp(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)].as().c_str()) != 0) changed = true; settings[FPSTR(FP_CONFIG_PASS)] = mqttPassword; if(changed) configOutput(settings, TAG_MQTT); @@ -428,33 +454,33 @@ bool mqttSetConfig(const JsonObject& settings) } if(!settings[FPSTR(FP_CONFIG_GROUP)].isNull()) { - changed |= mqttGroupName != settings[FPSTR(FP_CONFIG_GROUP)].as(); - mqttGroupName = settings[FPSTR(FP_CONFIG_GROUP)].as(); + changed |= strcmp(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)]) != 0; + strncpy(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)], sizeof(mqttGroupName)); } - if(mqttGroupName.length() == 0) { - mqttGroupName = String(F("plates")).c_str(); - changed = true; + if(strlen(mqttGroupName) == 0) { + strcpy_P(mqttGroupName, PSTR("plates")); + changed = true; } if(!settings[FPSTR(FP_CONFIG_HOST)].isNull()) { - changed |= mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as(); - mqttServer = settings[FPSTR(FP_CONFIG_HOST)].as(); + changed |= strcmp(mqttServer, settings[FPSTR(FP_CONFIG_HOST)]) != 0; + strncpy(mqttServer, settings[FPSTR(FP_CONFIG_HOST)], sizeof(mqttServer)); } if(!settings[FPSTR(FP_CONFIG_USER)].isNull()) { - changed |= mqttUser != settings[FPSTR(FP_CONFIG_USER)].as(); - mqttUser = settings[FPSTR(FP_CONFIG_USER)].as(); + changed |= strcmp(mqttUser, settings[FPSTR(FP_CONFIG_USER)]) != 0; + strncpy(mqttUser, settings[FPSTR(FP_CONFIG_USER)], sizeof(mqttUser)); } if(!settings[FPSTR(FP_CONFIG_PASS)].isNull() && settings[FPSTR(FP_CONFIG_PASS)].as() != String(FPSTR(D_PASSWORD_MASK))) { - changed |= mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as(); - mqttPassword = settings[FPSTR(FP_CONFIG_PASS)].as(); + changed |= strcmp(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)]) != 0; + strncpy(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)], sizeof(mqttPassword)); } snprintf_P(mqttNodeTopic, sizeof(mqttNodeTopic), PSTR(MQTT_PREFIX "/%s/"), haspDevice.get_hostname()); - snprintf_P(mqttGroupTopic, sizeof(mqttGroupTopic), PSTR(MQTT_PREFIX "/%s/"), mqttGroupName.c_str()); + snprintf_P(mqttGroupTopic, sizeof(mqttGroupTopic), PSTR(MQTT_PREFIX "/%s/"), mqttGroupName); return changed; } diff --git a/src/sys/gpio/hasp_gpio.cpp b/src/sys/gpio/hasp_gpio.cpp index 738bed7d..921a3e60 100644 --- a/src/sys/gpio/hasp_gpio.cpp +++ b/src/sys/gpio/hasp_gpio.cpp @@ -1,10 +1,10 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "AceButton.h" #include "lv_conf.h" // For timing defines #include "hasplib.h" + #include "hasp_gpio.h" #include "hasp_config.h" @@ -12,42 +12,78 @@ #define INPUT_PULLDOWN INPUT #endif -uint8_t gpioUsedInputCount = 0; +#ifdef ARDUINO +#include "AceButton.h" using namespace ace_button; -static AceButton* button[HASP_NUM_INPUTS]; +ButtonConfig buttonConfig; // Clicks, double-clicks and long presses +ButtonConfig switchConfig; // Clicks only +#else + +#define HIGH 1 +#define LOW 0 +#define NUM_DIGITAL_PINS 40 +#define digitalWrite(x, y) +#define analogWrite(x, y) + +#endif // ARDUINO + +#define SCALE_8BIT_TO_12BIT(x) x << 4 | x >> 4 +#define SCALE_8BIT_TO_10BIT(x) x << 2 | x >> 6 // An array of button pins, led pins, and the led states. Cannot be const // because ledState is mutable. hasp_gpio_config_t gpioConfig[HASP_NUM_GPIO_CONFIG] = { // {2, 8, INPUT, LOW}, {3, 9, OUTPUT, LOW}, {4, 10, INPUT, HIGH}, {5, 11, OUTPUT, LOW}, {6, 12, INPUT, LOW}, }; +uint8_t pwm_channel = 1; // Backlight has 0 + +static inline void gpio_update_group(uint8_t group, lv_obj_t* obj, bool power, int32_t val, int32_t min, int32_t max) +{ + hasp_update_value_t value = {.obj = obj, .group = group, .min = min, .max = max, .val = val, .power = power}; + dispatch_normalized_group_values(value); +} #if defined(ARDUINO_ARCH_ESP32) #include "driver/uart.h" +#include -class TouchConfig : public ButtonConfig { - public: - TouchConfig(); +volatile bool touchdetected = false; +RTC_DATA_ATTR int rtcRecordCounter = 0; + +void gotTouch() +{ + touchdetected = true; +} + +// Overrides the readButton function on ESP32 +class CapacitiveConfig : public ButtonConfig { protected: // Number of iterations to sample the capacitive switch. Higher number // provides better smoothing but increases the time taken for a single read. - static const uint8_t kSamples = 10; + // static const uint8_t kSamples = 10; // The threshold value which is considered to be a "touch" on the switch. - static const long kTouchThreshold = 70; + static const long kTouchThreshold = 32; int readButton(uint8_t pin) override { - // long total = mSensor.capacitiveSensor(kSamples); - return (touchRead(pin) > kTouchThreshold) ? LOW : HIGH; + return touchdetected ? HIGH : LOW; // HIGH = not touched } }; - -TouchConfig touchConfig(); +CapacitiveConfig touchConfig; // Capacitive touch #endif +void gpio_log_serial_dimmer(const char* command) +{ + char buffer[32]; + snprintf_P(buffer, sizeof(buffer), PSTR("Dimmer => %02x %02x %02x %02x"), command[0], command[1], command[2], + command[3]); + LOG_VERBOSE(TAG_GPIO, buffer); +} + +#ifdef ARDUINO static void gpio_event_handler(AceButton* button, uint8_t eventType, uint8_t buttonState) { uint8_t btnid = button->getId(); @@ -55,12 +91,13 @@ static void gpio_event_handler(AceButton* button, uint8_t eventType, uint8_t but bool state = false; switch(eventType) { case AceButton::kEventPressed: - if(gpioConfig[btnid].type == HASP_GPIO_SWITCH || gpioConfig[btnid].type == HASP_GPIO_SWITCH_INVERTED) { + if(gpioConfig[btnid].type != hasp_gpio_type_t::BUTTON) { eventid = HASP_EVENT_ON; } else { eventid = HASP_EVENT_DOWN; } state = true; + // touchdetected = false; break; case 2: // AceButton::kEventClicked: eventid = HASP_EVENT_UP; @@ -77,7 +114,7 @@ static void gpio_event_handler(AceButton* button, uint8_t eventType, uint8_t but // state = true; // do not repeat DOWN + LONG + HOLD // break; case AceButton::kEventReleased: - if(gpioConfig[btnid].type == HASP_GPIO_SWITCH || gpioConfig[btnid].type == HASP_GPIO_SWITCH_INVERTED) { + if(gpioConfig[btnid].type != hasp_gpio_type_t::BUTTON) { eventid = HASP_EVENT_OFF; } else { eventid = HASP_EVENT_RELEASE; @@ -87,381 +124,508 @@ static void gpio_event_handler(AceButton* button, uint8_t eventType, uint8_t but eventid = HASP_EVENT_LOST; } - event_gpio_input(gpioConfig[btnid].pin, gpioConfig[btnid].group, eventid); - if(eventid != HASP_EVENT_LONG) // do not repeat DOWN + LONG - dispatch_normalized_group_value(gpioConfig[btnid].group, NULL, state, HASP_EVENT_OFF, HASP_EVENT_ON); + event_gpio_input(gpioConfig[btnid].pin, eventid); + + // update objects and gpios in this group + if(gpioConfig[btnid].group && eventid != HASP_EVENT_LONG) // do not repeat DOWN + LONG + gpio_update_group(gpioConfig[btnid].group, NULL, gpioConfig[btnid].power, state, HASP_EVENT_OFF, HASP_EVENT_ON); } /* ********************************* GPIO Setup *************************************** */ -void gpio_log_serial_dimmer(const char* command) -{ - char buffer[32]; - snprintf_P(buffer, sizeof(buffer), PSTR("Dimmer: %02x %02x %02x %02x"), command[0], command[1], command[2], - command[3]); - LOG_VERBOSE(TAG_GPIO, buffer); -} - void aceButtonSetup(void) { - ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig(); - buttonConfig->setEventHandler(gpio_event_handler); - - // Features - // buttonConfig->setFeature(ButtonConfig::kFeatureClick); - // buttonConfig->setFeature(ButtonConfig::kFeatureLongPress); - // buttonConfig->setFeature(ButtonConfig::kFeatureRepeatPress); - // buttonConfig->setFeature(ButtonConfig::kFeatureDoubleClick); - // buttonConfig->setFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); - + // Button Features + buttonConfig.setEventHandler(gpio_event_handler); + buttonConfig.setFeature(ButtonConfig::kFeatureClick); + buttonConfig.clearFeature(ButtonConfig::kFeatureDoubleClick); + buttonConfig.setFeature(ButtonConfig::kFeatureLongPress); + // buttonConfig.clearFeature(ButtonConfig::kFeatureRepeatPress); + buttonConfig.clearFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); // Causes annoying pauses + buttonConfig.setFeature(ButtonConfig::kFeatureSuppressAfterClick); // Delays - buttonConfig->setClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); - buttonConfig->setDoubleClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); - buttonConfig->setLongPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); - buttonConfig->setRepeatPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); - buttonConfig->setRepeatPressInterval(LV_INDEV_DEF_LONG_PRESS_REP_TIME); + buttonConfig.setClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + buttonConfig.setDoubleClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + buttonConfig.setLongPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + buttonConfig.setRepeatPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + buttonConfig.setRepeatPressInterval(LV_INDEV_DEF_LONG_PRESS_REP_TIME); + + // Switch Features + switchConfig.setEventHandler(gpio_event_handler); + switchConfig.setFeature(ButtonConfig::kFeatureClick); + switchConfig.clearFeature(ButtonConfig::kFeatureLongPress); + switchConfig.clearFeature(ButtonConfig::kFeatureRepeatPress); + switchConfig.clearFeature(ButtonConfig::kFeatureDoubleClick); + switchConfig.setClickDelay(100); // decrease click delay from default 200 ms + +#if defined(ARDUINO_ARCH_ESP32) + // Capacitive Touch Features + touchConfig.setEventHandler(gpio_event_handler); + touchConfig.setFeature(ButtonConfig::kFeatureClick); + touchConfig.clearFeature(ButtonConfig::kFeatureDoubleClick); + touchConfig.setFeature(ButtonConfig::kFeatureLongPress); + // touchConfig.clearFeature(ButtonConfig::kFeatureRepeatPress); + touchConfig.clearFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); // Causes annoying pauses + touchConfig.setFeature(ButtonConfig::kFeatureSuppressAfterClick); + // Delays + touchConfig.setClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + touchConfig.setDoubleClickDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + touchConfig.setLongPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + touchConfig.setRepeatPressDelay(LV_INDEV_DEF_LONG_PRESS_TIME); + touchConfig.setRepeatPressInterval(LV_INDEV_DEF_LONG_PRESS_REP_TIME); +#endif } -void gpioLoop(void) +// Can be called ad-hoc to change a setup +static void gpio_setup_pin(uint8_t index) { - // Should be called every 4-5ms or faster, for the default debouncing time of ~20ms. - for(uint8_t i = 0; i < gpioUsedInputCount; i++) { - if(button[i]) button[i]->check(); + hasp_gpio_config_t* gpio = &gpioConfig[index]; + + if(gpioIsSystemPin(gpio->pin)) { + LOG_WARNING(TAG_GPIO, F("Invalid pin %d"), gpio->pin); + return; } -} -void gpioAddButton(uint8_t pin, uint8_t input_mode, uint8_t default_state, uint8_t index) -{ - uint8_t i; - for(i = 0; i < HASP_NUM_INPUTS; i++) { + uint8_t input_mode; + bool default_state = gpio->inverted ? LOW : HIGH; // default pullup + switch(gpio->gpio_function) { + case hasp_gpio_function_t::OUTPUT_PIN: + input_mode = OUTPUT; + break; + case hasp_gpio_function_t::EXTERNAL_PULLDOWN: + default_state = !default_state; // not pullup + case hasp_gpio_function_t::EXTERNAL_PULLUP: + input_mode = INPUT; + break; +#ifndef ARDUINO_ARCH_ESP8266 + case hasp_gpio_function_t::INTERNAL_PULLDOWN: + default_state = !default_state; // not pullup + input_mode = INPUT_PULLDOWN; + break; +#endif + case hasp_gpio_function_t::INTERNAL_PULLUP: + default: + input_mode = INPUT_PULLUP; + } - if(!button[i]) { - LOG_TRACE(TAG_GPIO, F("Creating Button%d on pin %d (index %d) mode %d default %d"), i, pin, index, - input_mode, default_state); + gpio->power = 1; // on by default, value is set to 0 + gpio->max = 255; + switch(gpio->type) { + case hasp_gpio_type_t::SWITCH: + case hasp_gpio_type_t::BATTERY... hasp_gpio_type_t::WINDOW: + if(gpio->btn) delete gpio->btn; + gpio->btn = new AceButton(&switchConfig, gpio->pin, default_state, index); + pinMode(gpio->pin, INPUT_PULLUP); + gpio->max = 0; + break; + case hasp_gpio_type_t::BUTTON: + if(gpio->btn) delete gpio->btn; + gpio->btn = new AceButton(&buttonConfig, gpio->pin, default_state, index); + pinMode(gpio->pin, INPUT_PULLUP); + gpio->max = 0; + break; +#if defined(ARDUINO_ARCH_ESP32) + case hasp_gpio_type_t::TOUCH: + if(gpio->btn) delete gpio->btn; + gpio->btn = new AceButton(&touchConfig, gpio->pin, HIGH, index); + gpio->max = 0; + // touchAttachInterrupt(gpio->pin, gotTouch, 33); + break; +#endif - button[i] = new AceButton(pin, default_state, index); + case hasp_gpio_type_t::POWER_RELAY: + case hasp_gpio_type_t::LIGHT_RELAY: + pinMode(gpio->pin, OUTPUT); + gpio->max = 1; // on-off + break; - if(button[i]) { - // pinMode(pin, input_mode); - - ButtonConfig* buttonConfig = button[i]->getButtonConfig(); - buttonConfig->setEventHandler(gpio_event_handler); - buttonConfig->setFeature(ButtonConfig::kFeatureClick); - buttonConfig->clearFeature(ButtonConfig::kFeatureDoubleClick); - buttonConfig->setFeature(ButtonConfig::kFeatureLongPress); - // buttonConfig->clearFeature(ButtonConfig::kFeatureRepeatPress); - buttonConfig->clearFeature( - ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); // Causes annoying pauses - buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterClick); - - LOG_INFO(TAG_GPIO, F("Button%d created on pin %d (index %d) mode %d default %d"), i, pin, index, - input_mode, default_state); - gpioUsedInputCount = i + 1; - return; + case hasp_gpio_type_t::PWM: + gpio->max = 4095; + case hasp_gpio_type_t::LED... hasp_gpio_type_t::LED_W: + // case hasp_gpio_type_t::BACKLIGHT: + pinMode(gpio->pin, OUTPUT); +#if defined(ARDUINO_ARCH_ESP32) + if(pwm_channel < 16) { + // configure LED PWM functionalitites + ledcSetup(pwm_channel, 20000, 12); + // attach the channel to the GPIO to be controlled + ledcAttachPin(gpio->pin, pwm_channel); + gpio->channel = pwm_channel++; + } else { + LOG_ERROR(TAG_GPIO, F("Too many PWM channels defined")); } +#endif + break; + + case hasp_gpio_type_t::DAC: +#if defined(ARDUINO_ARCH_ESP32) + gpio_num_t pin; + if(dac_pad_get_io_num(DAC_CHANNEL_1, &pin) == ESP_OK) + if(gpio->pin == pin) dac_output_enable(DAC_CHANNEL_1); + if(dac_pad_get_io_num(DAC_CHANNEL_2, &pin) == ESP_OK) + if(gpio->pin == pin) dac_output_enable(DAC_CHANNEL_2); +#endif + break; + + case hasp_gpio_type_t::SERIAL_DIMMER: + case hasp_gpio_type_t::SERIAL_DIMMER_AU: + case hasp_gpio_type_t::SERIAL_DIMMER_EU: { + const char command[9] = "\xEF\x01\x4D\xA3"; // Start Lanbon Dimmer +#if defined(ARDUINO_ARCH_ESP32) + Serial1.begin(115200UL, SERIAL_8N1, UART_PIN_NO_CHANGE, gpio->pin, + gpio->type == hasp_gpio_type_t::SERIAL_DIMMER_EU); // true = EU, false = AU + Serial1.flush(); + delay(20); + Serial1.print(" "); + delay(20); + Serial1.write((const uint8_t*)command, 8); +#endif + gpio_log_serial_dimmer(command); + break; } + + case hasp_gpio_type_t::FREE: + return; + + default: + LOG_WARNING(TAG_GPIO, F("Invalid config -> pin %d - type: %d"), gpio->pin, gpio->type); } - LOG_ERROR(TAG_GPIO, F("Failed to create Button%d pin %d (index %d). All %d slots available are in use!"), i, pin, - index, HASP_NUM_INPUTS); -} - -void gpioAddSwitch(uint8_t pin, uint8_t input_mode, uint8_t default_state, uint8_t index) -{ - uint8_t i; - for(i = 0; i < HASP_NUM_INPUTS; i++) { - - if(!button[i]) { - LOG_TRACE(TAG_GPIO, F("Creating Switch%d on pin %d (index %d) mode %d default %d"), i, pin, index, - input_mode, default_state); - - button[i] = new AceButton(pin, default_state, index); - - if(button[i]) { - // pinMode(pin, input_mode); - - ButtonConfig* buttonConfig = button[i]->getButtonConfig(); - buttonConfig->setEventHandler(gpio_event_handler); - buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAll); - - LOG_INFO(TAG_GPIO, F("Button%d switch on pin %d (index %d) mode %d default %d"), i, pin, index, - input_mode, default_state); - gpioUsedInputCount = i + 1; - return; - } - } - } - LOG_ERROR(TAG_GPIO, F("Failed to create Button%d pin %d (index %d). All %d slots available are in use!"), i, pin, - index, HASP_NUM_INPUTS); -} - -void gpioAddTouchButton(uint8_t pin, uint8_t input_mode, uint8_t default_state, uint8_t index) -{ - uint8_t i; - for(i = 0; i < HASP_NUM_INPUTS; i++) { - - if(!button[i]) { - button[i] = new AceButton(pin, default_state, index); - - if(button[i]) { - pinMode(pin, input_mode); - - ButtonConfig* buttonConfig = button[i]->getButtonConfig(); - buttonConfig->setEventHandler(gpio_event_handler); - buttonConfig->setFeature(ButtonConfig::kFeatureClick); - buttonConfig->clearFeature(ButtonConfig::kFeatureDoubleClick); - buttonConfig->setFeature(ButtonConfig::kFeatureLongPress); - buttonConfig->clearFeature(ButtonConfig::kFeatureRepeatPress); - buttonConfig->clearFeature( - ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); // Causes annoying pauses - - LOG_INFO(TAG_GPIO, F("Button%d created on pin %d (index %d) mode %d default %d"), i, pin, index, - input_mode, default_state); - gpioUsedInputCount = i + 1; - return; - } - } - } - LOG_ERROR(TAG_GPIO, F("Failed to create Button%d pin %d (index %d). All %d slots available are in use!"), i, pin, - index, HASP_NUM_INPUTS); + LOG_VERBOSE(TAG_GPIO, F(D_BULLET "Configured pin %d"), gpio->pin); } void gpioSetup() { + LOG_INFO(TAG_GPIO, F(D_SERVICE_STARTING)); +#if defined(ARDUINO_ARCH_ESP32) + LOG_WARNING(TAG_GPIO, F("Reboot counter %d"), rtcRecordCounter++); +#endif + aceButtonSetup(); for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { - uint8_t input_mode; - switch(gpioConfig[i].gpio_function) { - case OUTPUT: - input_mode = OUTPUT; - break; - case INPUT: - input_mode = INPUT; - break; -#ifndef ARDUINO_ARCH_ESP8266 - case INPUT_PULLDOWN: - input_mode = INPUT_PULLDOWN; - break; -#endif - default: - input_mode = INPUT_PULLUP; - } - - switch(gpioConfig[i].type) { - case HASP_GPIO_SWITCH: - gpioAddSwitch(gpioConfig[i].pin, input_mode, HIGH, i); - pinMode(gpioConfig[i].pin, INPUT_PULLUP); - break; - case HASP_GPIO_BUTTON: - gpioAddButton(gpioConfig[i].pin, input_mode, HIGH, i); - pinMode(gpioConfig[i].pin, INPUT_PULLUP); - break; - case HASP_GPIO_SWITCH_INVERTED: - gpioAddSwitch(gpioConfig[i].pin, input_mode, LOW, i); - pinMode(gpioConfig[i].pin, INPUT_PULLDOWN); - break; - case HASP_GPIO_BUTTON_INVERTED: - gpioAddButton(gpioConfig[i].pin, input_mode, LOW, i); - pinMode(gpioConfig[i].pin, INPUT_PULLDOWN); - break; - - case HASP_GPIO_RELAY: - case HASP_GPIO_RELAY_INVERTED: - pinMode(gpioConfig[i].pin, OUTPUT); - break; - - case HASP_GPIO_LED ... HASP_GPIO_LED_CW_INVERTED: - // case HASP_GPIO_LED_INVERTED: - case HASP_GPIO_PWM: - case HASP_GPIO_PWM_INVERTED: - // case HASP_GPIO_BACKLIGHT: - pinMode(gpioConfig[i].pin, OUTPUT); -#if defined(ARDUINO_ARCH_ESP32) - // configure LED PWM functionalitites - ledcSetup(gpioConfig[i].group, 20000, 12); - // attach the channel to the GPIO to be controlled - ledcAttachPin(gpioConfig[i].pin, gpioConfig[i].group); -#endif - break; - - case HASP_GPIO_SERIAL_DIMMER: -#if defined(ARDUINO_ARCH_ESP32) - Serial2.begin(115200, SERIAL_8N1, UART_PIN_NO_CHANGE, gpioConfig[i].pin); - delay(20); - const char command[5] = "\xEF\x01\x4D\xA3"; // Start Lanbon Dimmer - Serial2.print(command); - gpio_log_serial_dimmer(command); -#endif - break; - } + gpio_setup_pin(i); } + + LOG_INFO(TAG_GPIO, F(D_SERVICE_STARTED)); +} + +IRAM_ATTR void gpioLoop(void) +{ + // Should be called every 4-5ms or faster, for the default debouncing time of ~20ms. + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + if(gpioConfig[i].btn) gpioConfig[i].btn->check(); + } +} + +#else + +void gpioSetup(void) +{ + gpioSavePinConfig(0, 3, hasp_gpio_type_t::POWER_RELAY, 0, -1, false); + gpioSavePinConfig(1, 4, hasp_gpio_type_t::LIGHT_RELAY, 0, -1, false); + gpioSavePinConfig(2, 13, hasp_gpio_type_t::LED, 0, -1, false); + gpioSavePinConfig(3, 14, hasp_gpio_type_t::DAC, 0, -1, false); +} +IRAM_ATTR void gpioLoop(void) +{} + +#endif // ARDUINO + +static inline bool gpio_is_input(hasp_gpio_config_t* gpio) +{ + return (gpio->type != hasp_gpio_type_t::USER) && (gpio->type >= 0x80); +} + +static inline bool gpio_is_output(hasp_gpio_config_t* gpio) +{ + return (gpio->type > hasp_gpio_type_t::USED) && (gpio->type < 0x80); } /* ********************************* State Setters *************************************** */ -void gpio_get_value(hasp_gpio_config_t gpio) +bool gpio_get_pin_state(uint8_t pin, bool& power, int32_t& val) +{ + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + if(gpioConfig[i].pin == pin && gpio_is_output(&gpioConfig[i])) { + power = gpioConfig[i].power; + val = gpioConfig[i].val; + return true; + } + } + return false; +} + +void gpio_output_state(hasp_gpio_config_t* gpio) { char payload[32]; char topic[12]; - snprintf_P(topic, sizeof(topic), PSTR("gpio%d"), gpio.pin); - snprintf_P(payload, sizeof(payload), PSTR("%d"), gpio.val); + snprintf_P(topic, sizeof(topic), PSTR("output%d"), gpio->pin); + snprintf_P(payload, sizeof(payload), PSTR("{\"state\":%d,\"val\":%d}"), gpio->power, gpio->val); dispatch_state_subtopic(topic, payload); } -void gpio_get_value(uint8_t pin) +bool gpio_output_pin_state(uint8_t pin) { for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { - if(gpioConfig[i].pin == pin) return gpio_get_value(gpioConfig[i]); - } - LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d is not configured"), pin); -} - -void gpio_set_value(hasp_gpio_config_t gpio, int16_t val) -{ - bool inverted = false; - - switch(gpio.type) { - case HASP_GPIO_RELAY_INVERTED: - inverted = true; - case HASP_GPIO_RELAY: - gpio.val = val > 0 ? HIGH : LOW; - digitalWrite(gpio.pin, inverted ? gpio.val : !gpio.val); - break; - - case HASP_GPIO_LED_INVERTED: - case HASP_GPIO_LED_R_INVERTED: - case HASP_GPIO_LED_G_INVERTED: - case HASP_GPIO_LED_B_INVERTED: - inverted = true; - case HASP_GPIO_LED: - case HASP_GPIO_LED_R: - case HASP_GPIO_LED_G: - case HASP_GPIO_LED_B: - gpio.val = val >= 255 ? 255 : val > 0 ? val : 0; -#if defined(ARDUINO_ARCH_ESP32) - ledcWrite(gpio.group, gpio.val); // ledChannel and value -#else - analogWrite(gpio.pin, gpio.val); // 1023 -#endif - break; - - case HASP_GPIO_PWM_INVERTED: - inverted = true; - case HASP_GPIO_PWM: - gpio.val = val >= 4095 ? 4095 : val > 0 ? val : 0; -#if defined(ARDUINO_ARCH_ESP32) - ledcWrite(gpio.group, inverted ? 4095 - gpio.val : gpio.val); // ledChannel and value -#else - analogWrite(gpio.pin, (inverted ? 4095 - gpio.val : gpio.val) >> 2); // 1023 -#endif - break; - - case HASP_GPIO_SERIAL_DIMMER: { - gpio.val = val >= 100 ? 100 : val > 0 ? val : 0; -#if defined(ARDUINO_ARCH_ESP32) - char command[5] = "\xEF\x02\x00\xED"; - if(gpio.val == 0) { - command[2] = 0x20; - } else { - command[2] = (uint8_t)gpio.val; - command[3] ^= command[2]; - } - Serial2.print(command); - gpio_log_serial_dimmer(command); - -#endif - break; - } - - default: - return; - } - gpio_get_value(gpio); - LOG_VERBOSE(TAG_GPIO, F("Group %d - Pin %d = %d"), gpio.group, gpio.pin, gpio.val); -} - -void gpio_set_value(uint8_t pin, int16_t val) -{ - for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { - if(gpioConfig[i].pin == pin) return gpio_set_value(gpioConfig[i], val); - } - LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d is not configured"), pin); -} - -void gpio_set_normalized_value(hasp_gpio_config_t gpio, int16_t val, int16_t min, int16_t max) -{ - if(min == max) { - LOG_ERROR(TAG_GPIO, F("Invalid value range")); - return; - } - - int16_t newval; - switch(gpio.type) { - case HASP_GPIO_RELAY: - case HASP_GPIO_RELAY_INVERTED: - newval = val > min ? HIGH : LOW; - break; - - case HASP_GPIO_LED: - case HASP_GPIO_LED_R: - case HASP_GPIO_LED_G: - case HASP_GPIO_LED_B: - case HASP_GPIO_LED_INVERTED: - case HASP_GPIO_LED_R_INVERTED: - case HASP_GPIO_LED_G_INVERTED: - case HASP_GPIO_LED_B_INVERTED: - newval = map(val, min, max, 0, 255); - break; - - case HASP_GPIO_PWM: - case HASP_GPIO_PWM_INVERTED: - newval = map(val, min, max, 0, 4095); - break; - - default: - return; - } - - gpio_set_value(gpio, newval); -} - -void gpio_set_normalized_group_value(uint8_t groupid, int16_t val, int16_t min, int16_t max) -{ - if(min == max) { - LOG_ERROR(TAG_GPIO, F("Invalid value range")); - return; - } - - // bool state = Parser::get_event_state(eventid); - for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { - if(gpioConfig[i].group == groupid) { - gpio_set_normalized_value(gpioConfig[i], val, min, max); + if(gpioConfig[i].pin == pin && gpioConfigInUse(i)) { + gpio_output_state(&gpioConfig[i]); + return true; } } + return false; } -void gpio_set_moodlight(uint8_t r, uint8_t g, uint8_t b) +static inline int32_t gpio_limit(int32_t val, int32_t min, int32_t max) { - // uint16_t max_level = power == 0 ? 0 : map(brightness, 0, 0xFF, 0, 0xFFFFU); - uint16_t max_level = 0xFFFFU; + if(val >= max) return max; + if(val <= min) return min; + return val; +} + +// val is assumed to be 12 bits +static inline bool gpio_set_analog_value(hasp_gpio_config_t* gpio) +{ + uint16_t val = 0; +#if defined(ARDUINO_ARCH_ESP32) + + if(gpio->max == 255) + val = SCALE_8BIT_TO_12BIT(gpio->val); + else if(gpio->max == 4095) + val = gpio->val; + + if(!gpio->power) val = 0; + if(gpio->inverted) val = 4095 - val; + + ledcWrite(gpio->channel, val); // 12 bits + return true; // sent + +#elif defined(ARDUINO_ARCH_ESP8266) + + if(gpio->max == 255) + val = SCALE_8BIT_TO_10BIT(gpio->val); + else if(gpio->max == 4095) + val = gpio->val >> 2; + + if(!gpio->power) val = 0; + if(gpio->inverted) val = 1023 - val; + + analogWrite(gpio->pin, val); // 10 bits + return true; // sent + +#else + return false; // not implemented +#endif +} + +static inline bool gpio_set_serial_dimmer(hasp_gpio_config_t* gpio) +{ + uint16_t val = gpio_limit(gpio->val, 0, 255); + + if(!gpio->power) val = 0; + if(gpio->inverted) val = 255 - val; + + char command[5] = "\xEF\x02\x00\xED"; + command[2] = (uint8_t)map(val, 0, 255, 0, 100); + command[3] ^= command[2]; + +#if defined(ARDUINO_ARCH_ESP32) + Serial1.write((const uint8_t*)command, 4); + gpio_log_serial_dimmer(command); + return true; // sent +#else + gpio_log_serial_dimmer(command); + return false; // not sent +#endif +} + +static inline bool gpio_set_dac_value(hasp_gpio_config_t* gpio) +{ +#ifdef ARDUINO_ARCH_ESP32 + uint16_t val = gpio_limit(gpio->val, 0, 255); + gpio_num_t pin; + + if(!gpio->power) val = 0; + if(gpio->inverted) val = 255 - val; + + if(dac_pad_get_io_num(DAC_CHANNEL_1, &pin) == ESP_OK && gpio->pin == pin) + dac_output_voltage(DAC_CHANNEL_1, gpio->val); + else if(dac_pad_get_io_num(DAC_CHANNEL_2, &pin) == ESP_OK && gpio->pin == pin) + dac_output_voltage(DAC_CHANNEL_2, gpio->val); + else + return false; // not found + return true; // found +#else + return false; // not implemented +#endif +} + +bool gpio_get_pin_config(uint8_t pin, hasp_gpio_config_t** gpio) +{ + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + if(gpioConfig[i].pin == pin) { + *gpio = &gpioConfig[i]; + return true; + } + } + return false; +} + +// Update the actual value of one pin, does NOT update group members +// The value must be normalized first +static bool gpio_set_output_value(hasp_gpio_config_t* gpio, bool power, uint16_t val) +{ + // if val is 0, then set power to 0 + gpio->power = val == 0 ? 0 : power; + + // Only update the current value if power set to 1, otherwise retain previous value + if(power) gpio->val = gpio_limit(val, 0, gpio->max); + + switch(gpio->type) { + case hasp_gpio_type_t::POWER_RELAY: + case hasp_gpio_type_t::LIGHT_RELAY: + digitalWrite(gpio->pin, power ? (gpio->inverted ? !gpio->val : gpio->val) : 0); + return true; + + case hasp_gpio_type_t::LED... hasp_gpio_type_t::LED_W: + case hasp_gpio_type_t::PWM: + return gpio_set_analog_value(gpio); + + case hasp_gpio_type_t::DAC: + return gpio_set_dac_value(gpio); + + case hasp_gpio_type_t::SERIAL_DIMMER: + case hasp_gpio_type_t::SERIAL_DIMMER_AU: + case hasp_gpio_type_t::SERIAL_DIMMER_EU: + return gpio_set_serial_dimmer(gpio); + + default: + LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d is not a valid output"), gpio->pin); + return false; // not a valid output + } +} + +// Update the normalized value of one pin +static void gpio_set_normalized_value(hasp_gpio_config_t* gpio, hasp_update_value_t& value) +{ + int32_t val = value.val; + + if(value.min != 0 || value.max != gpio->max) { // do we need to recalculate? + if(value.min == value.max) { + LOG_ERROR(TAG_GPIO, F("Invalid value range")); + return; + } + + switch(gpio->type) { + case hasp_gpio_type_t::POWER_RELAY: + case hasp_gpio_type_t::LIGHT_RELAY: + val = val > value.min ? HIGH : LOW; + break; + + case hasp_gpio_type_t::LED... hasp_gpio_type_t::LED_W: + case hasp_gpio_type_t::DAC: + case hasp_gpio_type_t::PWM: + case hasp_gpio_type_t::SERIAL_DIMMER: + case hasp_gpio_type_t::SERIAL_DIMMER_AU: + case hasp_gpio_type_t::SERIAL_DIMMER_EU: + val = map(val, value.min, value.max, 0, gpio->max); + break; + + default: + return; // invalid output type + } + } + + gpio_set_output_value(gpio, value.power, val); // recalculated +} + +// Dispatch all group member values +void gpio_output_group_values(uint8_t group) +{ + for(uint8_t k = 0; k < HASP_NUM_GPIO_CONFIG; k++) { + hasp_gpio_config_t* gpio = &gpioConfig[k]; + if(gpio->group == group && gpio_is_output(gpio)) // group members that are outputs + gpio_output_state(&gpioConfig[k]); + } +} + +// SHOULD only by called from DISPATCH +// Update the normalized value of all group members +// Does not procude logging output +void gpio_set_normalized_group_values(hasp_update_value_t& value) +{ + // Set all pins first, minimizes delays + for(uint8_t k = 0; k < HASP_NUM_GPIO_CONFIG; k++) { + hasp_gpio_config_t* gpio = &gpioConfig[k]; + if(gpio->group == value.group && gpioConfigInUse(k)) // group members that are outputs + gpio_set_normalized_value(gpio, value); + } + + // Log the changed output values + // gpio_output_group_values(value.group); + + // object_set_normalized_group_values(group, NULL, val, min, max); // Update onsreen objects +} + +// Update the value of an output pin and its group members +bool gpio_set_pin_state(uint8_t pin, bool power, int32_t val) +{ + hasp_gpio_config_t* gpio = NULL; + + if(!gpio_get_pin_config(pin, &gpio) || !gpio) { + LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d is not configured"), pin); + return false; + } + + if(!gpio_is_output(gpio)) { + LOG_WARNING(TAG_GPIO, F(D_BULLET "Pin %d can not be set"), pin); + return false; + } + + if(gpio->group) { + // update objects and gpios in this group + gpio->power = power; + gpio->val = gpio_limit(val, 0, gpio->max); + gpio_update_group(gpio->group, NULL, gpio->power, gpio->val, 0, gpio->max); + + } else { + // update this gpio value only + if(gpio_set_output_value(gpio, power, val)) { + gpio_output_state(gpio); + LOG_VERBOSE(TAG_GPIO, F("No Group - Pin %d = %d"), gpio->pin, gpio->val); + } else { + return false; + } + } + + return true; // pin found and set +} + +// Updates the RGB pins directly, rgb are already normalized values +void gpio_set_moodlight(moodlight_t& moodlight) +{ + // RGBXX https://stackoverflow.com/questions/39949331/how-to-calculate-rgbaw-amber-white-from-rgb-for-leds + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + switch(gpioConfig[i].type) { + case hasp_gpio_type_t::LED_R... hasp_gpio_type_t::LED_W: + uint8_t index = (gpioConfig[i].type - hasp_gpio_type_t::LED_R); + if(index > 4) continue; + + uint8_t val = (moodlight.rgbww[index] * moodlight.brightness + 127) / 255; + gpio_set_output_value(&gpioConfig[i], moodlight.power, val); + break; + } + } for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { switch(gpioConfig[i].type) { - case HASP_GPIO_LED_R: - case HASP_GPIO_LED_R_INVERTED: - gpio_set_normalized_value(gpioConfig[i], r, 0, 0xFF); - break; - case HASP_GPIO_LED_G: - case HASP_GPIO_LED_G_INVERTED: - gpio_set_normalized_value(gpioConfig[i], g, 0, 0xFF); - break; - case HASP_GPIO_LED_B: - case HASP_GPIO_LED_B_INVERTED: - gpio_set_normalized_value(gpioConfig[i], b, 0, 0xFF); + case hasp_gpio_type_t::LED_R... hasp_gpio_type_t::LED_W: + LOG_VERBOSE(TAG_GPIO, F(D_BULLET D_GPIO_PIN " %d => %d"), gpioConfig[i].pin, gpioConfig[i].val); break; } } + + // TODO: Update objects when the Mood Color Pin is in a group } bool gpioIsSystemPin(uint8_t gpio) @@ -575,10 +739,10 @@ bool gpioIsSystemPin(uint8_t gpio) return false; } -bool gpioInUse(uint8_t gpio) +bool gpioInUse(uint8_t pin) { for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { - if((gpioConfig[i].pin == gpio) && gpioConfigInUse(i)) { + if((gpioConfig[i].pin == pin) && gpioConfigInUse(i)) { return true; // pin matches and is in use } } @@ -586,7 +750,7 @@ bool gpioInUse(uint8_t gpio) return false; } -bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t group, uint8_t pinfunc) +bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t group, uint8_t pinfunc, bool inverted) { // TODO: Input validation @@ -599,6 +763,7 @@ bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t gr gpioConfig[config_num].type = type; gpioConfig[config_num].group = group; gpioConfig[config_num].gpio_function = pinfunc; + gpioConfig[config_num].inverted = inverted; LOG_TRACE(TAG_GPIO, F("Saving Pin config #%d pin %d - type %d - group %d - func %d"), config_num, pin, type, group, pinfunc); return true; @@ -610,7 +775,7 @@ bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t gr bool gpioConfigInUse(uint8_t num) { if(num >= HASP_NUM_GPIO_CONFIG) return false; - return gpioConfig[num].type != HASP_GPIO_FREE; + return gpioConfig[num].type != hasp_gpio_type_t::FREE; } int8_t gpioGetFreeConfigId() @@ -628,6 +793,116 @@ hasp_gpio_config_t gpioGetPinConfig(uint8_t num) return gpioConfig[num]; } +void gpio_discovery(JsonObject& input, JsonArray& relay, JsonArray& light, JsonArray& dimmer) +{ + char description[20]; + + for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { + switch(gpioConfig[i].type) { + case hasp_gpio_type_t::LIGHT_RELAY: + light.add(gpioConfig[i].pin); + break; + + case hasp_gpio_type_t::POWER_RELAY: + relay.add(gpioConfig[i].pin); + break; + + case hasp_gpio_type_t::DAC: + case hasp_gpio_type_t::LED: // Don't include the moodlight + case hasp_gpio_type_t::SERIAL_DIMMER: + case hasp_gpio_type_t::SERIAL_DIMMER_AU: + case hasp_gpio_type_t::SERIAL_DIMMER_EU: + dimmer.add(gpioConfig[i].pin); + break; + + case SWITCH: + strcpy_P(description, PSTR("none")); + break; + case BATTERY: + strcpy_P(description, PSTR("battery")); + break; + case BATTERY_CHARGING: + strcpy_P(description, PSTR("battery_charging")); + break; + case COLD: + strcpy_P(description, PSTR("cold")); + break; + case CONNECTIVITY: + strcpy_P(description, PSTR("connectivity")); + break; + case DOOR: + strcpy_P(description, PSTR("door")); + break; + case GARAGE_DOOR: + strcpy_P(description, PSTR("garage_door")); + break; + case GAS: + strcpy_P(description, PSTR("gas")); + break; + case HEAT: + strcpy_P(description, PSTR("heat")); + break; + case LIGHT: + strcpy_P(description, PSTR("light")); + break; + case LOCK: + strcpy_P(description, PSTR("lock")); + break; + case MOISTURE: + strcpy_P(description, PSTR("moisture")); + break; + case MOTION: + strcpy_P(description, PSTR("motion")); + break; + case MOVING: + strcpy_P(description, PSTR("moving")); + break; + case OCCUPANCY: + strcpy_P(description, PSTR("occupancy")); + break; + case OPENING: + strcpy_P(description, PSTR("opening")); + break; + case PLUG: + strcpy_P(description, PSTR("plug")); + break; + case POWER: + strcpy_P(description, PSTR("power")); + break; + case PRESENCE: + strcpy_P(description, PSTR("presence")); + break; + case PROBLEM: + strcpy_P(description, PSTR("problem")); + break; + case SAFETY: + strcpy_P(description, PSTR("safety")); + break; + case SMOKE: + strcpy_P(description, PSTR("smoke")); + break; + case SOUND: + strcpy_P(description, PSTR("sound")); + break; + case VIBRATION: + strcpy_P(description, PSTR("vibration")); + break; + case WINDOW: + strcpy_P(description, PSTR("window")); + break; + case hasp_gpio_type_t::FREE: + default: + break; + } + + if(gpioConfig[i].type >= hasp_gpio_type_t::SWITCH && gpioConfig[i].type <= hasp_gpio_type_t::WINDOW) { + JsonArray arr = input[description]; + if(arr.isNull()) arr = input.createNestedArray(description); + arr.add(gpioConfig[i].pin); + } + } +}; + //////////////////////////////////////////////////////////////////////////////////////////////////// #if HASP_USE_CONFIG > 0 bool gpioGetConfig(const JsonObject& settings) @@ -640,7 +915,7 @@ bool gpioGetConfig(const JsonObject& settings) for(JsonVariant v : array) { if(i < HASP_NUM_GPIO_CONFIG) { uint32_t cur_val = gpioConfig[i].pin | (gpioConfig[i].group << 8) | (gpioConfig[i].type << 16) | - (gpioConfig[i].gpio_function << 24); + (gpioConfig[i].gpio_function << 24) | (gpioConfig[i].inverted << 31); LOG_INFO(TAG_GPIO, F("GPIO CONF: %d: %d <=> %d"), i, cur_val, v.as()); if(cur_val != v.as()) changed = true; @@ -656,7 +931,7 @@ bool gpioGetConfig(const JsonObject& settings) array = settings[FPSTR(FP_GPIO_CONFIG)].to(); // Clear JsonArray for(uint8_t i = 0; i < HASP_NUM_GPIO_CONFIG; i++) { uint32_t cur_val = gpioConfig[i].pin | (gpioConfig[i].group << 8) | (gpioConfig[i].type << 16) | - (gpioConfig[i].gpio_function << 24); + (gpioConfig[i].gpio_function << 24) | (gpioConfig[i].inverted << 31); array.add(cur_val); } changed = true; @@ -689,13 +964,14 @@ bool gpioSetConfig(const JsonObject& settings) if(i < HASP_NUM_GPIO_CONFIG) { uint32_t cur_val = gpioConfig[i].pin | (gpioConfig[i].group << 8) | (gpioConfig[i].type << 16) | - (gpioConfig[i].gpio_function << 24); + (gpioConfig[i].gpio_function << 24) | (gpioConfig[i].inverted << 31); if(cur_val != new_val) status = true; gpioConfig[i].pin = new_val & 0xFF; gpioConfig[i].group = new_val >> 8 & 0xFF; gpioConfig[i].type = new_val >> 16 & 0xFF; - gpioConfig[i].gpio_function = new_val >> 24 & 0xFF; + gpioConfig[i].gpio_function = new_val >> 24 & 0x7F; + gpioConfig[i].inverted = new_val >> 31 & 0x1; } i++; } diff --git a/src/sys/gpio/hasp_gpio.h b/src/sys/gpio/hasp_gpio.h index 4e114da4..7de0bf98 100644 --- a/src/sys/gpio/hasp_gpio.h +++ b/src/sys/gpio/hasp_gpio.h @@ -4,7 +4,12 @@ #ifndef HASP_GPIO_H #define HASP_GPIO_H -#include "ArduinoJson.h" +#include "hasplib.h" + +#ifdef ARDUINO +#include "AceButton.h" +using namespace ace_button; +#endif #ifdef __cplusplus extern "C" { @@ -12,27 +17,38 @@ extern "C" { struct hasp_gpio_config_t { - uint8_t pin; // pin number - uint8_t group; // groupid - uint8_t type; // switch, button, ... - uint8_t gpio_function; // INPUT, OUTPUT, PULLUP, etc + uint8_t pin : 8; // pin number + uint8_t group : 8; // groupid + uint8_t gpio_function : 7; // INPUT, OUTPUT, PULLUP, etc + uint8_t inverted : 1; + uint8_t channel : 4; // pwmchannel + uint8_t power : 1; + uint8_t type; // switch, button, ... uint16_t val; + uint16_t max; +#ifdef ARDUINO + AceButton* btn; +#endif }; void gpioSetup(void); -void gpioLoop(void); +IRAM_ATTR void gpioLoop(void); void gpioEvery5Seconds(void); -// void gpio_set_group_onoff(uint8_t groupid, bool ison); -void gpio_set_normalized_group_value(uint8_t groupid, int16_t val, int16_t min, int16_t max); -// void gpio_set_gpio_state(uint8_t pin, uint16_t state); -void gpio_get_value(uint8_t pin); -void gpio_set_value(uint8_t pin, int16_t val); -void gpio_set_moodlight(uint8_t r, uint8_t g, uint8_t b); +void gpio_set_normalized_group_values(hasp_update_value_t& value); +void gpio_output_group_values(uint8_t group); -bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t group, uint8_t pinfunc); +bool gpio_output_pin_state(uint8_t pin); +bool gpio_get_pin_state(uint8_t pin, bool& power, int32_t& val); +bool gpio_set_pin_state(uint8_t pin, bool power, int32_t val); + +void gpio_set_moodlight(moodlight_t& moodlight); + +void gpio_discovery(JsonObject& input, JsonArray& relay, JsonArray& light, JsonArray& dimmer); + +bool gpioSavePinConfig(uint8_t config_num, uint8_t pin, uint8_t type, uint8_t group, uint8_t pinfunc, bool inverted); bool gpioIsSystemPin(uint8_t gpio); -bool gpioInUse(uint8_t gpio); +bool gpioInUse(uint8_t pin); bool gpioConfigInUse(uint8_t num); int8_t gpioGetFreeConfigId(); hasp_gpio_config_t gpioGetPinConfig(uint8_t num); @@ -42,48 +58,89 @@ bool gpioGetConfig(const JsonObject& settings); bool gpioSetConfig(const JsonObject& settings); #endif -#define HASP_GPIO_FREE 0x00 -#define HASP_GPIO_USED 0x01 -#define HASP_GPIO_SWITCH 0x02 // User Inputs -#define HASP_GPIO_SWITCH_INVERTED 0x03 -#define HASP_GPIO_BUTTON 0x04 -#define HASP_GPIO_BUTTON_INVERTED 0x05 -#define HASP_GPIO_TOUCH 0x06 -#define HASP_GPIO_TOUCH_INVERTED 0x07 -#define HASP_GPIO_COUNTER_RISE 0x10 // User Counters -#define HASP_GPIO_COUNTER_RISE_INVERTED 0x11 -#define HASP_GPIO_COUNTER_FALL 0x12 -#define HASP_GPIO_COUNTER_FALL_INVERTED 0x13 -#define HASP_GPIO_COUNTER_BOTH 0x14 -#define HASP_GPIO_COUNTER_BOTH_INVERTED 0x15 -#define HASP_GPIO_RELAY 0x20 // User Outputs -#define HASP_GPIO_RELAY_INVERTED 0x21 -#define HASP_GPIO_LED 0x22 -#define HASP_GPIO_LED_INVERTED 0x23 -#define HASP_GPIO_LED_R 0x24 -#define HASP_GPIO_LED_R_INVERTED 0x25 -#define HASP_GPIO_LED_G 0x26 -#define HASP_GPIO_LED_G_INVERTED 0x27 -#define HASP_GPIO_LED_B 0x28 -#define HASP_GPIO_LED_B_INVERTED 0x29 -#define HASP_GPIO_LED_W 0x2A -#define HASP_GPIO_LED_W_INVERTED 0x2B -#define HASP_GPIO_LED_WW 0x2C -#define HASP_GPIO_LED_WW_INVERTED 0x2D -#define HASP_GPIO_LED_CW 0x2E -#define HASP_GPIO_LED_CW_INVERTED 0x2F -#define HASP_GPIO_BUZZER 0x30 -#define HASP_GPIO_BUZZER_INVERTED 0x31 -#define HASP_GPIO_HAPTIC 0x32 -#define HASP_GPIO_HAPTIC_INVERTED 0x33 -#define HASP_GPIO_PWM 0x40 -#define HASP_GPIO_PWM_INVERTED 0x41 -#define HASP_GPIO_DAC 0x50 -#define HASP_GPIO_DAC_INVERTED 0x51 -#define HASP_GPIO_ADC 0x52 -#define HASP_GPIO_ADC_INVERTED 0x53 -#define HASP_GPIO_SERIAL_DIMMER 0x60 -#define HASP_GPIO_USER 0xFF +enum hasp_gpio_function_t { + OUTPUT_PIN = 1, + INTERNAL_PULLUP = 2, + INTERNAL_PULLDOWN = 3, + EXTERNAL_PULLUP = 4, + EXTERNAL_PULLDOWN = 5 +}; + +enum hasp_gpio_type_t { + FREE = 0x00, + USED = 0x01, + + /* Outputs */ + LED = 0x02, + LED_R = 0x03, + LED_G = 0x04, + LED_B = 0x05, + LED_CW = 0x06, + LED_WW = 0x07, + LED_W = 0x08, + LIGHT_RELAY = 0x0A, + POWER_RELAY = 0x0B, + SHUTTER_RELAY = 0x0C, + SHUTTER_OPEN = 0x1A, + SHUTTER_CLOSE = 0x1B, + BACKLIGHT = 0x20, + PWM = 0x21, + DAC = 0x22, + SERIAL_DIMMER = 0x30, + SERIAL_DIMMER_EU = 0x31, + SERIAL_DIMMER_AU = 0x32, + BUZZER = 0x40, + HAPTIC = 0x41, + + /* Inputs */ + SWITCH = 0xA0, // Binary Sensors + BATTERY = 0xA1, + BATTERY_CHARGING = 0xA2, + COLD = 0xA3, + CONNECTIVITY = 0xA4, + DOOR = 0xA5, + GARAGE_DOOR = 0xA6, + GAS = 0xA7, + HEAT = 0xA8, + LIGHT = 0xA9, + LOCK = 0xAA, + MOISTURE = 0xAB, + MOTION = 0xAC, + MOVING = 0xAD, + OCCUPANCY = 0xAE, + OPENING = 0xAF, + PLUG = 0xB0, + POWER = 0xB1, + PRESENCE = 0xB2, + PROBLEM = 0xB3, + SAFETY = 0xB4, + SMOKE = 0xB5, + SOUND = 0xB6, + VIBRATION = 0xB7, + WINDOW = 0xB8, + + AWNING = 0xB9, + BLIND = 0xBA, + CURTAIN = 0xBB, + DAMPER = 0xBC, + GATE = 0xBD, + SHADE = 0xBE, + SHUTTER = 0xBF, + + BUTTON = 0xF0, + BUTTON_TOGGLE_ON = 0xF1, + BUTTON_TOGGLE_OFF = 0xF2, + BUTTON_TOGGLE_BOTH = 0xF3, + TOUCH = 0xF4, + + ADC = 0xF9, + + COUNTER_RISE = 0xFA, // User Counters + COUNTER_FALL = 0xFB, + COUNTER_BOTH = 0xFC, + + USER = 0xFF +}; #ifdef __cplusplus } /* extern "C" */ diff --git a/src/sys/net/hasp_ethernet_esp32.cpp b/src/sys/net/hasp_ethernet_esp32.cpp index a0415e71..b9cb872b 100644 --- a/src/sys/net/hasp_ethernet_esp32.cpp +++ b/src/sys/net/hasp_ethernet_esp32.cpp @@ -25,13 +25,13 @@ void EthernetEvent(WiFiEvent_t event) eth_connected = true; break; case SYSTEM_EVENT_ETH_GOT_IP: - LOG_TRACE(TAG_ETH, F("MAC Address %s"), ETH.macAddress().c_str()); + LOG_TRACE(TAG_ETH, F(D_INFO_MAC_ADDRESS " %s"), ETH.macAddress().c_str()); ip = ETH.localIP(); LOG_TRACE(TAG_ETH, F("IPv4: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); if(ETH.fullDuplex()) { - LOG_TRACE(TAG_ETH, F("FULL_DUPLEX")); + LOG_TRACE(TAG_ETH, F(D_INFO_FULL_DUPLEX)); } - LOG_TRACE(TAG_ETH, F("LINK_SPEED %d Mbps"), ETH.linkSpeed()); + LOG_TRACE(TAG_ETH, F(D_INFO_LINK_SPEED " %d Mbps"), ETH.linkSpeed()); eth_connected = true; networkStart(); // Start network services break; @@ -55,7 +55,7 @@ void ethernetSetup() ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLKMODE); } -void ethernetLoop(void) +IRAM_ATTR void ethernetLoop(void) {} bool ethernetEvery5Seconds() @@ -70,4 +70,25 @@ void ethernet_get_statusupdate(char* buffer, size_t len) eth_connected ? F("on") : F("off"), ETH.linkSpeed(), ETH.localIP().toString().c_str()); } +void ethernet_get_info(JsonDocument& doc) +{ + char size_buf[32]; + String buffer((char*)0); + buffer.reserve(64); + + JsonObject info = doc.createNestedObject(F(D_INFO_ETHERNET)); + + buffer = ETH.linkSpeed(); + buffer += F(" Mbps"); + if(ETH.fullDuplex()) { + buffer += F(" " D_INFO_FULL_DUPLEX); + } + + info[F(D_INFO_LINK_SPEED)] = buffer; + info[F(D_INFO_IP_ADDRESS)] = ETH.localIP().toString(); + info[F(D_INFO_GATEWAY)] = ETH.gatewayIP().toString(); + info[F(D_INFO_DNS_SERVER)] = ETH.dnsIP().toString(); + info[F(D_INFO_MAC_ADDRESS)] = ETH.macAddress(); +} + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_ethernet_esp32.h b/src/sys/net/hasp_ethernet_esp32.h index 8a25a147..d8c62dab 100644 --- a/src/sys/net/hasp_ethernet_esp32.h +++ b/src/sys/net/hasp_ethernet_esp32.h @@ -4,13 +4,17 @@ #ifndef HASP_ETHERNET_ESP32_H #define HASP_ETHERNET_ESP32_H +#include "ArduinoJson.h" + static bool eth_connected = false; void ethernetSetup(); -void ethernetLoop(void); +IRAM_ATTR void ethernetLoop(void); bool ethernetEverySecond(); bool ethernetEvery5Seconds(); void ethernet_get_statusupdate(char* buffer, size_t len); +void ethernet_get_info(JsonDocument& doc); + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_ethernet_stm32.h b/src/sys/net/hasp_ethernet_stm32.h index f499069f..8e3d2b96 100644 --- a/src/sys/net/hasp_ethernet_stm32.h +++ b/src/sys/net/hasp_ethernet_stm32.h @@ -4,6 +4,8 @@ #ifndef HASP_ETHERNET_STM32_H #define HASP_ETHERNET_STM32_H +#include "ArduinoJson.h" + static bool eth_connected = false; void ethernetSetup(); @@ -13,4 +15,6 @@ bool ethernetEverySecond(); bool ethernetEvery5Seconds(); void ethernet_get_statusupdate(char* buffer, size_t len); +void ethernet_get_info(JsonDocument& doc); + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_network.cpp b/src/sys/net/hasp_network.cpp index bdbc49c0..17b2fa86 100644 --- a/src/sys/net/hasp_network.cpp +++ b/src/sys/net/hasp_network.cpp @@ -3,9 +3,7 @@ #include #include -// #ifdef USE_CONFIG_OVERRIDE -// #include "user_config_override.h" -// #endif + #include #include "ArduinoLog.h" @@ -29,7 +27,7 @@ void networkStart(void) configTzTime(MYTZ, "pool.ntp.org", "time.nist.gov", NULL); // literal string #endif - haspProgressVal(255); // hide + // haspProgressVal(255); // hide haspReconnect(); debugStartSyslog(); // mqttStart(); @@ -58,7 +56,7 @@ void networkSetup() #endif } -void networkLoop(void) +IRAM_ATTR void networkLoop(void) { #if HASP_USE_ETHERNET > 0 ethernetLoop(); @@ -132,4 +130,15 @@ void network_get_statusupdate(char* buffer, size_t len) #endif } +void network_get_info(JsonDocument& doc) +{ +#if HASP_USE_ETHERNET > 0 + ethernet_get_info(doc); +#endif + +#if HASP_USE_WIFI > 0 + wifi_get_info(doc); +#endif +} + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_network.h b/src/sys/net/hasp_network.h index 094d96ad..15305d80 100644 --- a/src/sys/net/hasp_network.h +++ b/src/sys/net/hasp_network.h @@ -6,7 +6,7 @@ /* ===== Default Event Processors ===== */ void networkSetup(); -void networkLoop(void); +IRAM_ATTR void networkLoop(void); bool networkEvery5Seconds(void); // bool networkEverySecond(void); void networkStart(void); @@ -16,6 +16,7 @@ void networkStop(void); /* ===== Getter and Setter Functions ===== */ void network_get_statusupdate(char* buffer, size_t len); +void network_get_info(JsonDocument& doc); /* ===== Read/Write Configuration ===== */ diff --git a/src/sys/net/hasp_wifi.cpp b/src/sys/net/hasp_wifi.cpp index 09269c1c..1a55de43 100644 --- a/src/sys/net/hasp_wifi.cpp +++ b/src/sys/net/hasp_wifi.cpp @@ -48,6 +48,7 @@ char wifiPassword[64] = WIFI_PASSW; #else char wifiPassword[64] = ""; #endif +char wifiIpAddress[16] = ""; uint8_t wifiReconnectCounter = 0; // const byte DNS_PORT = 53; @@ -60,10 +61,11 @@ static void wifiConnected(IPAddress ipaddress) #if defined(STM32F4xx) IPAddress ip; ip = WiFi.localIP(); - LOG_TRACE(TAG_WIFI, F("Received IP address %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); + snprintf_P(wifiIpAddress, sizeof(wifiIpAddress), PSTR("%d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); #else - LOG_TRACE(TAG_WIFI, F(D_NETWORK_IP_ADDRESS_RECEIVED), ipaddress.toString().c_str()); + strncpy(wifiIpAddress, ipaddress.toString().c_str(), sizeof(wifiIpAddress)); #endif + LOG_TRACE(TAG_WIFI, F(D_NETWORK_IP_ADDRESS_RECEIVED), wifiIpAddress); LOG_VERBOSE(TAG_WIFI, F("Connected = %s"), WiFi.status() == WL_CONNECTED ? PSTR(D_NETWORK_ONLINE) : PSTR(D_NETWORK_OFFLINE)); @@ -73,8 +75,9 @@ static void wifiConnected(IPAddress ipaddress) static void wifiDisconnected(const char* ssid, uint8_t reason) { wifiReconnectCounter++; + char buffer[64]; - haspProgressVal(wifiReconnectCounter * 3); + // haspProgressVal(wifiReconnectCounter * 3); // networkStop(); if(wifiReconnectCounter > 33) { @@ -82,8 +85,6 @@ static void wifiDisconnected(const char* ssid, uint8_t reason) dispatch_reboot(false); } - char buffer[64]; - switch(reason) { #if defined(ARDUINO_ARCH_ESP8266) case REASON_UNSPECIFIED: @@ -354,13 +355,13 @@ bool wifiShowAP(char* ssid, char* pass) static void wifiReconnect(void) { #if defined(ARDUINO_ARCH_ESP8266) - WiFi.disconnect(true); + WiFi.disconnect(); WiFi.begin(wifiSsid, wifiPassword); WiFi.hostname(haspDevice.get_hostname()); #elif defined(ARDUINO_ARCH_ESP32) // https://github.com/espressif/arduino-esp32/issues/3438#issuecomment-721428310 - WiFi.disconnect(true); + WiFi.disconnect(); WiFi.begin(wifiSsid, wifiPassword, WIFI_ALL_CHANNEL_SCAN); // WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); // causes 255.255.255.255 IP errors WiFi.setHostname(haspDevice.get_hostname()); @@ -446,11 +447,11 @@ bool wifiEvery5Seconds() } else { wifiReconnectCounter++; if(wifiReconnectCounter > 45) { - LOG_ERROR(TAG_WIFI, F("Retries exceed %u: Rebooting..."), wifiReconnectCounter); + LOG_ERROR(TAG_WIFI, F("Retries exceeded %d: Rebooting..."), wifiReconnectCounter); dispatch_reboot(false); } - LOG_WARNING(TAG_WIFI, F("No Connection... retry %u"), wifiReconnectCounter); - if(wifiReconnectCounter % 6 == 0) { + LOG_WARNING(TAG_WIFI, F("No Connection... retry %d"), wifiReconnectCounter); + if(wifiReconnectCounter % 2 == 0) { wifiReconnect(); } return false; @@ -514,8 +515,61 @@ void wifi_get_statusupdate(char* buffer, size_t len) snprintf_P(buffer, len, PSTR("\"ssid\":\"%s\",\"rssi\":%i,\"ip\":\"%d.%d.%d.%d\","), WiFi.SSID(), WiFi.RSSI(), ip[0], ip[1], ip[2], ip[3]); #else + strncpy(wifiIpAddress, WiFi.localIP().toString().c_str(), sizeof(wifiIpAddress)); snprintf_P(buffer, len, PSTR("\"ssid\":\"%s\",\"rssi\":%i,\"ip\":\"%s\","), WiFi.SSID().c_str(), WiFi.RSSI(), - WiFi.localIP().toString().c_str()); + wifiIpAddress); +#endif +} + +const char* wifi_get_ssid() +{ + return wifiSsid; +} + +const char* wifi_get_ip_address() +{ + return wifiIpAddress; +} + +void wifi_get_info(JsonDocument& doc) +{ + String buffer((char*)0); + buffer.reserve(64); + + JsonObject info = doc.createNestedObject(F(D_INFO_WIFI)); + + int8_t rssi = WiFi.RSSI(); + buffer += String(rssi); + buffer += F("dBm ("); + + if(rssi >= -50) { + buffer += F(D_WIFI_RSSI_EXCELLENT ")"); + } else if(rssi >= -59) { + buffer += F(D_WIFI_RSSI_GOOD ")"); + } else if(rssi >= -68) { + buffer += F(D_WIFI_RSSI_FAIR ")"); + } else if(rssi >= -77) { + buffer += F(D_WIFI_RSSI_WEAK ")"); + } else { + buffer += F(D_WIFI_RSSI_BAD ")"); + } + + info[F(D_INFO_SSID)] = String(WiFi.SSID()); + info[F(D_INFO_RSSI)] = buffer; + +#if defined(STM32F4xx) + byte mac[6]; + WiFi.macAddress(mac); + char macAddress[16]; + snprintf_P(macAddress, sizeof(macAddress), PSTR("%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + info[F(D_INFO_IP_ADDRESS)] = String(WiFi.localIP()); + info[F(D_INFO_GATEWAY)] = String(WiFi.gatewayIP()); + info[F(D_INFO_MAC_ADDRESS)] = String(macAddress); +#else + info[F(D_INFO_IP_ADDRESS)] = WiFi.localIP().toString(); + info[F(D_INFO_GATEWAY)] = WiFi.gatewayIP().toString(); + info[F(D_INFO_DNS_SERVER)] = WiFi.dnsIP().toString(); + info[F(D_INFO_MAC_ADDRESS)] = WiFi.macAddress(); #endif } diff --git a/src/sys/net/hasp_wifi.h b/src/sys/net/hasp_wifi.h index 9854d449..7abba5bf 100644 --- a/src/sys/net/hasp_wifi.h +++ b/src/sys/net/hasp_wifi.h @@ -15,6 +15,10 @@ void wifiStop(void); bool wifiValidateSsid(const char* ssid, const char* pass); void wifi_get_statusupdate(char* buffer, size_t len); +void wifi_get_info(JsonDocument& doc); +const char* wifi_get_ssid(); +const char* wifi_get_ip_address(); + #if HASP_USE_CONFIG > 0 bool wifiGetConfig(const JsonObject& settings); bool wifiSetConfig(const JsonObject& settings); diff --git a/src/sys/svc/hasp_console.cpp b/src/sys/svc/hasp_console.cpp new file mode 100644 index 00000000..e6ab31ce --- /dev/null +++ b/src/sys/svc/hasp_console.cpp @@ -0,0 +1,78 @@ +/* MIT License - Copyright (c) 2019-2021 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#include "hasplib.h" + +#if HASP_USE_CONSOLE > 0 + +#include "ConsoleInput.h" + +#include "hasp_debug.h" +#include "hasp_config.h" +#include "hasp_console.h" + +#include "../../hasp/hasp_dispatch.h" + +uint8_t consoleInputEnabled = true; +ConsoleInput debugConsole(&Serial, HASP_CONSOLE_BUFFER); + +void console_update_prompt() +{ + debugConsole.update(); +} + +void consoleSetup() +{ + LOG_TRACE(TAG_MSGR, F(D_SERVICE_STARTING)); + debugConsole.setLineCallback(dispatch_text_line); + LOG_INFO(TAG_CONS, F(D_SERVICE_STARTED)); +} + +IRAM_ATTR void consoleLoop() +{ + if(!consoleInputEnabled) return; + + int16_t keypress; + do { + switch(keypress = debugConsole.readKey()) { + + case ConsoleInput::KEY_PAGE_UP: + dispatch_page_next(LV_SCR_LOAD_ANIM_NONE); + break; + + case ConsoleInput::KEY_PAGE_DOWN: + dispatch_page_prev(LV_SCR_LOAD_ANIM_NONE); + break; + + case(ConsoleInput::KEY_FN)...(ConsoleInput::KEY_FN + 12): + dispatch_set_page(keypress - ConsoleInput::KEY_FN, LV_SCR_LOAD_ANIM_NONE); + break; + } + } while(keypress != 0); +} + +#if HASP_USE_CONFIG > 0 +bool consoleGetConfig(const JsonObject& settings) +{ + bool changed = false; + + if(changed) configOutput(settings, TAG_CONS); + return changed; +} + +/** Set console Configuration. + * + * Read the settings from json and sets the application variables. + * + * @param[in] settings JsonObject with the config settings. + **/ +bool consoleSetConfig(const JsonObject& settings) +{ + configOutput(settings, TAG_CONS); + bool changed = false; + + return changed; +} +#endif // HASP_USE_CONFIG + +#endif \ No newline at end of file diff --git a/src/sys/svc/hasp_console.h b/src/sys/svc/hasp_console.h new file mode 100644 index 00000000..e0ed31c9 --- /dev/null +++ b/src/sys/svc/hasp_console.h @@ -0,0 +1,36 @@ +/* MIT License - Copyright (c) 2019-2021 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifndef HASP_CONSOLE_H +#define HASP_CONSOLE_H + +#if HASP_USE_CONSOLE > 0 + +#include "hasplib.h" + +/* ===== Default Event Processors ===== */ +void consoleSetup(); +IRAM_ATTR void consoleLoop(void); +void consoleEvery5Seconds(void); +void consoleEverySecond(void); +void consoleStart(void); +void consoleStop(void); + +/* ===== Special Event Processors ===== */ +void console_update_prompt(); + +/* ===== Getter and Setter Functions ===== */ + +/* ===== Read/Write Configuration ===== */ +#if HASP_USE_CONFIG > 0 +bool consoleSetConfig(const JsonObject& settings); +bool consoleGetConfig(const JsonObject& settings); +#endif + +#define CONSOLE_UNAUTHENTICATED 0 +#define CONSOLE_USERNAME_OK 10 +#define CONSOLE_USERNAME_NOK 99 +#define CONSOLE_AUTHENTICATED 255 + +#endif +#endif diff --git a/src/sys/svc/hasp_http.cpp b/src/sys/svc/hasp_http.cpp index 506e35f1..136d912f 100644 --- a/src/sys/svc/hasp_http.cpp +++ b/src/sys/svc/hasp_http.cpp @@ -2,9 +2,8 @@ For full license information read the LICENSE file in the project folder */ //#include "webServer.h" -#include "ArduinoJson.h" +#include "hasplib.h" #include "ArduinoLog.h" -#include "lvgl.h" #if defined(ARDUINO_ARCH_ESP32) #include "Update.h" @@ -12,18 +11,14 @@ #include "hasp_conf.h" #include "dev/device.h" - -#include "hasp_gui.h" -#include "hasp/hasp_dispatch.h" -#include "hasp_debug.h" -#include "hasp_config.h" #include "hal/hasp_hal.h" -#include "hasp/hasp_dispatch.h" -#include "hasp/hasp_page.h" -#include "hasp/hasp.h" +#include "hasp_gui.h" +#include "hasp_debug.h" +#include "hasp_config.h" #if HASP_USE_HTTP > 0 +#include "sys/net/hasp_network.h" // #ifdef USE_CONFIG_OVERRIDE // #include "user_config_override.h" @@ -94,15 +89,17 @@ ESP8266WebServer webServer(80); #include #include WebServer webServer(80); +extern const uint8_t EDIT_HTM_GZ_START[] asm("_binary_data_edit_htm_gz_start"); +extern const uint8_t EDIT_HTM_GZ_END[] asm("_binary_data_edit_htm_gz_end"); #endif // ESP32 HTTPUpload* upload; static const char HTTP_MENU_BUTTON[] PROGMEM = - "

"; + "

"; const char MAIN_MENU_BUTTON[] PROGMEM = - "

"; + "

"; const char MIT_LICENSE[] PROGMEM = "
MIT License

"; const char HTTP_DOCTYPE[] PROGMEM = ""; const char HTTP_META_GO_BACK[] PROGMEM = ""; const char HTTP_HEADER[] PROGMEM = "%s"; -const char HTTP_STYLE[] PROGMEM = - ""; + "td{font-size:0.87rem;padding-bottom:0px;padding-top:0px;}th{padding-top:0.5em;}"; const char HTTP_SCRIPT[] PROGMEM = ""; @@ -139,7 +135,7 @@ const char HTTP_HEADER_END[] PROGMEM = ""; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,23 +193,36 @@ static void close_form(String& str) static void add_form_button(String& str, const __FlashStringHelper* label, const __FlashStringHelper* action, const __FlashStringHelper* extra) { - str += F("

"); add_button(str, label, extra); close_form(str); } +static String getContentType(const String& path) +{ + char buff[sizeof(mime::mimeTable[0].mimeType)]; + // Check all entries but last one for match, return if found + for(size_t i = 0; i < sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1; i++) { + strcpy_P(buff, mime::mimeTable[i].endsWith); + if(path.endsWith(buff)) { + strcpy_P(buff, mime::mimeTable[i].mimeType); + return String(buff); + } + } + // Fall-through and just return default type + strcpy_P(buff, mime::mimeTable[sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1].mimeType); + return String(buff); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// + void webHandleHaspConfig(); -// static inline char* haspDevice.get_hostname() -// { -// return mqttNodeName; -// } - //////////////////////////////////////////////////////////////////////////////////////////////////// -bool httpIsAuthenticated(const __FlashStringHelper* fstr_page) + +bool httpIsAuthenticated() { if(http_config.password[0] != '\0') { // Request HTTP auth if httpPassword is set if(!webServer.authenticate(http_config.user, http_config.password)) { @@ -221,13 +230,19 @@ bool httpIsAuthenticated(const __FlashStringHelper* fstr_page) return false; } } + return true; +} + +bool httpIsAuthenticated(const __FlashStringHelper* notused) +{ + if(!httpIsAuthenticated()) return false; #if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) - LOG_TRACE(TAG_HTTP, F("Sending %S page to client connected from: %s"), fstr_page, + LOG_TRACE(TAG_HTTP, F(D_HTTP_SENDING_PAGE), webServer.uri().c_str(), webServer.client().remoteIP().toString().c_str()); #else - // LOG_INFO(TAG_HTTP,F("Sending %s page to client connected from: %s"), page, - // String(webServer.client().remoteIP()).c_str()); + // LOG_INFO(TAG_HTTP,F(D_HTTP_SENDING_PAGE), page, + // String(webServer.client().remoteIP()).c_str()); #endif return true; @@ -235,28 +250,35 @@ bool httpIsAuthenticated(const __FlashStringHelper* fstr_page) void webSendFooter() { - char buffer[16]; - haspGetVersion(buffer, sizeof(buffer)); - #if defined(STM32F4xx) webServer.sendContent(HTTP_END); - webServer.sendContent(buffer); + webServer.sendContent(haspDevice.get_version()); webServer.sendContent(HTTP_FOOTER); #else webServer.sendContent_P(HTTP_END); - webServer.sendContent(buffer); + webServer.sendContent(haspDevice.get_version()); webServer.sendContent_P(HTTP_FOOTER); #endif } +static int webSendCached(int statuscode, const char* contenttype, const char* data, size_t size) +{ + webServer.sendHeader(F("Cache-Control"), F("public, max-age=604800, immutable")); +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) + webServer.send_P(statuscode, contenttype, data, size); +#else + webServer.send(statuscode, contenttype, data); +#endif + return statuscode; +} + void webSendPage(const char* nodename, uint32_t httpdatalength, bool gohome = false) { { char buffer[64]; - haspGetVersion(buffer, sizeof(buffer)); /* Calculate Content Length upfront */ - uint16_t contentLength = strlen(buffer); // verion length + uint32_t contentLength = strlen(haspDevice.get_version()); // version length contentLength += sizeof(HTTP_DOCTYPE) - 1; contentLength += sizeof(HTTP_HEADER) - 1 - 2 + strlen(nodename); // -2 for %s contentLength += sizeof(HTTP_SCRIPT) - 1; @@ -349,29 +371,33 @@ void webHandleRoot() httpMessage += haspDevice.get_hostname(); httpMessage += F("
"); - httpMessage += F("

"); - httpMessage += F("

"); httpMessage += - F("

"); + F("

"); add_form_button(httpMessage, F(D_HTTP_CONFIGURATION), F("/config"), F("")); - // httpMessage += F("

"); - httpMessage += F("

"); #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 - if(HASP_FS.exists(F("/edit.htm.gz"))) { - httpMessage += - F("

"); +#ifdef ARDUINO_ARCH_ESP32 + bool flashfile = true; +#else + bool flashfile = false; +#endif + if(flashfile || HASP_FS.exists(F("/edit.htm.gz")) || HASP_FS.exists(F("/edit.htm"))) { + httpMessage += F("

"); } #endif - httpMessage += F("

"); webSendPage(haspDevice.get_hostname(), httpMessage.length(), false); @@ -442,13 +468,13 @@ void webHandleScreenshot() // Automatic refresh httpMessage += F(" onload=\"aref(5)\" onerror=\"aref(5)\"/>

"); - httpMessage += F("

"); httpMessage += - F("

"); httpMessage += - F("

"); httpMessage += FPSTR(MAIN_MENU_BUTTON); @@ -515,6 +541,70 @@ void webHandleAbout() webSendFooter(); } +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void add_json(String& data, JsonDocument& doc) +{ + char buffer[512]; + size_t len = serializeJson(doc, buffer, sizeof(buffer)); + if(doc.isNull()) return; // empty document + + buffer[len - 1] = ','; + char* start = buffer + 1; + data += String(start); + doc.clear(); +} + +void webHandleInfoJson() +{ // http://plate01/ + if(!httpIsAuthenticated(F("infojson"))) return; + + String htmldata((char*)0); + htmldata.reserve(HTTP_PAGE_SIZE); + DynamicJsonDocument doc(512); + + htmldata += F("

"); + htmldata += haspDevice.get_hostname(); + htmldata += F("


"); + + htmldata += "
"; + + // String path = F(".html"); + // webServer.send(200, getContentType(path), htmldata); + + htmldata += FPSTR(MAIN_MENU_BUTTON); + + webSendPage(haspDevice.get_hostname(), htmldata.length(), false); + webServer.sendContent(htmldata); + + htmldata.clear(); + webSendFooter(); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void webHandleInfo() { // http://plate01/ @@ -528,16 +618,9 @@ void webHandleInfo() httpMessage += haspDevice.get_hostname(); httpMessage += F("
"); - httpMessage += F("Model: "); - httpMessage += haspDevice.get_model(); - /* HASP Stats */ - httpMessage += F("
Version: "); - { - char version[32]; - haspGetVersion(version, sizeof(version)); - httpMessage += version; - } + httpMessage += F("HASP Version: "); + httpMessage += haspDevice.get_version(); httpMessage += F("
Build DateTime: "); httpMessage += __DATE__; httpMessage += F(" "); @@ -653,7 +736,7 @@ void webHandleInfo() httpMessage += String(ETH.linkSpeed()); httpMessage += F(" Mbps"); if(ETH.fullDuplex()) { - httpMessage += F(" FULL_DUPLEX"); + httpMessage += F(" " D_INFO_FULL_DUPLEX); } httpMessage += F("
IP Address: "); httpMessage += String(ETH.localIP().toString()); @@ -698,11 +781,11 @@ void webHandleInfo() Parser::format_bytes(ESP.getFlashChipSize(), size_buf, sizeof(size_buf)); httpMessage += size_buf; - httpMessage += F("
Program Size: "); + httpMessage += F("
Program Size Used: "); Parser::format_bytes(ESP.getSketchSize(), size_buf, sizeof(size_buf)); httpMessage += size_buf; - httpMessage += F("
Free Program Space: "); + httpMessage += F("
Program Size Free: "); Parser::format_bytes(ESP.getFreeSketchSpace(), size_buf, sizeof(size_buf)); httpMessage += size_buf; #endif @@ -715,7 +798,7 @@ void webHandleInfo() httpMessage += haspDevice.get_core_version(); //#endif httpMessage += F("
Last Reset: "); - httpMessage += halGetResetInfo(); + // httpMessage += halGetResetInfo(); httpMessage += FPSTR(MAIN_MENU_BUTTON); @@ -726,52 +809,6 @@ void webHandleInfo() webSendFooter(); } -// String getContentType(String filename) -// { -// if(webServer.hasArg(F("download"))) { -// return F("application/octet-stream"); -// } else if(filename.endsWith(F(".htm")) || filename.endsWith(F(".html"))) { -// return F("text/html"); -// } else if(filename.endsWith(F(".css"))) { -// return F("text/css"); -// } else if(filename.endsWith(F(".js"))) { -// return F("application/javascript"); -// } else if(filename.endsWith(F(".png"))) { -// return F("image/png"); -// } else if(filename.endsWith(F(".gif"))) { -// return F("image/gif"); -// } else if(filename.endsWith(F(".jpg"))) { -// return F("image/jpeg"); -// } else if(filename.endsWith(F(".ico"))) { -// return F("image/x-icon"); -// } else if(filename.endsWith(F(".xml"))) { -// return F("text/xml"); -// } else if(filename.endsWith(F(".pdf"))) { -// return F("application/x-pdf"); -// } else if(filename.endsWith(F(".zip"))) { -// return F("application/x-zip"); -// } else if(filename.endsWith(F(".gz"))) { -// return F("application/x-gzip"); -// } -// return F("text/plain"); -// } - -static String getContentType(const String& path) -{ - char buff[sizeof(mime::mimeTable[0].mimeType)]; - // Check all entries but last one for match, return if found - for(size_t i = 0; i < sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1; i++) { - strcpy_P(buff, mime::mimeTable[i].endsWith); - if(path.endsWith(buff)) { - strcpy_P(buff, mime::mimeTable[i].mimeType); - return String(buff); - } - } - // Fall-through and just return default type - strcpy_P(buff, mime::mimeTable[sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1].mimeType); - return String(buff); -} - /* String urldecode(String str) { String encodedString = ""; @@ -852,60 +889,116 @@ void webHandleFirmwareUpload() { upload = &webServer.upload(); - if(upload->status == UPLOAD_FILE_START) { - if(!httpIsAuthenticated(F("update"))) return; - LOG_TRACE(TAG_HTTP, F("Update: %s"), upload->filename.c_str()); - haspProgressMsg(upload->filename.c_str()); - // WiFiUDP::stopAll(); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - // if(!Update.begin(UPDATE_SIZE_UNKNOWN)) { // start with max available size - if(!Update.begin(maxSketchSpace)) { // start with max available size - webUpdatePrintError(); + switch(upload->status) { + + case UPLOAD_FILE_START: { + if(!httpIsAuthenticated(F("update"))) return; + LOG_TRACE(TAG_HTTP, F("Update: %s"), upload->filename.c_str()); + haspProgressMsg(upload->filename.c_str()); + // WiFiUDP::stopAll(); + + int command = webServer.arg(F("cmd")).toInt(); + size_t size = 0; + if(command == U_FLASH) { + size = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#ifdef ESP32 + } else if(command == U_SPIFFS) { + size = UPDATE_SIZE_UNKNOWN; +#endif + } + // if(!Update.begin(UPDATE_SIZE_UNKNOWN)) { // start with max available size + // const char label[] = "spiffs"; + if(!Update.begin(size, command, -1, 0U)) { // start with max available size + webUpdatePrintError(); + } + break; } - } else if(upload->status == UPLOAD_FILE_WRITE) { - // flashing firmware to ESP - if(Update.write(upload->buf, upload->currentSize) != upload->currentSize) { - webUpdatePrintError(); - } else { - webUploadProgress(); - } + case UPLOAD_FILE_WRITE: // flashing firmware to ESP + if(Update.write(upload->buf, upload->currentSize) != upload->currentSize) { + webUpdatePrintError(); + } else { + webUploadProgress(); + } + break; - } else if(upload->status == UPLOAD_FILE_END) { - haspProgressVal(100); - if(Update.end(true)) { // true to set the size to the current progress - haspProgressMsg(F(D_OTA_UPDATE_APPLY)); - webUpdateReboot(); - } else { - webUpdatePrintError(); - } + case UPLOAD_FILE_END: + haspProgressVal(100); + if(Update.end(true)) { // true to set the size to the current progress + haspProgressMsg(F(D_OTA_UPDATE_APPLY)); + webUpdateReboot(); + } else { + webUpdatePrintError(); + } + break; + + default:; } } #endif #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 -bool handleFileRead(String path) +int handleFileRead(String path) { - if(!httpIsAuthenticated(F("fileread"))) return false; + // if(!httpIsAuthenticated(F("fileread"))) return false; + if(!httpIsAuthenticated()) return false; path = webServer.urlDecode(path).substring(0, 31); if(path.endsWith("/")) { path += F("index.htm"); } + String pathWithGz = path + F(".gz"); if(HASP_FS.exists(pathWithGz) || HASP_FS.exists(path)) { - if(HASP_FS.exists(pathWithGz)) path += F(".gz"); - File file = HASP_FS.open(path, "r"); - String contentType = getContentType(path); - if(path == F("/edit.htm.gz")) { - contentType = F("text/html"); + String contentType((char*)0); + if(webServer.hasArg(F("download"))) + contentType = F("application/octet-stream"); + else + contentType = getContentType(path); + + if(!HASP_FS.exists(path) && HASP_FS.exists(pathWithGz)) + path = pathWithGz; // Only use .gz if normal file doesn't exist + File file = HASP_FS.open(path, "r"); + + String configFile((char*)0); // Verify if the file is config.json + configFile = String(FPSTR(FP_HASP_CONFIG_FILE)); + + if(!strncasecmp(file.name(), configFile.c_str(), configFile.length())) { + file.close(); + DynamicJsonDocument settings(8 * 256); + DeserializationError error = configParseFile(configFile, settings); + + if(error) return 500; // Internal Server Error + + configMaskPasswords(settings); // Output settings to the client with masked passwords! + char buffer[1024]; + size_t len = serializeJson(settings, buffer, sizeof(buffer)); + webServer.setContentLength(len); + webServer.send(200, contentType, buffer); + + } else { + + // Stream other files directly from filesystem + webServer.streamFile(file, contentType); + file.close(); } - webServer.streamFile(file, contentType); - file.close(); - return true; + + return 200; // OK } - return false; + +#ifdef ARDUINO_ARCH_ESP32 + if(path == F("/edit.htm")) { + size_t size = EDIT_HTM_GZ_END - EDIT_HTM_GZ_START; + webServer.sendHeader(F("Content-Encoding"), F("gzip")); + return webSendCached(200, PSTR("text/html"), (const char*)EDIT_HTM_GZ_START, size); // OK + } +#endif + + if(!strcasecmp_P(path.c_str(), PSTR("/favicon.ico"))) + return webSendCached(204, PSTR("image/bmp"), "", 0); // No content + + return 404; // Not found } void handleFileUpload() @@ -918,7 +1011,7 @@ void handleFileUpload() if(!httpIsAuthenticated(F("fileupload"))) return; LOG_INFO(TAG_HTTP, F("Total size: %s"), webServer.headerName(0).c_str()); String filename((char*)0); - filename.reserve(128); + filename.reserve(64); filename = upload->filename; if(!filename.startsWith("/")) { filename = "/"; @@ -984,22 +1077,37 @@ void handleFileCreate() if(webServer.args() == 0) { return webServer.send(500, PSTR("text/plain"), PSTR("BAD ARGS")); } - String path = webServer.arg(0); - LOG_TRACE(TAG_HTTP, F("handleFileCreate: %s"), path.c_str()); - if(path == "/") { - return webServer.send(500, PSTR("text/plain"), PSTR("BAD PATH")); + + if(webServer.hasArg(F("path"))) { + String path = webServer.arg(F("path")); + LOG_TRACE(TAG_HTTP, F("handleFileCreate: %s"), path.c_str()); + if(path == "/") { + return webServer.send(500, PSTR("text/plain"), PSTR("BAD PATH")); + } + if(HASP_FS.exists(path)) { + return webServer.send(500, PSTR("text/plain"), PSTR("FILE EXISTS")); + } + File file = HASP_FS.open(path, "w"); + if(file) { + file.close(); + } else { + return webServer.send(500, PSTR("text/plain"), PSTR("CREATE FAILED")); + } } - if(HASP_FS.exists(path)) { - return webServer.send(500, PSTR("text/plain"), PSTR("FILE EXISTS")); + if(webServer.hasArg(F("init"))) { + dispatch_wakeup(NULL, NULL); + hasp_init(); } - File file = HASP_FS.open(path, "w"); - if(file) { - file.close(); - } else { - return webServer.send(500, PSTR("text/plain"), PSTR("CREATE FAILED")); + if(webServer.hasArg(F("load"))) { + dispatch_wakeup(NULL, NULL); + hasp_load_json(); + } + if(webServer.hasArg(F("page"))) { + uint8_t pageid = atoi(webServer.arg(F("page")).c_str()); + dispatch_wakeup(NULL, NULL); + dispatch_set_page(pageid, LV_SCR_LOAD_ANIM_NONE); } webServer.send(200, PSTR("text/plain"), ""); - path.clear(); } void handleFileList() @@ -1012,13 +1120,15 @@ void handleFileList() } String path = webServer.arg(F("dir")); - LOG_TRACE(TAG_HTTP, F("handleFileList: %s"), path.c_str()); + // LOG_TRACE(TAG_HTTP, F("handleFileList: %s"), path.c_str()); path.clear(); #if defined(ARDUINO_ARCH_ESP32) - File root = HASP_FS.open("/", FILE_READ); - File file = root.openNextFile(); - String output = "["; + File root = HASP_FS.open("/", FILE_READ); + File file = root.openNextFile(); + String output((char*)0); + output.reserve(HTTP_PAGE_SIZE); + output = "["; while(file) { if(output != "[") { @@ -1042,7 +1152,10 @@ void handleFileList() webServer.send(200, PSTR("text/json"), output); #elif defined(ARDUINO_ARCH_ESP8266) Dir dir = HASP_FS.openDir(path); - String output = "["; + String output((char*)0); + output.reserve(HTTP_PAGE_SIZE); + output = "["; + while(dir.next()) { File entry = dir.openFile("r"); if(output != "[") { @@ -1089,35 +1202,28 @@ void webHandleConfig() httpMessage += F("
"); #if HASP_USE_WIFI > 0 - httpMessage += F("

"); + add_form_button(httpMessage, F(D_HTTP_WIFI_SETTINGS), F("/config/wifi"), F("")); #endif - #if HASP_USE_MQTT > 0 - httpMessage += F("

"); + add_form_button(httpMessage, F(D_HTTP_MQTT_SETTINGS), F("/config/mqtt"), F("")); #endif - - httpMessage += F("

"); - - httpMessage += F("

"); + add_form_button(httpMessage, F(D_HTTP_HTTP_SETTINGS), F("/config/http"), F("")); + add_form_button(httpMessage, F(D_HTTP_GUI_SETTINGS), F("/config/gui"), F("")); // httpMessage += - // F("

"); #if HASP_USE_GPIO > 0 - httpMessage += F("

"); #endif - httpMessage += F("

"); httpMessage += - F("

"); httpMessage += FPSTR(MAIN_MENU_BUTTON); @@ -1170,11 +1276,7 @@ void webHandleMqttConfig() httpMessage += F("'>

"); - add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F("")); - // httpMessage += PSTR("

"); - + add_form_button(httpMessage, F(D_BACK_ICON D_HTTP_CONFIGURATION), F("/config"), F("")); webSendPage(haspDevice.get_hostname(), httpMessage.length(), false); webServer.sendContent(httpMessage); } @@ -1259,24 +1361,16 @@ void webHandleGuiConfig() // F("

"); #if TOUCH_DRIVER == 2046 && defined(TOUCH_CS) - add_form_button(httpMessage, F(D_HTTP_CALIBRATE), F("/config/gui"), F("name='action' value='calibrate'")); - -// httpMessage += PSTR("

"); + add_form_button(httpMessage, F(D_HTTP_CALIBRATE), F("/config/gui"), F("name='cal' value='1'")); #endif - add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F("")); - - // httpMessage += PSTR("

"); - + add_form_button(httpMessage, F(D_BACK_ICON D_HTTP_CONFIGURATION), F("/config"), F("")); webSendPage(haspDevice.get_hostname(), httpMessage.length(), false); webServer.sendContent(httpMessage); } webSendFooter(); - if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str()); + if(webServer.hasArg(F("cal"))) dispatch_calibrate(NULL, NULL); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1308,10 +1402,7 @@ void webHandleWifiConfig() #if HASP_USE_WIFI > 0 && !defined(STM32F4xx) if(WiFi.getMode() == WIFI_STA) { - add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F("")); - // httpMessage += PSTR("

"); + add_form_button(httpMessage, F(D_BACK_ICON D_HTTP_CONFIGURATION), F("/config"), F("")); } #endif @@ -1336,28 +1427,6 @@ void webHandleHttpConfig() StaticJsonDocument<256> settings; httpGetConfig(settings.to()); - // String httpMessage((char *)0); - // httpMessage.reserve(HTTP_PAGE_SIZE); - // httpMessage += F("

"); - // httpMessage += haspDevice.get_hostname(); - // httpMessage += F("


"); - - // httpMessage += F("
"); - // httpMessage += F("Web Username (optional)
Web Password (optional)

"); - - // httpMessage += PSTR("

"); - char httpMessage[HTTP_PAGE_SIZE]; size_t len = snprintf_P( @@ -1369,7 +1438,7 @@ void webHandleHttpConfig() "Web Password (optional)" "" "

" - "

"), haspDevice.get_hostname(), settings[FPSTR(FP_CONFIG_USER)].as().c_str(), settings[FPSTR(FP_CONFIG_PASS)].as().c_str()); @@ -1399,15 +1468,16 @@ void webHandleGpioConfig() if(webServer.hasArg(PSTR("save"))) { uint8_t id = webServer.arg(F("id")).toInt(); uint8_t pin = webServer.arg(F("pin")).toInt(); - uint8_t type = webServer.arg(F("type")).toInt() + webServer.arg(F("state")).toInt(); + uint8_t type = webServer.arg(F("type")).toInt(); uint8_t group = webServer.arg(F("group")).toInt(); uint8_t pinfunc = webServer.arg(F("func")).toInt(); - gpioSavePinConfig(id, pin, type, group, pinfunc); + bool inverted = webServer.arg(F("state")).toInt(); + gpioSavePinConfig(id, pin, type, group, pinfunc, inverted); } if(webServer.hasArg(PSTR("del"))) { uint8_t id = webServer.arg(F("id")).toInt(); uint8_t pin = webServer.arg(F("pin")).toInt(); - gpioSavePinConfig(id, pin, HASP_GPIO_FREE, 0, 0); + gpioSavePinConfig(id, pin, hasp_gpio_type_t::FREE, 0, 0, false); } { @@ -1419,76 +1489,83 @@ void webHandleGpioConfig() httpMessage += F("
"); - httpMessage += F(""); + httpMessage += F("
PinTypeGroupDefaultAction
"); for(uint8_t gpio = 0; gpio < NUM_DIGITAL_PINS; gpio++) { for(uint8_t id = 0; id < HASP_NUM_GPIO_CONFIG; id++) { hasp_gpio_config_t conf = gpioGetPinConfig(id); if((conf.pin == gpio) && gpioConfigInUse(id) && gpioInUse(gpio) && !gpioIsSystemPin(gpio)) { httpMessage += F("
" D_GPIO_PIN "Type" D_GPIO_GROUP + "DefaultAction
"); - httpMessage += halGpioName(gpio); - httpMessage += F(""); + // httpMessage += halGpioName(gpio); + httpMessage += haspDevice.gpio_name(gpio).c_str(); + if(conf.type >= 0x80) { + httpMessage += F(""); - switch(conf.type & 0xfe) { - case HASP_GPIO_SWITCH: - // case HASP_GPIO_SWITCH_INVERTED: - httpMessage += F("Switch"); + switch(conf.type) { + case hasp_gpio_type_t::SWITCH: + httpMessage += F(D_GPIO_SWITCH); break; - case HASP_GPIO_BUTTON: - // case HASP_GPIO_BUTTON_INVERTED: - httpMessage += F("Button"); + case hasp_gpio_type_t::BUTTON: + httpMessage += F(D_GPIO_BUTTON); break; - case HASP_GPIO_LED: - // case HASP_GPIO_LED_INVERTED: - httpMessage += F("Led"); + case hasp_gpio_type_t::TOUCH: + httpMessage += F(D_GPIO_TOUCH); break; - case HASP_GPIO_LED_R: - case HASP_GPIO_LED_G: - case HASP_GPIO_LED_B: - // case HASP_GPIO_LED_INVERTED: - httpMessage += F("Mood "); + case hasp_gpio_type_t::LED: + httpMessage += F(D_GPIO_LED); break; - case HASP_GPIO_RELAY: - // case HASP_GPIO_RELAY_INVERTED: - httpMessage += F("Relay"); + case hasp_gpio_type_t::LED_R: + httpMessage += F(D_GPIO_LED_R); break; - case HASP_GPIO_PWM: - // case HASP_GPIO_PWM_INVERTED: - httpMessage += F("PWM"); + case hasp_gpio_type_t::LED_G: + httpMessage += F(D_GPIO_LED_G); break; - case HASP_GPIO_SERIAL_DIMMER: - httpMessage += F("Serial Dimmer"); + case hasp_gpio_type_t::LED_B: + httpMessage += F(D_GPIO_LED_B); break; + case hasp_gpio_type_t::LIGHT_RELAY: + httpMessage += F(D_GPIO_LIGHT_RELAY); + break; + case hasp_gpio_type_t::POWER_RELAY: + httpMessage += F(D_GPIO_POWER_RELAY); + break; + case hasp_gpio_type_t::SHUTTER_RELAY: + httpMessage += F("SHUTTER_RELAY"); + break; + case hasp_gpio_type_t::PWM: + httpMessage += F(D_GPIO_PWM); + break; + case hasp_gpio_type_t::DAC: + httpMessage += F(D_GPIO_DAC); + break; + +#if defined(LANBONL8) + // case hasp_gpio_type_t::SERIAL_DIMMER: + // httpMessage += F(D_GPIO_SERIAL_DIMMER); + // break; + case hasp_gpio_type_t::SERIAL_DIMMER_EU: + httpMessage += F("L8-HD (EU)"); + break; + case hasp_gpio_type_t::SERIAL_DIMMER_AU: + httpMessage += F("L8-HD (AU)"); + break; +#endif default: - httpMessage += F("Unknown"); + httpMessage += F(D_GPIO_UNKNOWN); } - switch(conf.type & 0xfe) { - case HASP_GPIO_LED_R: - httpMessage += F("Red"); - break; - case HASP_GPIO_LED_G: - httpMessage += F("Green"); - break; - case HASP_GPIO_LED_B: - httpMessage += F("Blue"); - break; - } - - httpMessage += F(""); + httpMessage += F(""); httpMessage += conf.group; httpMessage += F(""); + httpMessage += (conf.inverted) ? F(D_GPIO_STATE_INVERTED) : F(D_GPIO_STATE_NORMAL); - // bool inverted = (conf.type == HASP_GPIO_BUTTON_INVERTED) || - // (conf.type == HASP_GPIO_SWITCH_INVERTED) || (conf.type == HASP_GPIO_LED_INVERTED) - // || (conf.type == HASP_GPIO_RELAY_INVERTED) || (conf.type == - // HASP_GPIO_PWM_INVERTED); - - httpMessage += (conf.type & 0x1) ? F("High") : F("Low"); - - httpMessage += F("Edit "); if(configCount < HASP_NUM_GPIO_CONFIG) { + httpMessage += F("

"); + httpMessage += F("

"); + httpMessage += F("

"); httpMessage += F("

"); + httpMessage += F("'>

"); } - add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F("")); - // httpMessage += F("

"); @@ -1545,73 +1627,80 @@ void webHandleGpioOptions() httpMessage += config_id; httpMessage += F(" Options

"); - httpMessage += F("

Pin "); hasp_gpio_config_t conf = gpioGetPinConfig(config_id); for(uint8_t io = 0; io < NUM_DIGITAL_PINS; io++) { if(((conf.pin == io) || !gpioInUse(io)) && !gpioIsSystemPin(io)) { - httpMessage += getOption(io, halGpioName(io), conf.pin == io); + httpMessage += getOption(io, haspDevice.gpio_name(io).c_str(), conf.pin == io); } } httpMessage += F("

"); bool selected; httpMessage += F("

Type

"); - httpMessage += F("

Group "); + httpMessage += getOption(0, F(D_GPIO_GROUP_NONE), conf.group == 0); String group((char*)0); group.reserve(10); for(int i = 1; i < 15; i++) { - group = F("Group "); + group = F(D_GPIO_GROUP " "); group += i; httpMessage += getOption(i, group, conf.group == i); } httpMessage += F("

"); - httpMessage += F("

Default State "); + httpMessage += getOption(0, F(D_GPIO_STATE_NORMAL), !conf.inverted); + httpMessage += getOption(1, F(D_GPIO_STATE_INVERTED), conf.inverted); httpMessage += F("

"); httpMessage += F("

"); - httpMessage += PSTR("

"); webSendPage(haspDevice.get_hostname(), httpMessage.length(), false); @@ -1619,7 +1708,135 @@ void webHandleGpioOptions() } webSendFooter(); - if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str()); // Security check + // if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str()); // Security check +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void webHandleGpioInput() +{ // http://plate01/config/gpio/options + if(!httpIsAuthenticated(F("config/gpio/input"))) return; + { + StaticJsonDocument<256> settings; + guiGetConfig(settings.to()); + + uint8_t config_id = webServer.arg(F("id")).toInt(); + + String httpMessage((char*)0); + httpMessage.reserve(HTTP_PAGE_SIZE); + httpMessage += F("

"); + httpMessage += haspDevice.get_hostname(); + httpMessage += F("


"); + + httpMessage += F("
"); + httpMessage += F(""); + + httpMessage += F("

GPIO Options"); + httpMessage += config_id; + httpMessage += F(" Options

"); + + httpMessage += F("

" D_GPIO_PIN "

"); + + bool selected; + httpMessage += F("

Type

"); + + httpMessage += F("

" D_GPIO_GROUP "

"); + + httpMessage += F("

Default State

"); + + httpMessage += F("

Resistor

"); + + httpMessage += + F("

"); + + httpMessage += PSTR("

"); + + webSendPage(haspDevice.get_hostname(), httpMessage.length(), false); + webServer.sendContent(httpMessage); + } + webSendFooter(); + + // if(webServer.hasArg(F("action"))) dispatch_text_line(webServer.arg(F("action")).c_str()); // Security check } #endif // HASP_USE_GPIO @@ -1642,7 +1859,7 @@ void webHandleDebugConfig() uint16_t baudrate = settings[FPSTR(FP_CONFIG_BAUD)].as(); httpMessage += F("

Serial Port

"); httpMessage += F("


"); @@ -1733,10 +1950,10 @@ void webHandleHaspConfig() httpMessage += getOption(7, F("Template"), themeid == 7); #endif httpMessage += F("
"); - httpMessage += - F("Hue

"); httpMessage += F("

Default Font

"); - httpMessage += F("

"); + // httpMessage += F("

"); - // httpMessage += F("

Firmware   " + "Filesystem"); + + add_button(httpMessage, F(D_HTTP_UPDATE_FIRMWARE), F("")); + httpMessage += F("

"); + + // httpMessage += F("

"); // httpMessage += F("

"); - httpMessage += F("
"); + httpMessage += F(""); httpMessage += F("
Update ESP from URL"); httpMessage += F("
" - "

" - "


"); + "will be erased and the device is restarted. You may need to connect to the WiFi AP displayed on " + "the " + "panel to re-configure the device before accessing it again. ALL FILES WILL BE LOST!" + "


"); - add_form_button(httpMessage, F("↩ " D_HTTP_CONFIGURATION), F("/config"), F("")); - // httpMessage += - // PSTR("

"); + add_button(httpMessage, F(D_HTTP_ERASE_DEVICE), F("name='confirm' value='yes'")); + close_form(httpMessage); + + add_form_button(httpMessage, F(D_BACK_ICON D_HTTP_CONFIGURATION), F("/config"), F("")); } webSendPage(haspDevice.get_hostname(), httpMessage.length(), resetConfirmed); @@ -1992,6 +2222,7 @@ void httpSetup() // Shared pages webServer.on(F("/about"), webHandleAbout); + webServer.on(F("/css"), []() { webSendCached(200, PSTR("text/css"), HTTP_CSS, sizeof(HTTP_CSS) - 1); }); webServer.onNotFound(httpHandleNotFound); #if HASP_USE_WIFI > 0 @@ -2023,7 +2254,7 @@ void httpSetup() webServer.on(F("/list"), HTTP_GET, handleFileList); // load editor webServer.on(F("/edit"), HTTP_GET, []() { - if(!handleFileRead("/edit.htm")) { + if(handleFileRead("/edit.htm") != 200) { char mimetype[16]; snprintf_P(mimetype, sizeof(mimetype), PSTR("text/plain")); webServer.send_P(404, mimetype, PSTR("FileNotFound")); @@ -2043,7 +2274,8 @@ void httpSetup() #endif webServer.on(F("/"), webHandleRoot); - webServer.on(F("/info"), webHandleInfo); + webServer.on(F("/info"), webHandleInfoJson); + // webServer.on(F("/info"), webHandleInfo); webServer.on(F("/screenshot"), webHandleScreenshot); webServer.on(F("/firmware"), webHandleFirmware); webServer.on(F("/reboot"), httpHandleReboot); @@ -2062,6 +2294,7 @@ void httpSetup() #if HASP_USE_GPIO > 0 webServer.on(F("/config/gpio"), webHandleGpioConfig); webServer.on(F("/config/gpio/options"), webHandleGpioOptions); + webServer.on(F("/config/gpio/input"), webHandleGpioInput); #endif webServer.on(F("/saveConfig"), webHandleSaveConfig); webServer.on(F("/resetConfig"), httpHandleResetConfig); @@ -2099,9 +2332,10 @@ void httpReconnect() } //////////////////////////////////////////////////////////////////////////////////////////////////// -void httpLoop(void) +IRAM_ATTR void httpLoop(void) { - if(http_config.enable) webServer.handleClient(); + // if(http_config.enable) + webServer.handleClient(); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/sys/svc/hasp_http.h b/src/sys/svc/hasp_http.h index 51313c6d..23be22ca 100644 --- a/src/sys/svc/hasp_http.h +++ b/src/sys/svc/hasp_http.h @@ -16,7 +16,7 @@ struct hasp_http_config_t }; void httpSetup(); -void httpLoop(void); +IRAM_ATTR void httpLoop(void); void httpEvery5Seconds(void); // void httpReconnect(void); void httpStart(void); diff --git a/src/sys/svc/hasp_mdns.cpp b/src/sys/svc/hasp_mdns.cpp index 822c7970..35761d98 100644 --- a/src/sys/svc/hasp_mdns.cpp +++ b/src/sys/svc/hasp_mdns.cpp @@ -60,8 +60,7 @@ void mdnsStart() MDNS.addService(service, proto, 80); strcpy_P(key, PSTR("app_version")); - haspGetVersion(value, sizeof(value)); - MDNS.addServiceTxt(service, proto, key, value); + MDNS.addServiceTxt(service, proto, key, haspDevice.get_version()); strcpy_P(key, PSTR("app_name")); strcpy_P(value, PSTR(D_MANUFACTURER)); @@ -78,7 +77,7 @@ void mdnsStart() } } -void mdnsLoop(void) +IRAM_ATTR void mdnsLoop(void) { #if defined(ARDUINO_ARCH_ESP8266) if(mdns_config.enable) { diff --git a/src/sys/svc/hasp_mdns.h b/src/sys/svc/hasp_mdns.h index 2700595b..d81cd3f6 100644 --- a/src/sys/svc/hasp_mdns.h +++ b/src/sys/svc/hasp_mdns.h @@ -13,7 +13,7 @@ struct hasp_mdns_config_t /* ===== Default Event Processors ===== */ void mdnsSetup(); -void mdnsLoop(void); +IRAM_ATTR void mdnsLoop(void); void mdnsStart(void); void mdnsStop(void); diff --git a/src/sys/svc/hasp_ota.cpp b/src/sys/svc/hasp_ota.cpp index d9b1c285..42bee68c 100644 --- a/src/sys/svc/hasp_ota.cpp +++ b/src/sys/svc/hasp_ota.cpp @@ -106,8 +106,7 @@ bool otaUpdateCheck() static inline void otaProgress(void) { LOG_VERBOSE(TAG_OTA, F("%s %3u%"), - (ArduinoOTA.getCommand() == U_FLASH ? PSTR("Firmware update in progress...") - : PSTR("Filesystem update in progress...")), + (ArduinoOTA.getCommand() == U_FLASH ? F(D_OTA_UPDATING_FIRMWARE) : F(D_OTA_UPDATING_FILESYSTEM)), otaPrecentageComplete); } @@ -201,7 +200,7 @@ void otaSetup(void) } } -void otaLoop(void) +IRAM_ATTR void otaLoop(void) { ArduinoOTA.handle(); } diff --git a/src/sys/svc/hasp_ota.h b/src/sys/svc/hasp_ota.h index 215cd73a..83908539 100644 --- a/src/sys/svc/hasp_ota.h +++ b/src/sys/svc/hasp_ota.h @@ -10,7 +10,7 @@ /* ===== Default Event Processors ===== */ void otaSetup(void); -void otaLoop(void); +IRAM_ATTR void otaLoop(void); void otaEverySecond(void); /* ===== Special Event Processors ===== */ diff --git a/src/sys/svc/hasp_slave.cpp b/src/sys/svc/hasp_slave.cpp index a3739845..d3259e1c 100644 --- a/src/sys/svc/hasp_slave.cpp +++ b/src/sys/svc/hasp_slave.cpp @@ -1,17 +1,14 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "hasp_conf.h" +#include "hasplib.h" #if HASP_USE_TASMOTA_CLIENT > 0 #include "hasp_slave.h" -#include "ArduinoJson.h" -#include "hasp_dispatch.h" #include "hasp_gui.h" #include "hasp_hal.h" #include "hasp_config.h" -#include "hasp.h" #include "tasmotaSlave.h" // set RX and TX pins @@ -73,10 +70,9 @@ void TASMO_TELE_JSON() char data[3 * 128]; { char buffer[128]; - haspGetVersion(buffer, sizeof(buffer)); - snprintf_P(data, sizeof(data), PSTR("{\"status\":\"available\",\"version\":\"%s\",\"uptime\":%lu,"), buffer, - long(millis() / 1000)); + snprintf_P(data, sizeof(data), PSTR("{\"status\":\"available\",\"version\":\"%s\",\"uptime\":%lu,"), + haspDevice.get_version(), long(millis() / 1000)); snprintf_P(buffer, sizeof(buffer), PSTR("\"espCanUpdate\":\"false\",\"page\":%u,\"numPages\":%u,"), haspGetPage(), (HASP_NUM_PAGES)); diff --git a/src/sys/svc/hasp_telnet.cpp b/src/sys/svc/hasp_telnet.cpp index 52de8836..4001dd3a 100644 --- a/src/sys/svc/hasp_telnet.cpp +++ b/src/sys/svc/hasp_telnet.cpp @@ -1,11 +1,10 @@ /* MIT License - Copyright (c) 2019-2021 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#include "hasp_conf.h" +#include "hasplib.h" #if HASP_USE_TELNET > 0 -#include "ArduinoJson.h" #include "ConsoleInput.h" #include "hasp_debug.h" @@ -45,8 +44,8 @@ void telnetClientDisconnect() LOG_TRACE(TAG_TELN, F(D_TELNET_CLOSING_CONNECTION), telnetClient.remoteIP().toString().c_str()); telnetLoginState = TELNET_UNAUTHENTICATED; telnetLoginAttempt = 0; // Initial attempt - // delete telnetConsole; - // telnetConsole = NULL; + delete telnetConsole; + telnetConsole = NULL; telnetClient.stop(); } @@ -182,7 +181,7 @@ static inline void telnetProcessCharacter(char ch) #endif -static inline void telnetProcessLine(const char* input) +static void telnetProcessLine(const char* input) { switch(telnetLoginState) { case TELNET_UNAUTHENTICATED: { @@ -216,11 +215,16 @@ static inline void telnetProcessLine(const char* input) break; } default: - if(strcasecmp_P(input, PSTR("exit")) == 0) { + if(strcasecmp_P(input, PSTR("exit")) == 0 || strcasecmp_P(input, PSTR("quit")) == 0 || + strcasecmp_P(input, PSTR("bye")) == 0) { telnetClientDisconnect(); } else if(strcasecmp_P(input, PSTR("logoff")) == 0) { - telnetClient.println(F("\r\n" D_USERNAME " ")); - telnetLoginState = TELNET_UNAUTHENTICATED; + if(strcmp(input, http_config.password) == 0) { + telnetClient.println(F("\r\n" D_USERNAME " ")); + telnetLoginState = TELNET_UNAUTHENTICATED; + } else { + telnetClientDisconnect(); + } } else { dispatch_text_line(input); } @@ -246,20 +250,21 @@ void telnetSetup() telnetServer->setNoDelay(true); telnetServer->begin(); - telnetConsole = new ConsoleInput(&telnetClient, HASP_CONSOLE_BUFFER); - if(telnetConsole != NULL) { - telnetConsole->setLineCallback(telnetProcessLine); - LOG_INFO(TAG_TELN, F(D_TELNET_STARTED)); - return; - } + // telnetConsole = new ConsoleInput(&telnetClient, HASP_CONSOLE_BUFFER); + // if(telnetConsole != NULL) { + // telnetConsole->setLineCallback(telnetProcessLine); + // LOG_INFO(TAG_TELN, F(D_TELNET_STARTED)); + // return; + // } + LOG_INFO(TAG_TELN, F(D_TELNET_STARTED)); + } else { + LOG_ERROR(TAG_TELN, F(D_TELNET_FAILED)); } - - LOG_ERROR(TAG_TELN, F(D_TELNET_FAILED)); #endif } } -void telnetLoop() +IRAM_ATTR void telnetLoop() { // Basic telnet client handling code from: https://gist.github.com/tablatronix/4793677ca748f5f584c95ec4a2b10303 @@ -291,6 +296,31 @@ void telnetLoop() } } #else + + /* Active Client: Process user input */ + if(telnetClient.connected()) { + if(telnetConsole) { + int16_t keypress = telnetConsole->readKey(); + } else { + telnetConsole = new ConsoleInput(&telnetClient, HASP_CONSOLE_BUFFER); + if(telnetConsole) { + telnetConsole->setLineCallback(telnetProcessLine); + } else { + telnetClientDisconnect(); + LOG_ERROR(TAG_TELN, F(D_TELNET_FAILED)); + } + } + } + +#endif +} + +void telnetEverySecond(void) +{ + if(!telnetClient.connected() && telnetLoginState != TELNET_UNAUTHENTICATED) { + telnetClientDisconnect(); // active client disconnected + } + if(telnetServer && telnetServer->hasClient()) { // a new client has connected if(!telnetClient.connected()) { // nobody is already connected telnetAcceptClient(); // allow the new client @@ -298,22 +328,7 @@ void telnetLoop() LOG_WARNING(TAG_TELN, F(D_TELNET_CLIENT_REJECTED)); telnetServer->available().stop(); // already have a client, block new connections } - } else { - if(!telnetClient.connected() && telnetLoginState != TELNET_UNAUTHENTICATED) { - telnetClientDisconnect(); // active client disconnected - } else { - - /* Active Client: Process user input */ - if(telnetConsole && telnetClient.connected()) { - int16_t keypress = telnetConsole->readKey(); - switch(keypress) { - case ConsoleInput::KEY_PAUSE: - break; - } - } - } } -#endif } #if HASP_USE_CONFIG > 0 diff --git a/src/sys/svc/hasp_telnet.h b/src/sys/svc/hasp_telnet.h index 314e678b..1e4ebdbb 100644 --- a/src/sys/svc/hasp_telnet.h +++ b/src/sys/svc/hasp_telnet.h @@ -6,12 +6,11 @@ #if HASP_USE_TELNET > 0 -#include "hasp_conf.h" -#include "ArduinoJson.h" +#include "hasplib.h" /* ===== Default Event Processors ===== */ void telnetSetup(); -void telnetLoop(void); +IRAM_ATTR void telnetLoop(void); void telnetEvery5Seconds(void); void telnetEverySecond(void); void telnetStart(void); diff --git a/test/config.yaml b/test/config.yaml new file mode 100644 index 00000000..801002f1 --- /dev/null +++ b/test/config.yaml @@ -0,0 +1,11 @@ +# config.yaml +--- +name: Common test information +description: Connection information for MQTT Client + +variables: + host: homeassistant.local + username: hasp + password: hasp + port: 1883 + plate: ubuntu-desktop-20-04 diff --git a/test/config_template.yaml b/test/config_template.yaml index af9cee27..fd0dfab2 100644 --- a/test/config_template.yaml +++ b/test/config_template.yaml @@ -1,11 +1,12 @@ # Copy and rename this file to config.yaml +# Enter the connection details for the mqtt test server --- name: Common test information description: Connection information for MQTT Client variables: - host: homeassistant.local - username: - password: + host: port: 1883 - plate: + username: + password: + plate: diff --git a/test/test_checkbox.tavern.yaml b/test/test_checkbox.tavern.yaml new file mode 100644 index 00000000..dc721221 --- /dev/null +++ b/test/test_checkbox.tavern.yaml @@ -0,0 +1,293 @@ +# test_page.tavern.yaml +--- +test_name: Obj Standard Properties + +includes: + - !include config.yaml + +paho-mqtt: + client: + transport: tcp + client_id: tavern-tester + connect: + host: "{host}" + port: !int "{port:d}" + timeout: 3 + auth: + username: "{username}" + password: "{password}" + +marks: + - parametrize: + key: obj + vals: + - checkbox + - parametrize: + key: + - hidden + - hidden_inv + - x + - y + - w + - h + - radius + - opacity + - str1 + - str2 + vals: + - [1, 0, 120, 121, 122, 123, 0, 255, "I'm sorry.", "louie"] + - [1, 0, -10, -10, 256, 257, 1, 64, "louie", " The cat stretched."] + - [ + 0, + 1, + 1024, + 1025, + 1026, + 1027, + 5, + 0, + "The pipe began to rust while new.", + "", + ] + - [ + 0, + 1, + 80, + 81, + 82, + 83, + 32535, + 192, + "", + " Oak is strong and also gives shade.", + ] + +stages: + - name: Page 1 + mqtt_publish: + topic: hasp/{plate}/command + payload: "page 1" + mqtt_response: + topic: hasp/{plate}/state/page + payload: "1" + timeout: 1 + delay_after: 0 + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" + delay_after: 0.2 + + - name: Create object + mqtt_publish: + topic: hasp/{plate}/command/jsonl + json: + obj: "{obj}" + id: 1 + x: 128 + y: 128 + delay_after: 0 + + - name: Test x + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.x={x}","p1b1.x"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + x: !int "{x:d}" + timeout: 1 + + - name: Test y + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.y={y}","p1b1.y"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + y: !int "{y:d}" + timeout: 1 + + # - name: Test w + # mqtt_publish: + # topic: hasp/{plate}/command/json + # payload: '["p1b1.w={w}","p1b1.w"]' + # mqtt_response: + # topic: hasp/{plate}/state/p1b1 + # json: + # w: !int "{w:d}" + # timeout: 1 + + # - name: Test h + # mqtt_publish: + # topic: hasp/{plate}/command/json + # payload: '["p1b1.h={h}","p1b1.h"]' + # delay_after: 0 + # mqtt_response: + # topic: hasp/{plate}/state/p1b1 + # json: + # h: !int "{h:d}" + # timeout: 1 + + - name: Test enabled + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.enabled={hidden}","p1b1.enabled"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + enabled: !int "{hidden:d}" + timeout: 1 + + - name: Test click + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.click={hidden}","p1b1.click"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + click: !int "{hidden:d}" + timeout: 1 + + - name: Test swipe + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.swipe={hidden_inv}","p1b1.swipe"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + swipe: !int "{hidden_inv:d}" + timeout: 1 + + - name: Set vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden_inv:d}" + timeout: 1 + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden:d}" + timeout: 1 + + - name: Set hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden:d}" + timeout: 1 + + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden_inv:d}" + timeout: 1 + + - name: Test opacity + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.opacity={opacity}","p1b1.opacity"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + opacity: !int "{opacity:d}" + timeout: 1 + + - name: Test radius + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.radius={radius}","p1b1.radius"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + radius: !int "{radius:d}" + timeout: 1 + + - name: Set value_str + mqtt_publish: + topic: "hasp/{plate}/command/p1b1.value_str" + payload: "{str1}{str2}" + delay_after: 0.05 + - name: Get value_str + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.value_str" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + value_str: "{str1}{str2}" + timeout: 1 + + - name: Get obj type + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.obj" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + obj: "{obj}" + timeout: 1 + + - name: Test groupid + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.groupid={hidden}","p1b1.groupid"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + groupid: !int "{hidden:d}" + timeout: 1 + + - name: Test val + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.val={hidden}","p1b1.val"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + val: !int "{hidden:d}" + timeout: 1 + + - name: Set text + mqtt_publish: + topic: "hasp/{plate}/command/p1b1.text" + payload: "{str1}{str2}" + delay_after: 0.05 + - name: Get text + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.text" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + text: "{str1}{str2}" + timeout: 1 diff --git a/test/test_colors.tavern.yaml b/test/test_colors.tavern.yaml index 017fe246..167ccfc5 100644 --- a/test/test_colors.tavern.yaml +++ b/test/test_colors.tavern.yaml @@ -1,6 +1,6 @@ # test_value_str.tavern.yaml --- -test_name: Colors setup +test_name: Obj Standard Properties includes: - !include config.yaml @@ -26,10 +26,9 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 - delay_after: 0 --- -test_name: Colors loop +test_name: Obj Standard Properties includes: - !include config.yaml @@ -108,8 +107,13 @@ marks: stages: - name: Set bg_color mqtt_publish: - topic: hasp/{plate}/command/json - payload: '["p1b0.bg_color={color}","clearpage"]' + topic: hasp/{plate}/command + payload: "p[1].b[0].bg_color={color}" + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" - name: Create object mqtt_publish: @@ -117,10 +121,7 @@ stages: json: obj: "{obj}" id: 1 - txt: "{color}" - r: !int "{r:d}" - g: !int "{g:d}" - b: !int "{b:d}" + text: "{color}" - name: Set bg_color mqtt_publish: @@ -130,7 +131,7 @@ stages: - name: Test named COLOR mqtt_publish: topic: hasp/{plate}/command/json - payload: '["p1b1.text_color={color}","p1b1.text_color"]' + payload: '["p[1].b[1].text_color={color}","p[1].b[1].text_color"]' mqtt_response: topic: hasp/{plate}/state/p1b1 json: @@ -143,12 +144,12 @@ stages: - name: Reset mqtt_publish: topic: hasp/{plate}/command - payload: "p1b1.text_color=123" + payload: "p[1].b[1].text_color=123" - name: Test hex COLOR mqtt_publish: topic: hasp/{plate}/command/json - payload: '["p1b1.text_color={hex}","p1b1.text_color"]' + payload: '["p[1].b[1].text_color={hex}","p[1].b[1].text_color"]' mqtt_response: topic: hasp/{plate}/state/p1b1 json: @@ -161,12 +162,12 @@ stages: - name: Reset mqtt_publish: topic: hasp/{plate}/command - payload: "p1b1.text_color=529" + payload: "p[1].b[1].text_color=529" - name: Test rgb565 COLOR mqtt_publish: topic: hasp/{plate}/command/json - payload: '["p1b1.text_color={rgb565}","p1b1.text_color"]' + payload: '["p[1].b[1].text_color={rgb565}","p[1].b[1].text_color"]' mqtt_response: topic: hasp/{plate}/state/p1b1 json: diff --git a/test/test_cpicker.tavern.yaml b/test/test_cpicker.tavern.yaml deleted file mode 100644 index 143636bf..00000000 --- a/test/test_cpicker.tavern.yaml +++ /dev/null @@ -1,227 +0,0 @@ -# test_page.tavern.yaml ---- -test_name: Cpicker setup - -includes: - - !include config.yaml - -paho-mqtt: - client: - transport: tcp - client_id: tavern-tester - connect: - host: "{host}" - port: !int "{port:d}" - timeout: 3 - auth: - username: "{username}" - password: "{password}" - -stages: - - name: Page 1 - mqtt_publish: - topic: hasp/{plate}/command - payload: "page 1" - mqtt_response: - topic: hasp/{plate}/state/page - payload: "1" - timeout: 1 - - - name: Clear page - mqtt_publish: - topic: hasp/{plate}/command/clearpage - payload: "" - ---- -test_name: Cpicker loop - -includes: - - !include config.yaml - -paho-mqtt: - client: - transport: tcp - client_id: tavern-tester - connect: - host: "{host}" - port: !int "{port:d}" - timeout: 3 - auth: - username: "{username}" - password: "{password}" - -marks: - - parametrize: - key: - - color - - hex - - r - - g - - b - - rgb565 - vals: - - ["red", "#ff0000", 255, 0, 0, 63488] - - ["tan", "#d2b48c", 213, 182, 139, 54705] - - ["aqua", "#00ffff", 0, 255, 255, 2047] - - ["blue", "#0000ff", 0, 0, 255, 31] - - ["cyan", "#00ffff", 0, 255, 255, 2047] - - ["gold", "#ffd600", 255, 214, 0, 65184] - - ["gray", "#838183", 131, 129, 131, 33808] - - ["grey", "#838183", 131, 129, 131, 33808] - - ["lime", "#00ff00", 0, 255, 0, 2016] - - ["navy", "#000083", 0, 0, 131, 16] - - ["peru", "#cd8139", 205, 129, 57, 52263] - - ["pink", "#ffc2cd", 255, 194, 205, 65049] - - ["plum", "#dea1de", 222, 161, 222, 56603] - - ["snow", "#fffaff", 255, 250, 255, 65503] - - ["teal", "#008183", 0, 129, 131, 1040] - - ["azure", "#f6ffff", 246, 255, 255, 63487] - - ["beige", "#f6f6de", 246, 246, 222, 63419] - - ["black", "#000000", 0, 0, 0, 0] - - ["blush", "#b50000", 181, 0, 0, 45056] - - ["brown", "#a42829", 164, 40, 41, 41285] - - ["coral", "#ff7d52", 255, 125, 82, 64490] - - ["green", "#008100", 0, 129, 0, 1024] - - ["ivory", "#fffff6", 255, 255, 246, 65534] - - ["khaki", "#f6e28b", 246, 226, 139, 63281] - - ["linen", "#fff2ee", 255, 242, 238, 65436] - - ["olive", "#837d00", 131, 125, 0, 33792] - - ["wheat", "#f6dab5", 246, 218, 181, 63222] - - ["white", "#ffffff", 255, 255, 255, 65535] - - ["bisque", "#ffe6c5", 255, 230, 197, 65336] - - ["indigo", "#4a0083", 74, 0, 131, 18448] - - ["maroon", "#830000", 131, 0, 0, 32768] - - ["orange", "#ffa500", 255, 165, 0, 64800] - - ["orchid", "#de71d5", 222, 113, 213, 56218] - - ["purple", "#7b0083", 123, 0, 131, 32784] - - ["salmon", "#ff8173", 255, 129, 115, 64526] - - ["sienna", "#a44c29", 164, 76, 41, 41605] - - ["tomato", "#ff6141", 255, 97, 65, 64264] - - ["violet", "#ee81ee", 230, 129, 238, 60445] - - ["yellow", "#ffff00", 255, 255, 0, 65504] - - ["fuchsia", "#f600ff", 246, 0, 255, 63519] - - ["magenta", "#f600ff", 246, 0, 255, 63519] - - ["silver", "#c5c2c5", 197, 194, 197, 50712] - -stages: - - name: Set bg_color - mqtt_publish: - topic: hasp/{plate}/command - payload: "p[1].b[0].bg_color={color}" - - - name: Create cpicker - mqtt_publish: - topic: hasp/{plate}/command/jsonl - json: - obj: "cpicker" - id: 1 - color: "{color}" - - - name: Test named COLOR - mqtt_publish: - topic: hasp/{plate}/command - payload: "p1b1.color" - mqtt_response: - topic: hasp/{plate}/state/p1b1 - json: - color: "{hex}" - r: !int "{r:d}" - g: !int "{g:d}" - b: !int "{b:d}" - timeout: 1 - - - name: Test hex COLOR - mqtt_publish: - topic: hasp/{plate}/command/json - payload: '["p1b1.color={hex}","p1b1.color"]' - mqtt_response: - topic: hasp/{plate}/state/p1b1 - json: - color: "{hex}" - r: !int "{r:d}" - g: !int "{g:d}" - b: !int "{b:d}" - timeout: 1 - - name: Reset - mqtt_publish: - topic: hasp/{plate}/command - payload: "p1b1.color=529" - - - name: Test rgb565 COLOR - mqtt_publish: - topic: hasp/{plate}/command/json - payload: '["p1b1.color={rgb565}","p1b1.color"]' - mqtt_response: - topic: hasp/{plate}/state/p1b1 - json: - color: "{hex}" - r: !int "{r:d}" - g: !int "{g:d}" - b: !int "{b:d}" - timeout: 1 - ---- -test_name: Value String Loop - -includes: - - !include config.yaml - -paho-mqtt: - client: - transport: tcp - client_id: tavern-tester - connect: - host: "{host}" - port: !int "{port:d}" - timeout: 3 - auth: - username: "{username}" - password: "{password}" - -marks: - - parametrize: - key: str1 - vals: - - "Test 123456 longer strings work too" - - "The cat stretched." - - "Jacob stood on his tiptoes." - - "The car turned the corner." - - "Kelly twirled in circles." - - "She opened the door." - - "Aaron made a picture." - - "I'm sorry." - - huey - - dewey - - "" - - - parametrize: - key: str2 - vals: - - louie - - " I danced." - - " Oak is strong and also gives shade." - - " Cats and dogs each hate the other." - - " The pipe began to rust while new." - - " Open the crate but don't break the glass." - - " Add the sum to the product of these three." - - " Thieves who rob friends deserve jail." - - " The ripe taste of cheese improves with age." - - " Act on these orders with great speed." - - " The hog crawled under the high fence." - - " Move the vat over the hot fire." - - "" - -stages: - - name: Set value_str - mqtt_publish: - topic: "hasp/{plate}/command/p1b1.value_str" - payload: "{str1}{str2}" - - name: Get value_str - mqtt_publish: - topic: hasp/{plate}/command - payload: "p1b1.value_str" - mqtt_response: - topic: hasp/{plate}/state/p1b1 - json: - value_str: "{str1}{str2}" - timeout: 1 diff --git a/test/test_label.tavern.yaml b/test/test_label.tavern.yaml index 0d564d17..207baafe 100644 --- a/test/test_label.tavern.yaml +++ b/test/test_label.tavern.yaml @@ -26,11 +26,13 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 + delay_after: 0.02 - name: Clear page mqtt_publish: topic: hasp/{plate}/command/clearpage payload: "" + delay_after: 0.02 - name: Create object mqtt_publish: @@ -38,12 +40,13 @@ stages: json: objid: 12 id: 1 + delay_after: 0.02 - name: Set x mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.x=50" - + delay_after: 0.02 - name: Get x mqtt_publish: topic: hasp/{plate}/command @@ -71,7 +74,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.y=50" - + delay_after: 0.02 - name: Get y mqtt_publish: topic: hasp/{plate}/command @@ -85,7 +88,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.y=25" - + delay_after: 0.02 - name: Get y mqtt_publish: topic: hasp/{plate}/command @@ -100,7 +103,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.w=50" - + delay_after: 0.02 - name: Get w mqtt_publish: topic: hasp/{plate}/command @@ -128,7 +131,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.h=50" - + delay_after: 0.02 - name: Get h mqtt_publish: topic: hasp/{plate}/command @@ -142,7 +145,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.h=25" - + delay_after: 0.02 - name: Get h mqtt_publish: topic: hasp/{plate}/command @@ -157,7 +160,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.mode=crop" - + delay_after: 0.02 - name: Get mode mqtt_publish: topic: hasp/{plate}/command @@ -167,7 +170,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.w=50" - + delay_after: 0.02 - name: Get w mqtt_publish: topic: hasp/{plate}/command @@ -195,7 +198,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.h=50" - + delay_after: 0.02 - name: Get h mqtt_publish: topic: hasp/{plate}/command @@ -209,7 +212,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.h=45" - + delay_after: 0.02 - name: Get h mqtt_publish: topic: hasp/{plate}/command @@ -224,7 +227,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.enabled=0" - + delay_after: 0.02 - name: Get enabled mqtt_publish: topic: hasp/{plate}/command @@ -238,7 +241,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.enabled=1" - + delay_after: 0.02 - name: Get enabled mqtt_publish: topic: hasp/{plate}/command @@ -253,7 +256,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.hidden=1" - + delay_after: 0.02 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -277,7 +280,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.hidden=0" - + delay_after: 0.02 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -301,7 +304,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.vis=0" - + delay_after: 0.02 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -325,7 +328,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.vis=1" - + delay_after: 0.02 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -344,69 +347,3 @@ stages: json: vis: 1 timeout: 1 - ---- -test_name: Value String Loop - -includes: - - !include config.yaml - -paho-mqtt: - client: - transport: tcp - client_id: tavern-tester - connect: - host: "{host}" - port: !int "{port:d}" - timeout: 3 - auth: - username: "{username}" - password: "{password}" - -marks: - - parametrize: - key: str1 - vals: - - "Test 123456 longer strings work too" - - "The cat stretched." - - "Jacob stood on his tiptoes." - - "The car turned the corner." - - "Kelly twirled in circles." - - "She opened the door." - - "Aaron made a picture." - - "I'm sorry." - - huey - - dewey - - "" - - - parametrize: - key: str2 - vals: - - louie - - " I danced." - - " Oak is strong and also gives shade." - - " Cats and dogs each hate the other." - - " The pipe began to rust while new." - - " Open the crate but don't break the glass." - - " Add the sum to the product of these three." - - " Thieves who rob friends deserve jail." - - " The ripe taste of cheese improves with age." - - " Act on these orders with great speed." - - " The hog crawled under the high fence." - - " Move the vat over the hot fire." - - "" - -stages: - - name: Set value_str - mqtt_publish: - topic: "hasp/{plate}/command/p1b1.value_str" - payload: "{str1}{str2}" - - name: Get value_str - mqtt_publish: - topic: hasp/{plate}/command - payload: "p1b1.value_str" - mqtt_response: - topic: hasp/{plate}/state/p1b1 - json: - value_str: "{str1}{str2}" - timeout: 1 diff --git a/test/test_led.tavern.yaml b/test/test_led.tavern.yaml new file mode 100644 index 00000000..578e21be --- /dev/null +++ b/test/test_led.tavern.yaml @@ -0,0 +1,288 @@ +# test_page.tavern.yaml +--- +test_name: Obj Standard Properties + +includes: + - !include config.yaml + +paho-mqtt: + client: + transport: tcp + client_id: tavern-tester + connect: + host: "{host}" + port: !int "{port:d}" + timeout: 3 + auth: + username: "{username}" + password: "{password}" + +marks: + - parametrize: + key: obj + vals: + - led + - parametrize: + key: + - hidden + - hidden_inv + - x + - y + - w + - h + - radius + - opacity + - str1 + - str2 + vals: + - [1, 0, 120, 121, 122, 123, 0, 255, "I'm sorry.", "louie"] + - [1, 0, -10, -10, 256, 257, 1, 64, "louie", " The cat stretched."] + - [ + 0, + 1, + 1024, + 1025, + 1026, + 1027, + 5, + 0, + "The pipe began to rust while new.", + "", + ] + - [ + 0, + 1, + 80, + 81, + 82, + 83, + 32535, + 192, + "", + " Oak is strong and also gives shade.", + ] + +stages: + - name: Page 1 + mqtt_publish: + topic: hasp/{plate}/command + payload: "page 1" + mqtt_response: + topic: hasp/{plate}/state/page + payload: "1" + timeout: 1 + delay_after: 0 + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" + delay_after: 0.2 + + - name: Create object + mqtt_publish: + topic: hasp/{plate}/command/jsonl + json: + obj: "{obj}" + id: 1 + x: 128 + y: 128 + delay_after: 0 + + - name: Test x + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.x={x}","p1b1.x"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + x: !int "{x:d}" + timeout: 1 + + - name: Test y + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.y={y}","p1b1.y"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + y: !int "{y:d}" + timeout: 1 + + - name: Test w + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.w={w}","p1b1.w"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + w: !int "{w:d}" + timeout: 1 + + - name: Test h + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.h={h}","p1b1.h"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + h: !int "{h:d}" + timeout: 1 + + - name: Test enabled + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.enabled={hidden}","p1b1.enabled"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + enabled: !int "{hidden:d}" + timeout: 1 + + - name: Test click + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.click={hidden}","p1b1.click"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + click: !int "{hidden:d}" + timeout: 1 + + - name: Test swipe + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.swipe={hidden_inv}","p1b1.swipe"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + swipe: !int "{hidden_inv:d}" + timeout: 1 + + - name: Set vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden_inv:d}" + timeout: 1 + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden:d}" + timeout: 1 + + - name: Set hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden:d}" + timeout: 1 + + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden_inv:d}" + timeout: 1 + + - name: Test opacity + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.opacity={opacity}","p1b1.opacity"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + opacity: !int "{opacity:d}" + timeout: 1 + + - name: Test radius + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.radius={radius}","p1b1.radius"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + radius: !int "{radius:d}" + timeout: 1 + + - name: Test radius2 + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.radius2={radius}","p1b1.radius2"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + radius2: !int "{radius:d}" + timeout: 1 + + - name: Set value_str + mqtt_publish: + topic: "hasp/{plate}/command/p1b1.value_str" + payload: "{str1}{str2}" + delay_after: 0.05 + - name: Get value_str + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.value_str" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + value_str: "{str1}{str2}" + timeout: 1 + + - name: Get obj type + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.obj" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + obj: "{obj}" + timeout: 1 + + - name: Test groupid + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.groupid={hidden}","p1b1.groupid"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + groupid: !int "{hidden:d}" + timeout: 1 + + - name: Test val + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.val={opacity}","p1b1.val"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + val: !int "{opacity:d}" + timeout: 1 diff --git a/test/test_mqtt.tavern.yaml b/test/test_mqtt.tavern.yaml index d3bba97c..b5e43aab 100644 --- a/test/test_mqtt.tavern.yaml +++ b/test/test_mqtt.tavern.yaml @@ -1,6 +1,6 @@ # test_page.tavern.yaml --- -test_name: Page command +test_name: Obj Standard Properties includes: - !include config.yaml @@ -26,7 +26,7 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 - + delay_after: 0 - name: step 2 - Page test mqtt_publish: topic: hasp/{plate}/command @@ -35,7 +35,7 @@ stages: topic: hasp/{plate}/state/page payload: "2" timeout: 1 - + delay_after: 0 - name: step 3 - Page test mqtt_publish: topic: hasp/{plate}/command @@ -44,7 +44,7 @@ stages: topic: hasp/{plate}/state/page payload: "3" timeout: 1 - + delay_after: 0 - name: step 4 - Page test mqtt_publish: topic: hasp/{plate}/command/json @@ -53,7 +53,7 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 - + delay_after: 0 - name: step 5 - Page test mqtt_publish: topic: hasp/{plate}/command/json @@ -62,6 +62,7 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 + delay_after: 0 --- test_name: Reboot Command @@ -82,12 +83,6 @@ paho-mqtt: password: "{password}" stages: - - name: Lower idle values - mqtt_publish: - topic: hasp/{plate}/config/gui - json: - idle1: 6 - idle2: 13 - name: Test reboot command mqtt_publish: topic: hasp/{plate}/command/reboot @@ -96,6 +91,7 @@ stages: topic: hasp/{plate}/LWT payload: "offline" timeout: 20 + delay_after: 0 --- test_name: Idle States @@ -122,16 +118,19 @@ stages: payload: "wakeup" mqtt_response: topic: hasp/{plate}/state/idle - payload: "off" - timeout: 5 + payload: "long" + timeout: 190 + delay_after: 0 + + - name: Test idle + mqtt_publish: + topic: hasp/{plate}/command + payload: "wakeup" mqtt_response: topic: hasp/{plate}/state/idle payload: "short" - timeout: 15 - mqtt_response: - topic: hasp/{plate}/state/idle - payload: "long" - timeout: 30 + timeout: 70 + delay_after: 0 - name: Test idle mqtt_publish: @@ -140,4 +139,5 @@ stages: mqtt_response: topic: hasp/{plate}/state/idle payload: "off" - timeout: 5 + timeout: 1 + delay_after: 0 diff --git a/test/test_obj.tavern.yaml b/test/test_obj.tavern.yaml index 44fada5a..4ad63f7f 100644 --- a/test/test_obj.tavern.yaml +++ b/test/test_obj.tavern.yaml @@ -1,34 +1,6 @@ # test_page.tavern.yaml --- -test_name: Objects Standard Properties Setup - -includes: - - !include config.yaml - -paho-mqtt: - client: - transport: tcp - client_id: tavern-tester - connect: - host: "{host}" - port: !int "{port:d}" - timeout: 3 - auth: - username: "{username}" - password: "{password}" - -stages: - - name: Page 1 - mqtt_publish: - topic: hasp/{plate}/command - payload: "page 1" - mqtt_response: - topic: hasp/{plate}/state/page - payload: "1" - timeout: 1 - ---- -test_name: Objects Standard Properties Loop +test_name: Obj Standard Properties includes: - !include config.yaml @@ -58,13 +30,13 @@ marks: - btn - label - slider - - checkbox - - switch + #- checkbox + #- switch - bar - arc - - led + # - led # has a shadow bug - obj - - lmeter + - linemeter - dropdown - calendar - spinner @@ -118,11 +90,13 @@ stages: topic: hasp/{plate}/state/page payload: "1" timeout: 1 + delay_after: 0 - name: Clear page mqtt_publish: topic: hasp/{plate}/command/clearpage payload: "" + delay_after: 0.2 - name: Create object mqtt_publish: @@ -132,6 +106,7 @@ stages: id: 1 x: 128 y: 128 + delay_after: 0 - name: Test y mqtt_publish: @@ -167,7 +142,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command/json payload: '["p1b1.h={h}","p1b1.h"]' - + delay_after: 0 mqtt_response: topic: hasp/{plate}/state/p1b1 json: @@ -178,7 +153,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command/json payload: '["p1b1.enabled={hidden}","p1b1.enabled"]' - + delay_after: 0 mqtt_response: topic: hasp/{plate}/state/p1b1 json: @@ -189,7 +164,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.vis={hidden}" - + delay_after: 0 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -213,7 +188,7 @@ stages: mqtt_publish: topic: hasp/{plate}/command payload: "p1b1.hidden={hidden}" - + delay_after: 0 - name: Get hidden mqtt_publish: topic: hasp/{plate}/command @@ -258,7 +233,7 @@ stages: mqtt_publish: topic: "hasp/{plate}/command/p1b1.value_str" payload: "{str1}{str2}" - + delay_after: 0.05 - name: Get value_str mqtt_publish: topic: hasp/{plate}/command diff --git a/test/test_range.tavern.yaml b/test/test_range.tavern.yaml new file mode 100644 index 00000000..75a2d5c0 --- /dev/null +++ b/test/test_range.tavern.yaml @@ -0,0 +1,91 @@ +# test_page.tavern.yaml +--- +test_name: Obj Standard Properties + +includes: + - !include config.yaml + +paho-mqtt: + client: + transport: tcp + client_id: tavern-tester + connect: + host: "{host}" + port: !int "{port:d}" + timeout: 3 + auth: + username: "{username}" + password: "{password}" + +marks: + - parametrize: + key: obj + vals: + - arc + - bar + - slider + - gauge + - linemeter + - parametrize: + key: + - min + - max + - val + vals: + - [-1000, 10000, 90] + - [128, 512, 256] + - [-15, 0, -5] + +stages: + - name: Page 1 + mqtt_publish: + topic: hasp/{plate}/command + payload: "page 1" + mqtt_response: + topic: hasp/{plate}/state/page + payload: "1" + timeout: 1 + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" + + - name: Create object + mqtt_publish: + topic: hasp/{plate}/command/jsonl + json: + obj: "{obj}" + id: 1 + x: 128 + y: 128 + + - name: Test min + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.min={min}","p1b1.min"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + min: !int "{min:d}" + timeout: 1 + + - name: Test max + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.max={max}","p1b1.max"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + max: !int "{max:d}" + timeout: 1 + + - name: Test val + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.val={val}","p1b1.val"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + val: !int "{val:d}" + timeout: 1 diff --git a/test/test_spinner.tavern.yaml b/test/test_spinner.tavern.yaml new file mode 100644 index 00000000..d2ad657d --- /dev/null +++ b/test/test_spinner.tavern.yaml @@ -0,0 +1,87 @@ +# test_page.tavern.yaml +--- +test_name: Obj Standard Properties + +includes: + - !include config.yaml + +paho-mqtt: + client: + transport: tcp + client_id: tavern-tester + connect: + host: "{host}" + port: !int "{port:d}" + timeout: 3 + auth: + username: "{username}" + password: "{password}" + +marks: + - parametrize: + key: obj + vals: + - spinner + - parametrize: + key: + - speed + - direction + - angle + vals: + - [1000, 0, 90] + - [2000, 1, 120] + - [1500, 0, 30] + +stages: + - name: Page 1 + mqtt_publish: + topic: hasp/{plate}/command + payload: "page 1" + mqtt_response: + topic: hasp/{plate}/state/page + payload: "1" + timeout: 1 + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" + + - name: Create object + mqtt_publish: + topic: hasp/{plate}/command/jsonl + json: + obj: "{obj}" + id: 1 + x: 128 + y: 128 + + - name: Test speed + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.speed={speed}","p1b1.speed"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + speed: !int "{speed:d}" + timeout: 1 + + - name: Test direction + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.direction={direction}","p1b1.direction"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + direction: !int "{direction:d}" + timeout: 1 + + - name: Test angle + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.angle={angle}","p1b1.angle"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + angle: !int "{angle:d}" + timeout: 1 diff --git a/test/test_switch.tavern.yaml b/test/test_switch.tavern.yaml new file mode 100644 index 00000000..6d142de2 --- /dev/null +++ b/test/test_switch.tavern.yaml @@ -0,0 +1,288 @@ +# test_page.tavern.yaml +--- +test_name: Obj Standard Properties + +includes: + - !include config.yaml + +paho-mqtt: + client: + transport: tcp + client_id: tavern-tester + connect: + host: "{host}" + port: !int "{port:d}" + timeout: 3 + auth: + username: "{username}" + password: "{password}" + +marks: + - parametrize: + key: obj + vals: + - switch + - parametrize: + key: + - hidden + - hidden_inv + - x + - y + - w + - h + - radius + - opacity + - str1 + - str2 + vals: + - [1, 0, 120, 121, 122, 123, 0, 255, "I'm sorry.", "louie"] + - [1, 0, -10, -10, 256, 257, 1, 64, "louie", " The cat stretched."] + - [ + 0, + 1, + 1024, + 1025, + 1026, + 1027, + 5, + 0, + "The pipe began to rust while new.", + "", + ] + - [ + 0, + 1, + 80, + 81, + 82, + 83, + 32535, + 192, + "", + " Oak is strong and also gives shade.", + ] + +stages: + - name: Page 1 + mqtt_publish: + topic: hasp/{plate}/command + payload: "page 1" + mqtt_response: + topic: hasp/{plate}/state/page + payload: "1" + timeout: 1 + delay_after: 0 + + - name: Clear page + mqtt_publish: + topic: hasp/{plate}/command/clearpage + payload: "" + delay_after: 0.2 + + - name: Create object + mqtt_publish: + topic: hasp/{plate}/command/jsonl + json: + obj: "{obj}" + id: 1 + x: 128 + y: 128 + delay_after: 0 + + - name: Test x + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.x={x}","p1b1.x"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + x: !int "{x:d}" + timeout: 1 + + - name: Test y + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.y={y}","p1b1.y"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + y: !int "{y:d}" + timeout: 1 + + - name: Test w + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.w={w}","p1b1.w"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + w: !int "{w:d}" + timeout: 1 + + - name: Test h + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.h={h}","p1b1.h"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + h: !int "{h:d}" + timeout: 1 + + - name: Test enabled + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.enabled={hidden}","p1b1.enabled"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + enabled: !int "{hidden:d}" + timeout: 1 + + - name: Test click + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.click={hidden}","p1b1.click"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + click: !int "{hidden:d}" + timeout: 1 + + - name: Test swipe + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.swipe={hidden_inv}","p1b1.swipe"]' + delay_after: 0 + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + swipe: !int "{hidden_inv:d}" + timeout: 1 + + - name: Set vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden_inv:d}" + timeout: 1 + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden:d}" + timeout: 1 + + - name: Set hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden={hidden}" + delay_after: 0 + - name: Get hidden + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.hidden" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + hidden: !int "{hidden:d}" + timeout: 1 + + - name: Get vis + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.vis" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + vis: !int "{hidden_inv:d}" + timeout: 1 + + - name: Test opacity + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.opacity={opacity}","p1b1.opacity"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + opacity: !int "{opacity:d}" + timeout: 1 + + - name: Test radius + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.radius={radius}","p1b1.radius"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + radius: !int "{radius:d}" + timeout: 1 + + - name: Test radius2 + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.radius2={radius}","p1b1.radius2"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + radius2: !int "{radius:d}" + timeout: 1 + + - name: Set value_str + mqtt_publish: + topic: "hasp/{plate}/command/p1b1.value_str" + payload: "{str1}{str2}" + delay_after: 0.05 + - name: Get value_str + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.value_str" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + value_str: "{str1}{str2}" + timeout: 1 + + - name: Get obj type + mqtt_publish: + topic: hasp/{plate}/command + payload: "p1b1.obj" + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + obj: "{obj}" + timeout: 1 + + - name: Test groupid + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.groupid={hidden}","p1b1.groupid"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + groupid: !int "{hidden:d}" + timeout: 1 + + - name: Test val + mqtt_publish: + topic: hasp/{plate}/command/json + payload: '["p1b1.val={hidden}","p1b1.val"]' + mqtt_response: + topic: hasp/{plate}/state/p1b1 + json: + val: !int "{hidden:d}" + timeout: 1 diff --git a/test/test_value_str.tavern.yaml b/test/test_value_str.tavern.yaml index 0db2e70f..90c68f1d 100644 --- a/test/test_value_str.tavern.yaml +++ b/test/test_value_str.tavern.yaml @@ -1,6 +1,6 @@ # test_value_str.tavern.yaml --- -test_name: Value String Setup +test_name: Obj Standard Properties includes: - !include config.yaml @@ -20,15 +20,16 @@ paho-mqtt: stages: - name: Page 1 mqtt_publish: - topic: hasp/{plate}/command/json - payload: '["page 1","clearpage"]' + topic: hasp/{plate}/command + payload: "page 1" mqtt_response: topic: hasp/{plate}/state/page payload: "1" timeout: 1 + delay_after: 0 --- -test_name: Value String Loop +test_name: Obj Standard Properties includes: - !include config.yaml @@ -49,13 +50,13 @@ marks: - parametrize: key: obj vals: - # - cpicker + - cpicker - table - tabview - chart - gauge - btn - # - label + - label - slider - checkbox - switch @@ -63,7 +64,7 @@ marks: - arc - led - obj - - lmeter + - linemeter - dropdown - spinner - roller @@ -102,10 +103,10 @@ marks: - "" stages: - - name: Clear page + - name: Clear Page mqtt_publish: topic: hasp/{plate}/command - payload: clearpage + payload: "clearpage 1" - name: Create object mqtt_publish: @@ -117,15 +118,17 @@ stages: y: 0 w: 240 h: 240 + delay_after: 0 - name: Set value_str mqtt_publish: - topic: "hasp/{plate}/command/p[1].b[1].value_str" - payload: "{str1}{str2}" + topic: "hasp/{plate}/command" + payload: "p1b1.value_str={str1}{str2}" + delay_after: 0 - name: Get value_str mqtt_publish: - topic: hasp/{plate}/command - payload: "p[1].b[1].value_str" + topic: hasp/{plate}/command/p1b1.value_str + payload: "" mqtt_response: topic: hasp/{plate}/state/p1b1 json: diff --git a/tools/littlefsbuilder.py b/tools/littlefsbuilder.py new file mode 100644 index 00000000..3528642d --- /dev/null +++ b/tools/littlefsbuilder.py @@ -0,0 +1,2 @@ +Import("env") +env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/tools/mklittlefs' ) \ No newline at end of file diff --git a/tools/mklittlefs b/tools/mklittlefs new file mode 100644 index 00000000..f2e6c113 Binary files /dev/null and b/tools/mklittlefs differ diff --git a/tools/mklittlefs.exe b/tools/mklittlefs.exe new file mode 100644 index 00000000..aa52570b Binary files /dev/null and b/tools/mklittlefs.exe differ diff --git a/user_setups/darwin_sdl/darwin_sdl_64bits.ini b/user_setups/darwin_sdl/darwin_sdl_64bits.ini index d26c7676..4fe98cc7 100644 --- a/user_setups/darwin_sdl/darwin_sdl_64bits.ini +++ b/user_setups/darwin_sdl/darwin_sdl_64bits.ini @@ -6,7 +6,7 @@ extra_scripts = tools/linux_build_extra.py build_flags = ${env.build_flags} - -D HASP_MODEL="MacOS X" + -D HASP_MODEL="MacOS X App" ; ----- Monitor -D TFT_WIDTH=240 @@ -26,11 +26,14 @@ build_flags = -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=0 + -D HASP_USE_GPIO=1 -D HASP_USE_CONFIG=0 ; Standalone application, as library -D HASP_USE_DEBUG=1 -D HASP_USE_MQTT=1 -D MQTT_MAX_PACKET_SIZE=2048 + -D HASP_ATTRIBUTE_FAST_MEM= + -D IRAM_ATTR= ; No IRAM_ATTR available + -D PROGMEM= ; No PROGMEM available ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO ;-D LV_LOG_PRINTF=1 ; Add recursive dirs for hal headers search @@ -86,6 +89,7 @@ src_filter = - - - + + - + - diff --git a/user_setups/esp32/arduitouch-esp32_ili9341.ini b/user_setups/esp32/arduitouch-esp32_ili9341.ini index f822bb65..40bbb440 100644 --- a/user_setups/esp32/arduitouch-esp32_ili9341.ini +++ b/user_setups/esp32/arduitouch-esp32_ili9341.ini @@ -12,7 +12,7 @@ board = esp32dev build_flags = ${env.build_flags} ${esp32.build_flags} - -D HASP_MODEL="Arduitouch" + -D HASP_MODEL="Arduitouch ESP32" ;region -- TFT_eSPI build options ------------------------ ${lcd.lolin24} diff --git a/user_setups/esp32/d1-mini-esp32_ili9341.ini b/user_setups/esp32/d1-mini-esp32_ili9341.ini index 8746bd98..3f32e022 100644 --- a/user_setups/esp32/d1-mini-esp32_ili9341.ini +++ b/user_setups/esp32/d1-mini-esp32_ili9341.ini @@ -12,6 +12,7 @@ board = wemos_d1_mini32 build_flags = ${env.build_flags} ${esp32.build_flags} + -D HASP_MODEL="ESP32 D1 Mini" ;region -- TFT_eSPI build options ------------------------ ${lcd.lolin24} diff --git a/user_setups/esp32/esp32-touchdown.ini b/user_setups/esp32/esp32-touchdown.ini index d8bb6c3c..f02387e0 100644 --- a/user_setups/esp32/esp32-touchdown.ini +++ b/user_setups/esp32/esp32-touchdown.ini @@ -12,7 +12,7 @@ build_flags = ${env.build_flags} ${esp32.build_flags} ${esp32.vspi} ; Use VSPI hardware SPI bus - -D HASP_MODEL="ESP32-Touchdown" + -D HASP_MODEL="ESP32 Touchdown" ;region -- TFT_eSPI build options ------------------------ -D USER_SETUP_LOADED=1 diff --git a/user_setups/esp32/huzzah32-featherwing-24.ini b/user_setups/esp32/huzzah32-featherwing-24.ini index ed93df51..ecbb68e0 100644 --- a/user_setups/esp32/huzzah32-featherwing-24.ini +++ b/user_setups/esp32/huzzah32-featherwing-24.ini @@ -12,7 +12,7 @@ board = featheresp32 build_flags = ${env.build_flags} ${esp32.build_flags} - -D HASP_MODEL="Featherwing 2.4" + -D HASP_MODEL="Adafruit Featherwing 2.4" ;region -- TFT_eSPI build options ------------------------ ${lcd.featherwing-24} diff --git a/user_setups/esp32/huzzah32-featherwing-35.ini b/user_setups/esp32/huzzah32-featherwing-35.ini index 1734b376..27775bf6 100644 --- a/user_setups/esp32/huzzah32-featherwing-35.ini +++ b/user_setups/esp32/huzzah32-featherwing-35.ini @@ -12,7 +12,7 @@ board = featheresp32 build_flags = ${env.build_flags} ${esp32.build_flags} - -D HASP_MODEL="Featherwing 3.5" + -D HASP_MODEL="Adafruit Featherwing 3.2" ;region -- TFT_eSPI build options ------------------------ ${lcd.featherwing-35} @@ -22,7 +22,7 @@ build_flags = -D TFT_DC=33 -D TFT_CS=15 -D TFT_RST=-1 ; RST - -D TFT_BCKL=-1 ; Solder the LITE pad to a PWM enabled pin of the ESP. + -D TFT_BCKL=21 ; Solder the LITE pad to a PWM enabled pin of the ESP, like GPIO 21 -D STMPE_CS=32 ;endregion diff --git a/user_setups/esp32/lanbon_l8.ini b/user_setups/esp32/lanbon_l8.ini index 0fad7ef0..6c8e6606 100644 --- a/user_setups/esp32/lanbon_l8.ini +++ b/user_setups/esp32/lanbon_l8.ini @@ -8,10 +8,9 @@ [env:lanbon_l8] extends = esp32 board = esp32dev - board_upload.flash_size=8MB board_upload.maximum_size = 8388608 -board_build.partitions = user_setups/esp32/partition_app2000k_spiffs4000k.csv +board_build.partitions = user_setups/esp32/partitions_8MB.csv build_flags = ${env.build_flags} diff --git a/user_setups/esp32/lolin-d32-pro_ili9341.ini b/user_setups/esp32/lolin-d32-pro_ili9341.ini index a3fb65ce..83a53575 100644 --- a/user_setups/esp32/lolin-d32-pro_ili9341.ini +++ b/user_setups/esp32/lolin-d32-pro_ili9341.ini @@ -10,15 +10,15 @@ [env:lolin-d32-pro_ili9341] extends = esp32 board = lolin_d32_pro - board_upload.flash_size=16MB board_upload.maximum_size = 16777216 -board_build.partitions = user_setups/esp32/partition_app2000k_spiffs4000k.csv +board_build.partitions = user_setups/esp32/partitions_16MB.csv build_flags = ${env.build_flags} ${esp32.build_flags} ${esp32.ps_ram} + -D HASP_MODEL="Lolin D32 Pro" ;region -- TFT_eSPI build options ------------------------ ${lcd.lolin24} diff --git a/user_setups/esp32/m5stack_core2.ini b/user_setups/esp32/m5stack_core2.ini index 9ca1a8b4..bf336aec 100644 --- a/user_setups/esp32/m5stack_core2.ini +++ b/user_setups/esp32/m5stack_core2.ini @@ -8,16 +8,14 @@ [env:m5stack-core2] extends = esp32 board = esp32dev - board_upload.flash_size=16MB board_upload.maximum_size = 16777216 -board_build.partitions = user_setups/esp32/partition_app2000k_spiffs4000k.csv +board_build.partitions = user_setups/esp32/partitions_16MB.csv build_flags = ${env.build_flags} ${esp32.build_flags} ${esp32.ps_ram} - -D HASP_MODEL="M5Stack core2" ;region -- TFT_eSPI build options ------------------------ ${lcd.m5stack} diff --git a/user_setups/esp32/partition_app1280k_spiffs1472k.csv b/user_setups/esp32/partition_app1280k_spiffs1472k.csv deleted file mode 100644 index 47367800..00000000 --- a/user_setups/esp32/partition_app1280k_spiffs1472k.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size,Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xE000, 0x2000, -app0, app, ota_0, 0x10000, 0x140000, -app1, app, ota_1, 0x150000, 0x140000, -spiffs, data, spiffs, 0x290000, 0x170000, diff --git a/user_setups/esp32/partition_app1300k_spiffs1216k.csv b/user_setups/esp32/partition_app1300k_spiffs1216k.csv deleted file mode 100644 index d361047a..00000000 --- a/user_setups/esp32/partition_app1300k_spiffs1216k.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x160000, -app1, app, ota_1, 0x170000, 0x160000, -spiffs, data, spiffs, 0x2D0000, 0x130000, diff --git a/user_setups/esp32/partition_app1536k_spiffs1024k.csv b/user_setups/esp32/partition_app1536k_spiffs1024k.csv deleted file mode 100644 index 414702e0..00000000 --- a/user_setups/esp32/partition_app1536k_spiffs1024k.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x180000, -app1, app, ota_1, 0x190000, 0x180000, -spiffs, data, spiffs, 0x310000, 0x0F0000, diff --git a/user_setups/esp32/partition_app1704k_spiffs720k.csv b/user_setups/esp32/partition_app1704k_spiffs720k.csv deleted file mode 100644 index cd96d227..00000000 --- a/user_setups/esp32/partition_app1704k_spiffs720k.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x1A0000, -app1, app, ota_1, 0x1B0000, 0x1A0000, -spiffs, data, spiffs, 0x350000, 0x0B0000, diff --git a/user_setups/esp32/partition_app2000k_spiffs4000k.csv b/user_setups/esp32/partition_app2000k_spiffs4000k.csv deleted file mode 100644 index 0ada1ac6..00000000 --- a/user_setups/esp32/partition_app2000k_spiffs4000k.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x1F0000, -app1, app, ota_1, 0x200000, 0x1F0000, -spiffs, data, spiffs, 0x3F0000, 0x410000, diff --git a/user_setups/esp32/partitions_16MB.csv b/user_setups/esp32/partitions_16MB.csv new file mode 100644 index 00000000..45bb999d --- /dev/null +++ b/user_setups/esp32/partitions_16MB.csv @@ -0,0 +1,9 @@ +# Two application partitions of 2.0 MB +# Filesystem: ~12 MB +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 36K, 20K, +otadata, data, ota, 56K, 8K, +app0, app, ota_0, 64K, 2048K, +app1, app, ota_1, 2112K, 2048K, +spiffs, data, spiffs, 4160K, 12224K, diff --git a/user_setups/esp32/partitions_2MB.csv b/user_setups/esp32/partitions_2MB.csv new file mode 100644 index 00000000..19840001 --- /dev/null +++ b/user_setups/esp32/partitions_2MB.csv @@ -0,0 +1,8 @@ +# One single application partition of 1.625 MB +# 320 kB filesystem +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 36K, 20K, +otadata, data, ota, 56K, 8K, +app0, app, ota_0, 64K, 1664K, +spiffs, data, spiffs, 1728K, 320K, diff --git a/user_setups/esp32/partitions_32MB.csv b/user_setups/esp32/partitions_32MB.csv new file mode 100644 index 00000000..d3cfde77 --- /dev/null +++ b/user_setups/esp32/partitions_32MB.csv @@ -0,0 +1,9 @@ +# Two application partitions of 2.0 MB +# Filesystem: ~28 MB +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 36K, 20K, +otadata, data, ota, 56K, 8K, +app0, app, ota_0, 64K, 2048K, +app1, app, ota_1, 2112K, 2048K, +spiffs, data, spiffs, 4160K, 28608K, diff --git a/user_setups/esp32/partitions_4MB.csv b/user_setups/esp32/partitions_4MB.csv new file mode 100644 index 00000000..733ff0c5 --- /dev/null +++ b/user_setups/esp32/partitions_4MB.csv @@ -0,0 +1,9 @@ +# Two application partitions of 1664 kB +# 704 kB filesystem +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 36K, 20K, +otadata, data, ota, 56K, 8K, +app0, app, ota_0, 64K, 1664K, +app1, app, ota_1, 1728K, 1664K, +spiffs, data, spiffs, 3392K, 704K, diff --git a/user_setups/esp32/partitions_8MB.csv b/user_setups/esp32/partitions_8MB.csv new file mode 100644 index 00000000..741b8fb2 --- /dev/null +++ b/user_setups/esp32/partitions_8MB.csv @@ -0,0 +1,9 @@ +# Two application partitions of 2.0 MB +# Filesystem: ~4 MB +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 36K, 20K, +otadata, data, ota, 56K, 8K, +app0, app, ota_0, 64K, 2048K, +app1, app, ota_1, 2112K, 2048K, +spiffs, data, spiffs, 4160K, 4032K, diff --git a/user_setups/esp32/ttgo-esp32-lilygo-pi.ini b/user_setups/esp32/ttgo-esp32-lilygo-pi.ini index 13420a45..53ea2d09 100644 --- a/user_setups/esp32/ttgo-esp32-lilygo-pi.ini +++ b/user_setups/esp32/ttgo-esp32-lilygo-pi.ini @@ -8,16 +8,14 @@ [env:ttgo-esp32-lilygo-pi] extends = esp32 board = esp32dev - board_upload.flash_size=16MB board_upload.maximum_size = 16777216 -board_build.partitions = user_setups/esp32/partition_app2000k_spiffs4000k.csv +board_build.partitions = user_setups/esp32/partitions_16MB.csv build_flags = ${env.build_flags} ${esp32.build_flags} ${esp32.ps_ram} - -D HASP_MODEL="TTGO Lilygo Pi" ;region -- TFT_eSPI build options ------------------------ -D ILI9481_DRIVER=1 diff --git a/user_setups/esp32/wt32-sc01.ini b/user_setups/esp32/wt32-sc01.ini index 6dfeb9db..7b489c3f 100644 --- a/user_setups/esp32/wt32-sc01.ini +++ b/user_setups/esp32/wt32-sc01.ini @@ -13,7 +13,6 @@ build_flags = ${env.build_flags} ${esp32.build_flags} ${esp32.ps_ram} - -D HASP_MODEL="WT32-SC01" ;region -- TFT_eSPI build options ------------------------ ${lcd.wt32-sc01} diff --git a/user_setups/linux_sdl/linux_sdl_64bits.ini b/user_setups/linux_sdl/linux_sdl_64bits.ini index 7750398d..34017d30 100644 --- a/user_setups/linux_sdl/linux_sdl_64bits.ini +++ b/user_setups/linux_sdl/linux_sdl_64bits.ini @@ -5,7 +5,7 @@ extra_scripts = tools/linux_build_extra.py build_flags = ${env.build_flags} - -D HASP_MODEL="Posix Linux" + -D HASP_MODEL="Linux App" ; ----- Monitor -D TFT_WIDTH=240 @@ -25,11 +25,15 @@ build_flags = -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=0 + -D HASP_USE_GPIO=1 -D HASP_USE_CONFIG=0 ; Standalone application, as library -D HASP_USE_DEBUG=1 + -D HASP_USE_PNGDECODE=1 -D HASP_USE_MQTT=1 -D MQTT_MAX_PACKET_SIZE=2048 + -D HASP_ATTRIBUTE_FAST_MEM= + -D IRAM_ATTR= ; No IRAM_ATTR available + -D PROGMEM= ; No PROGMEM available ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO ;-D LV_LOG_PRINTF=1 ; Add recursive dirs for hal headers search @@ -43,8 +47,7 @@ build_flags = ;-D NO_PERSISTENCE -I.pio/libdeps/linux_sdl_64bits/paho/src -I.pio/libdeps/linux_sdl_64bits/ArduinoJson/src - -I lib/ArduinoJson/src - -I lib/lv_fs_if + !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" ; ----- Statically linked libraries -------------------- -lSDL2 @@ -58,12 +61,12 @@ lib_deps = https://github.com/eclipse/paho.mqtt.c.git bblanchon/ArduinoJson@^6.17.2 ; Json(l) parser https://github.com/fvanroie/lv_drivers + git+https://github.com/lvgl/lv_lib_png.git lib_ignore = paho AXP192 ArduinoLog - lv_fs_if src_filter = +<*> @@ -81,6 +84,7 @@ src_filter = - - - + + - + - diff --git a/user_setups/win32/windows_sdl_64bits.ini b/user_setups/win32/windows_sdl_64bits.ini index 40de8761..b7b94638 100644 --- a/user_setups/win32/windows_sdl_64bits.ini +++ b/user_setups/win32/windows_sdl_64bits.ini @@ -5,7 +5,7 @@ extra_scripts = tools/windows_build_extra.py build_flags = ${env.build_flags} - -D HASP_MODEL="Windows" + -D HASP_MODEL="Windows App" ; ----- Monitor -D TFT_WIDTH=240 @@ -25,11 +25,15 @@ build_flags = -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=0 + -D HASP_USE_GPIO=1 -D HASP_USE_CONFIG=0 ; Standalone application, as library -D HASP_USE_DEBUG=1 + -D HASP_USE_PNGDECODE=1 -D HASP_USE_MQTT=1 -D MQTT_MAX_PACKET_SIZE=2048 + -D HASP_ATTRIBUTE_FAST_MEM= + -D IRAM_ATTR= ; No IRAM_ATTR available + -D PROGMEM= ; No PROGMEM available ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO ;-D LV_LOG_PRINTF=1 ; Add recursive dirs for hal headers search @@ -45,7 +49,6 @@ build_flags = ;-D NO_PERSISTENCE -I.pio/libdeps/windows_sdl_64bits/paho/src -I.pio/libdeps/windows_sdl_64bits/ArduinoJson/src - -I lib/ArduinoJson/src -I lib/lv_fs_if !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" -mconsole @@ -80,6 +83,7 @@ lib_deps = https://github.com/eclipse/paho.mqtt.c.git bblanchon/ArduinoJson@^6.17.2 ; Json(l) parser https://github.com/fvanroie/lv_drivers + git+https://github.com/lvgl/lv_lib_png.git lib_ignore = paho @@ -102,6 +106,7 @@ src_filter = - - - + + - + -