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 =
"";
const char HTTP_END[] PROGMEM = "
";
+const char HTTP_FOOTER[] PROGMEM = " " D_HTTP_FOOTER "