diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ee3d7f48..93ab81f7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -154,7 +154,7 @@ jobs: - name: Enable Linux platform from platformio_override.ini run: | sed 's/; user_setups\/linux/user_setups\/linux/g' platformio_override-template.ini > platformio_override.ini - mkdir -p .pio/libdeps/linux_sdl_64bits/paho/src + mkdir -p .pio/libdeps/linux_sdl/paho/src - name: Install SDL2 library run: | sudo apt-get update @@ -168,10 +168,10 @@ jobs: - name: Enable Linux platform from platformio_override.ini run: | sed -i 's/; user_setups\/linux/user_setups\/linux/g' platformio_override.ini - mkdir -p .pio/libdeps/linux_sdl_64bits/paho/src + mkdir -p .pio/libdeps/linux_sdl/paho/src - name: Install SDL2 library run: | sudo apt-get update sudo apt-get install libsdl2-dev - name: Run PlatformIO - run: pio run -e linux_sdl_64bits + run: pio run -e linux_sdl diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml index bfe4ede4..cd92d6be 100644 --- a/.github/workflows/build_linux.yaml +++ b/.github/workflows/build_linux.yaml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: environments: - - linux_sdl_64bits + - linux_sdl steps: - uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: - name: Enable Linux platform from platformio_override.ini run: | sed -i 's/; user_setups\/linux/user_setups\/linux/g' platformio_override.ini - mkdir -p .pio/libdeps/linux_sdl_64bits/paho/src + mkdir -p .pio/libdeps/linux_sdl/paho/src - name: Install SDL2 library run: | sudo apt-get update diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f4d093be..ed1ce258 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: - name: Enable Linux platform from platformio_override.ini run: | sed -i 's/; user_setups\/linux/user_setups\/linux/g' platformio_override.ini - mkdir -p .pio/libdeps/linux_sdl_64bits/paho/src + mkdir -p .pio/libdeps/linux_sdl/paho/src - name: Install SDL2 library run: | sudo apt-get update diff --git a/hal/sdl2/app_hal.c b/hal/sdl2/app_hal.c index fd062ada..c7524836 100644 --- a/hal/sdl2/app_hal.c +++ b/hal/sdl2/app_hal.c @@ -1,3 +1,4 @@ +#if USE_MONITOR #include #define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/ #include @@ -53,3 +54,4 @@ void hal_loop(void) // lv_task_handler(); // } } +#endif diff --git a/include/hasp_conf.h b/include/hasp_conf.h index 448b66a3..d7b666cb 100644 --- a/include/hasp_conf.h +++ b/include/hasp_conf.h @@ -33,6 +33,11 @@ #define HASP_USE_APP 1 +/* Validate that build target was specified */ +#if HASP_TARGET_ARDUINO + HASP_TARGET_PC != 1 +#error "Build target invalid! Set *one* of: HASP_TARGET_ARDUINO, HASP_TARGET_PC" +#endif + #ifndef HASP_USE_DEBUG #define HASP_USE_DEBUG 1 #endif @@ -65,6 +70,10 @@ #define HASP_USE_MQTT (HASP_HAS_NETWORK) #endif +#ifndef HASP_USE_MQTT_ASYNC +#define HASP_USE_MQTT_ASYNC (HASP_TARGET_PC) +#endif + #ifndef HASP_USE_WIREGUARD #define HASP_USE_WIREGUARD (HASP_HAS_NETWORK) #endif @@ -190,6 +199,46 @@ #define HASP_OBJECT_NOTATION "p%ub%u" +#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 + +#ifndef PGM_P +#define PGM_P const char* +#endif + +/* Workarounds for PC build */ +#if HASP_TARGET_PC +#ifndef __FlashStringHelper +typedef char __FlashStringHelper; +#endif + +#if defined(__cplusplus) && !defined(String) +#include +using String = std::string; +#endif + +#ifndef F +#define F(x) (x) +#endif + +#ifndef PSTR +#define PSTR(x) x +#endif + +#ifndef PROGMEM +#define PROGMEM +#endif +#endif + /* Includes */ #ifdef WINDOWS #include "winsock2.h" @@ -281,7 +330,7 @@ static WiFiSpiClass WiFi; #if HASP_USE_MQTT > 0 #include "mqtt/hasp_mqtt.h" -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #define HASP_USE_PAHO #else #define HASP_USE_ESP_MQTT @@ -326,52 +375,27 @@ 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 - -#ifndef PGM_P -#define PGM_P const char* -#endif - -#if defined(WINDOWS) || defined(POSIX) -#ifndef __FlashStringHelper -#define __FlashStringHelper char -#endif - -#ifndef F -#define F(x) (x) -#endif - -#ifndef PSTR -#define PSTR(x) x -#endif - -#ifndef PROGMEM -#define PROGMEM -#endif -#endif - #if defined(WINDOWS) #include #define delay Sleep #endif + #if defined(POSIX) +#ifdef USE_MONITOR #define delay SDL_Delay +#else +#define delay msleep #endif -#if defined(WINDOWS) || defined(POSIX) +#endif + +#if HASP_TARGET_PC #include #include #include + +#if USE_MONITOR #include +#endif #define snprintf_P snprintf #define memcpy_P memcpy @@ -380,7 +404,13 @@ static WiFiSpiClass WiFi; #define strcpy_P strcpy #define strstr_P strstr #define halRestartMcu() +#if USE_MONITOR #define millis SDL_GetTicks +#elif defined(WINDOWS) +#define millis Win32Millis +#elif defined(POSIX) +#define millis PosixMillis +#endif #define DEC 10 #define HEX 16 diff --git a/include/hasp_macro.h b/include/hasp_macro.h index f1f937b9..7dc1f82b 100644 --- a/include/hasp_macro.h +++ b/include/hasp_macro.h @@ -12,15 +12,15 @@ #include "user_config_override.h" #endif -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #define HASP_RANDOM(x) rand() % x -#elif defined(ARDUINO) +#elif HASP_TARGET_ARDUINO #define HASP_RANDOM(x) random(x) #else #define HASP_RANDOM(x) random() % x #endif -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #define LOG_OUTPUT(x, ...) printf(__VA_ARGS__) #else diff --git a/include/lv_conf_v7.h b/include/lv_conf_v7.h index 0b094c3f..e2adf67e 100644 --- a/include/lv_conf_v7.h +++ b/include/lv_conf_v7.h @@ -227,7 +227,9 @@ typedef void* lv_fs_drv_user_data_t; //# define LV_FS_IF_SPIFFS '\0' // no internal esp Flash #endif #endif /*LV_USE_FS_IF*/ +#if HASP_TARGET_ARDUINO #define LV_FS_PC_PATH "/littlefs" +#endif #endif diff --git a/include/lv_drv_conf.h b/include/lv_drv_conf.h index cb5bd603..3afc0d92 100644 --- a/include/lv_drv_conf.h +++ b/include/lv_drv_conf.h @@ -125,7 +125,11 @@ #define USE_WINDOWS 0 #endif -#if USE_WINDOWS +#ifndef USE_WIN32DRV +#define USE_WINDOWS 0 +#endif + +#if USE_WINDOWS || USE_WIN32DRV #define WINDOW_HOR_RES 480 #define WINDOW_VER_RES 320 #endif diff --git a/lib/lv_lib_zifont/lv_zifont.cpp b/lib/lv_lib_zifont/lv_zifont.cpp index 43f7e340..3db55627 100644 --- a/lib/lv_lib_zifont/lv_zifont.cpp +++ b/lib/lv_lib_zifont/lv_zifont.cpp @@ -5,7 +5,7 @@ * INCLUDES *********************/ -#if !(defined(WINDOWS) || defined(POSIX) || defined(STM32F7xx)) +#if !(HASP_TARGET_PC || defined(STM32F7xx)) #include #include diff --git a/platformio_override-template.ini b/platformio_override-template.ini index 941fe815..f746bb26 100644 --- a/platformio_override-template.ini +++ b/platformio_override-template.ini @@ -6,11 +6,11 @@ [platformio] extra_configs = ; Uncomment or edit the lines to show more User Setups in the PIO sidebar - ; user_setups/darwin_sdl/*.ini + ; user_setups/darwin/*.ini ; user_setups/esp32/*.ini ; user_setups/esp32s2/*.ini ; user_setups/esp32s3/*.ini - ; user_setups/linux_sdl/*.ini + ; user_setups/linux/*.ini ; user_setups/stm32f4xx/*.ini ; user_setups/win32/*.ini diff --git a/src/dev/device.h b/src/dev/device.h index 32ad1dc7..a17ef6dd 100644 --- a/src/dev/device.h +++ b/src/dev/device.h @@ -8,7 +8,7 @@ #include "Arduino.h" #endif -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #include #endif #if defined(POSIX) @@ -30,6 +30,9 @@ class BaseDevice { public: bool has_battery = false; bool has_backligth_control = true; +#if HASP_TARGET_PC + bool pc_is_running = true; +#endif virtual void reboot() {} diff --git a/src/dev/posix/hasp_posix.cpp b/src/dev/posix/hasp_posix.cpp index a70ff4e2..ad020e4b 100644 --- a/src/dev/posix/hasp_posix.cpp +++ b/src/dev/posix/hasp_posix.cpp @@ -19,7 +19,15 @@ #include "hasp_conf.h" #include "hasp_debug.h" +#ifdef USE_MONITOR #include "display/monitor.h" +#elif USE_FBDEV +#include "display/fbdev.h" +#include "drv/tft/tft_driver.h" +#endif + +#include +#include // extern monitor_t monitor; @@ -35,12 +43,6 @@ PosixDevice::PosixDevice() _core_version = "unknown"; _chip_model = "unknown"; } else { - // LOG_VERBOSE(0,"Sysname: %s", uts.sysname); - // LOG_VERBOSE(0,"Nodename: %s", uts.nodename); - // LOG_VERBOSE(0,"Release: %s", uts.release); - // LOG_VERBOSE(0,"Version: %s", uts.version); - // LOG_VERBOSE(0,"Machine: %s", uts.machine); - char version[256]; snprintf(version, sizeof(version), "%s %s", uts.sysname, uts.release); _core_version = version; @@ -53,6 +55,32 @@ PosixDevice::PosixDevice() _backlight_level = 255; } +void PosixDevice::set_config(const JsonObject& settings) +{ + configOutput(settings, 0); +#if USE_FBDEV + if(settings["fbdev"].is()) { + haspTft.fbdev_path = "/dev/" + settings["fbdev"].as(); + } +#if USE_EVDEV + if(settings["evdev"].is()) { + haspTft.evdev_names.push_back(settings["evdev"].as()); + } + if(settings["evdevs"].is()) { + for(auto v : settings["evdevs"].as()) { + haspTft.evdev_names.push_back(v.as()); + } + } +#endif + if(settings["bldev"].is()) { + haspDevice.backlight_device = settings["bldev"].as(); + } + if(settings["blmax"].is()) { + haspDevice.backlight_max = settings["blmax"]; + } +#endif +} + void PosixDevice::reboot() {} void PosixDevice::show_info() @@ -62,15 +90,16 @@ void PosixDevice::show_info() if(uname(&uts) < 0) { LOG_ERROR(0, "uname() error"); } else { - LOG_VERBOSE(0, "Sysname: %s", uts.sysname); - LOG_VERBOSE(0, "Nodename: %s", uts.nodename); - LOG_VERBOSE(0, "Release: %s", uts.release); - LOG_VERBOSE(0, "Version: %s", uts.version); - LOG_VERBOSE(0, "Machine: %s", uts.machine); + LOG_VERBOSE(0, "Sysname : %s", uts.sysname); + LOG_VERBOSE(0, "Nodename : %s", uts.nodename); + LOG_VERBOSE(0, "Release : %s", uts.release); + LOG_VERBOSE(0, "Version : %s", uts.version); + LOG_VERBOSE(0, "Machine : %s", uts.machine); } - LOG_VERBOSE(0, "Processor : %s", "unknown"); - LOG_VERBOSE(0, "CPU freq. : %i MHz", 0); + LOG_VERBOSE(0, "Processor : %s", get_chip_model()); + LOG_VERBOSE(0, "CPU freq. : %i MHz", get_cpu_frequency()); + LOG_VERBOSE(0, "OS Version : %s", get_core_version()); } const char* PosixDevice::get_hostname() @@ -81,8 +110,11 @@ const char* PosixDevice::get_hostname() void PosixDevice::set_hostname(const char* hostname) { _hostname = hostname; +#if USE_MONITOR monitor_title(hostname); - // SDL_SetWindowTitle(monitor.window, hostname); +#elif USE_FBDEV + // fbdev doesn't really have a title bar +#endif } const char* PosixDevice::get_core_version() @@ -146,13 +178,35 @@ void PosixDevice::update_backlight() { uint8_t level = _backlight_power ? _backlight_level : 0; if(_backlight_invert) level = 255 - level; +#if USE_MONITOR monitor_backlight(level); - // SDL_SetTextureColorMod(monitor.texture, level, level, level); - // window_update(&monitor); - // monitor.sdl_refr_qry = true; - // monitor_sdl_refr(NULL); - // const lv_area_t area = {1,1,0,0}; - // monitor_flush(NULL,&area,NULL); +#elif USE_FBDEV + // set display backlight, if possible + if(backlight_device != "") { + if(backlight_max == 0) { + std::ifstream f; + f.open("/sys/class/backlight/" + backlight_device + "/max_brightness"); + if(!f.fail()) { + f >> backlight_max; + f.close(); + } else { + perror("Max brightness read failed"); + } + } + + int brightness = map(level, 0, 255, 0, backlight_max); + LOG_VERBOSE(0, "Setting brightness to %d/255 (%d)", level, brightness); + + std::ofstream f; + f.open("/sys/class/backlight/" + backlight_device + "/brightness"); + if(!f.fail()) { + f << brightness; + f.close(); + } else { + perror("Brightness write failed (are you root?)"); + } + } +#endif } size_t PosixDevice::get_free_max_block() @@ -192,6 +246,12 @@ bool PosixDevice::is_system_pin(uint8_t pin) return false; } +void PosixDevice::run_thread(void (*func)(void*), void* arg) +{ + pthread_t thread; + pthread_create(&thread, NULL, (void* (*)(void*))func, arg); +} + #ifndef TARGET_OS_MAC long PosixDevice::get_uptime() { @@ -221,6 +281,25 @@ long PosixDevice::get_uptime() } // namespace dev +static time_t tv_sec_start = 0; + +unsigned long PosixMillis() +{ + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + if(tv_sec_start == 0) { + tv_sec_start = spec.tv_sec; + } + unsigned long msec1 = (spec.tv_sec - tv_sec_start) * 1000; + unsigned long msec2 = spec.tv_nsec / 1e6; + return msec1 + msec2; +} + +void msleep(unsigned long millis) +{ + usleep(millis * 1000); +} + dev::PosixDevice haspDevice; #endif // POSIX diff --git a/src/dev/posix/hasp_posix.h b/src/dev/posix/hasp_posix.h index 9dc8ad9f..70f62695 100644 --- a/src/dev/posix/hasp_posix.h +++ b/src/dev/posix/hasp_posix.h @@ -31,6 +31,8 @@ class PosixDevice : public BaseDevice { public: PosixDevice(); + void set_config(const JsonObject& settings); + void reboot() override; void show_info() override; @@ -56,6 +58,12 @@ class PosixDevice : public BaseDevice { bool is_system_pin(uint8_t pin) override; + void run_thread(void (*func)(void*), void* arg); + + public: + std::string backlight_device; + int backlight_max = 0; + private: std::string _hostname; std::string _core_version; @@ -71,6 +79,9 @@ class PosixDevice : public BaseDevice { } // namespace dev +extern unsigned long PosixMillis(); +extern void msleep(unsigned long millis); + using dev::PosixDevice; extern dev::PosixDevice haspDevice; diff --git a/src/dev/win32/hasp_win32.cpp b/src/dev/win32/hasp_win32.cpp index 61fce87d..591ccb75 100644 --- a/src/dev/win32/hasp_win32.cpp +++ b/src/dev/win32/hasp_win32.cpp @@ -11,7 +11,11 @@ #include "hasp_conf.h" #include "hasp_debug.h" +#if USE_MONITOR #include "display/monitor.h" +#elif USE_WIN32DRV +#include "win32drv/win32drv.h" +#endif namespace dev { @@ -21,11 +25,33 @@ static inline void native_cpuid(unsigned int* eax, unsigned int* ebx, unsigned i asm volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "0"(*eax), "2"(*ecx) : "memory"); } -void Win32Device::reboot() -{} - -void Win32Device::show_info() +Win32Device::Win32Device() { + char buffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD length = sizeof(buffer); + + if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNameNetBIOS, buffer, &length)) { + _hostname = buffer; + } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNameDnsHostname, buffer, &length)) { + _hostname = buffer; + } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNamePhysicalDnsHostname, buffer, &length)) { + _hostname = buffer; + } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNamePhysicalDnsDomain, buffer, &length)) { + _hostname = buffer; + } else { + _hostname = "localhost"; + } + + // Get the Windows version. + DWORD dwBuild = 0; + DWORD dwVersion = GetVersion(); + DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); + DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); + if(dwVersion < 0x80000000) dwBuild = (DWORD)(HIWORD(dwVersion)); + + char version[128]; + snprintf(version, sizeof(version), "Windows %d.%d.%d", dwMajorVersion, dwMinorVersion, dwBuild); + _core_version = version; unsigned int eax, ebx, ecx, edx; eax = 0; @@ -35,10 +61,23 @@ void Win32Device::show_info() memcpy(vendor, &ebx, 4); memcpy(vendor + 4, &edx, 4); memcpy(vendor + 8, &ecx, 4); - vendor[12] = '\0'; + vendor[12] = '\0'; + _chip_model = vendor; - LOG_VERBOSE(0, F("Processor : %s"), vendor); + // _backlight_pin = -1; + _backlight_power = 1; + _backlight_invert = 0; + _backlight_level = 255; +} + +void Win32Device::reboot() +{} + +void Win32Device::show_info() +{ + LOG_VERBOSE(0, F("Processor : %s"), get_chip_model()); LOG_VERBOSE(0, F("CPU freq. : %i MHz"), get_cpu_frequency()); + LOG_VERBOSE(0, F("OS Version : %s"), get_core_version()); } const char* Win32Device::get_hostname() @@ -48,8 +87,11 @@ const char* Win32Device::get_hostname() void Win32Device::set_hostname(const char* hostname) { _hostname = hostname; +#if USE_MONITOR monitor_title(hostname); - // SDL_SetWindowTitle(monitor.window, hostname); +#elif USE_WIN32DRV + lv_win32_set_title(hostname); +#endif } const char* Win32Device::get_core_version() { @@ -58,7 +100,7 @@ const char* Win32Device::get_core_version() const char* Win32Device::get_chip_model() { - return "SDL2"; + return _chip_model.c_str(); } const char* Win32Device::get_hardware_id() @@ -112,7 +154,9 @@ void Win32Device::update_backlight() { uint8_t level = _backlight_power ? _backlight_level : 0; if(_backlight_invert) level = 255 - level; +#if USE_MONITOR monitor_backlight(level); +#endif } size_t Win32Device::get_free_max_block() @@ -143,6 +187,11 @@ bool Win32Device::is_system_pin(uint8_t pin) return false; } +void Win32Device::run_thread(void (*func)(void*), void* arg) +{ + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL); +} + long Win32Device::get_uptime() { return GetTickCount64() / 1000; @@ -150,6 +199,11 @@ long Win32Device::get_uptime() } // namespace dev +long Win32Millis() +{ + return GetTickCount64(); +} + dev::Win32Device haspDevice; -#endif // WINDOWS \ No newline at end of file +#endif // WINDOWS diff --git a/src/dev/win32/hasp_win32.h b/src/dev/win32/hasp_win32.h index 3c2edb23..09f56773 100644 --- a/src/dev/win32/hasp_win32.h +++ b/src/dev/win32/hasp_win32.h @@ -18,39 +18,7 @@ namespace dev { class Win32Device : public BaseDevice { public: - Win32Device() - { - char buffer[MAX_COMPUTERNAME_LENGTH + 1]; - DWORD length = sizeof(buffer); - - if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNameNetBIOS, buffer, &length)) { - _hostname = buffer; - } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNameDnsHostname, buffer, &length)) { - _hostname = buffer; - } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNamePhysicalDnsHostname, buffer, &length)) { - _hostname = buffer; - } else if(GetComputerNameExA((COMPUTER_NAME_FORMAT)ComputerNamePhysicalDnsDomain, buffer, &length)) { - _hostname = buffer; - } else { - _hostname = "localhost"; - } - - // Get the Windows version. - DWORD dwBuild = 0; - DWORD dwVersion = GetVersion(); - DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); - DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); - if(dwVersion < 0x80000000) dwBuild = (DWORD)(HIWORD(dwVersion)); - - char version[128]; - snprintf(version, sizeof(version), "Windows %d.%d-%d", dwMajorVersion, dwMinorVersion, dwBuild); - _core_version = version; - - // _backlight_pin = -1; - _backlight_power = 1; - _backlight_invert = 0; - _backlight_level = 255; - } + Win32Device(); void reboot() override; void show_info() override; @@ -77,9 +45,12 @@ class Win32Device : public BaseDevice { bool is_system_pin(uint8_t pin) override; + void run_thread(void (*func)(void*), void* arg); + private: std::string _hostname; std::string _core_version; + std::string _chip_model; uint8_t _backlight_pin; uint8_t _backlight_level; @@ -91,9 +62,11 @@ class Win32Device : public BaseDevice { } // namespace dev +extern long Win32Millis(); + using dev::Win32Device; extern dev::Win32Device haspDevice; #endif // WINDOWS -#endif // HASP_DEVICE_WINDOWS_H \ No newline at end of file +#endif // HASP_DEVICE_WINDOWS_H diff --git a/src/drv/tft/tft_driver.h b/src/drv/tft/tft_driver.h index 913be18f..4a9dff48 100644 --- a/src/drv/tft/tft_driver.h +++ b/src/drv/tft/tft_driver.h @@ -77,9 +77,15 @@ class BaseTft { #elif defined(STM32F7) #warning Building for STM32F7xx Tfts #include "tft_driver_tftespi.h" -#elif defined(WINDOWS) || defined(POSIX) +#elif USE_MONITOR && HASP_TARGET_PC // #warning Building for SDL2 #include "tft_driver_sdl2.h" +#elif USE_WIN32DRV && HASP_TARGET_PC +// #warning Building for Win32Drv +#include "tft_driver_win32drv.h" +#elif USE_FBDEV && HASP_TARGET_PC +// #warning Building for POSIX fbdev +#include "tft_driver_posix_fbdev.h" #else // #warning Building for Generic Tfts using dev::BaseTft; diff --git a/src/drv/tft/tft_driver_posix_fbdev.cpp b/src/drv/tft/tft_driver_posix_fbdev.cpp new file mode 100644 index 00000000..7abafa58 --- /dev/null +++ b/src/drv/tft/tft_driver_posix_fbdev.cpp @@ -0,0 +1,251 @@ +/* MIT License - Copyright (c) 2019-2022 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#if USE_FBDEV && HASP_TARGET_PC + +#include "hasplib.h" +#include "lvgl.h" + +#include "display/fbdev.h" + +#include "drv/tft/tft_driver.h" +#include "tft_driver_posix_fbdev.h" + +#if USE_EVDEV || USE_BSD_EVDEV +#include "indev/evdev.h" +#endif + +#include "dev/device.h" +#include "hasp_debug.h" +#include "hasp_gui.h" + +#ifdef HASP_CUSTOMIZE_BOOTLOGO +#include "custom/bootlogo.h" // Sketch tab header for xbm images +#else +#include "custom/bootlogo_template.h" // Sketch tab header for xbm images +#endif + +#include +#include +#include +#include +#include +#include + +#if USE_BSD_EVDEV +#include +#else +#include +#endif + +extern uint16_t tft_width; +extern uint16_t tft_height; + +namespace dev { + +/** + * A task to measure the elapsed time for LittlevGL + * @param data unused + * @return never return + */ +static void* tick_thread(void* data) +{ + (void)data; + + while(1) { + usleep(5000); /*Sleep for 5 millisecond*/ + lv_tick_inc(5); /*Tell LittelvGL that 5 milliseconds were elapsed*/ + } + + return 0; +} + +int32_t TftFbdevDrv::width() +{ + return _width; +} +int32_t TftFbdevDrv::height() +{ + return _height; +} + +static void* gui_entrypoint(void* arg) +{ +#if HASP_USE_LVGL_TASK + // create an LVGL GUI task thread + pthread_t gui_pthread; + pthread_create(&gui_pthread, 0, (void* (*)(void*))gui_task, NULL); +#endif + // create an LVGL tick thread + pthread_t tick_pthread; + pthread_create(&tick_pthread, 0, tick_thread, NULL); + return 0; +} + +void TftFbdevDrv::init(int32_t w, int h) +{ + // try to switch the active tty to tty7 + int tty_fd = open("/dev/tty0", O_WRONLY); + if(tty_fd == -1) { + perror("Couldn't open /dev/tty0 (try running as root)"); + } else { + if(ioctl(tty_fd, VT_ACTIVATE, 7) == -1) perror("Couldn't change active tty"); + } + close(tty_fd); + + // check active tty + std::ifstream f; + f.open("/sys/class/tty/tty0/active"); + std::string tty; + f >> tty; + tty = "/dev/" + tty; + f.close(); + + // try to hide the cursor + tty_fd = open(tty.c_str(), O_WRONLY); + if(tty_fd == -1) { + perror("Couldn't open active tty (try running as root)"); + } else { + write(tty_fd, "\033[?25l", 6); + } + close(tty_fd); + + /* Add a display + * Use the 'fbdev' driver which uses POSIX framebuffer device as a display + * The following input devices are handled: mouse, keyboard, mousewheel */ + fbdev_init(fbdev_path.empty() ? NULL : fbdev_path.c_str()); + fbdev_get_sizes((uint32_t*)&_width, (uint32_t*)&_height); + + // show the splashscreen early + splashscreen(); + + tft_width = _width; + tft_height = _height; + +#if USE_EVDEV || USE_BSD_EVDEV + DIR* dir = opendir("/dev/input"); + if(dir == NULL) { + perror("/dev/input opendir failed"); + } else { + // iterate through /dev/input devices + struct dirent* dirent; + unsigned char ev_type[EV_MAX / 8 + 1]; + while((dirent = readdir(dir)) != NULL) { + // make sure it's a block device matching /dev/input/event* + if(strncmp(dirent->d_name, "event", 5) != 0 || strlen(dirent->d_name) <= 5) continue; + if(dirent->d_type != DT_CHR) continue; + // skip device if not specified on command line + if(!evdev_names.empty() && + std::find(evdev_names.begin(), evdev_names.end(), std::string(dirent->d_name)) == evdev_names.end()) + continue; + // get full path + char dev_path[64]; + strcpy(dev_path, "/dev/input/"); + strcat(dev_path, dirent->d_name); +#if USE_BSD_EVDEV + // open the device + int fd = open(dev_path, O_RDONLY | O_NOCTTY); +#else + int fd = open(dev_path, O_RDONLY | O_NOCTTY | O_NDELAY); +#endif + if(fd == -1) { + perror("input open failed"); + continue; + } + // read supported event types + memset(ev_type, 0, sizeof(ev_type)); + if(ioctl(fd, EVIOCGBIT(0, sizeof(ev_type)), ev_type) < 0) { + perror("ioctl failed"); + close(fd); + continue; + } + // read device name + char dev_name[256]; + if(ioctl(fd, EVIOCGNAME(sizeof(dev_name)), dev_name) < 0) { + perror("ioctl failed"); + close(fd); + continue; + } + // check which types are supported; judge LVGL device type + lv_indev_type_t dev_type; + const char* dev_type_name; + if(ev_type[EV_REL / 8] & (1 << (EV_REL % 8))) { + dev_type = LV_INDEV_TYPE_POINTER; + dev_type_name = "EV_REL"; + } else if(ev_type[EV_ABS / 8] & (1 << (EV_ABS % 8))) { + dev_type = LV_INDEV_TYPE_POINTER; + dev_type_name = "EV_ABS"; + } else if(ev_type[EV_KEY / 8] & (1 << (EV_KEY % 8))) { + dev_type = LV_INDEV_TYPE_KEYPAD; + dev_type_name = "EV_KEY"; + } else { + close(fd); + continue; + } + // register the device + switch(dev_type) { + case LV_INDEV_TYPE_POINTER: + LOG_VERBOSE(TAG_TFT, F("Pointer : %s %s (%s)"), dev_path, dev_type_name, dev_name); + break; + case LV_INDEV_TYPE_KEYPAD: + LOG_VERBOSE(TAG_TFT, F("Keypad : %s %s (%s)"), dev_path, dev_type_name, dev_name); + break; + default: + LOG_VERBOSE(TAG_TFT, F("Input : %s %s (%s)"), dev_path, dev_type_name, dev_name); + break; + } + close(fd); + // print verbose resolution info + lv_indev_t* indev; + if(!evdev_register(dev_path, dev_type, &indev) || indev == NULL) { + printf("Failed to register evdev\n"); + continue; + } + evdev_data_t* user_data = (evdev_data_t*)indev->driver.user_data; + LOG_VERBOSE(TAG_TFT, F("Resolution : X=%d (%d..%d), Y=%d (%d..%d)"), user_data->x_max, + user_data->x_absinfo.minimum, user_data->x_absinfo.maximum, user_data->y_max, + user_data->y_absinfo.minimum, user_data->y_absinfo.maximum); + } + closedir(dir); + } +#endif + + gui_entrypoint(NULL); +} +void TftFbdevDrv::show_info() +{ + splashscreen(); + + LOG_VERBOSE(TAG_TFT, F("Driver : %s"), get_tft_model()); +} + +void TftFbdevDrv::splashscreen() +{ + uint8_t fg[] = logoFgColor; + uint8_t bg[] = logoBgColor; + lv_color_t fgColor = lv_color_make(fg[0], fg[1], fg[2]); + lv_color_t bgColor = lv_color_make(bg[0], bg[1], bg[2]); + fbdev_splashscreen(logoImage, logoWidth, logoHeight, fgColor, bgColor); +} +void TftFbdevDrv::set_rotation(uint8_t rotation) +{} +void TftFbdevDrv::set_invert(bool invert) +{} +void TftFbdevDrv::flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) +{ + lv_disp_flush_ready(disp); +} +bool TftFbdevDrv::is_driver_pin(uint8_t pin) +{ + return false; +} +const char* TftFbdevDrv::get_tft_model() +{ + return "POSIX fbdev"; +} + +} // namespace dev + +dev::TftFbdevDrv haspTft; + +#endif // WINDOWS || POSIX diff --git a/src/drv/tft/tft_driver_posix_fbdev.h b/src/drv/tft/tft_driver_posix_fbdev.h new file mode 100644 index 00000000..643126bf --- /dev/null +++ b/src/drv/tft/tft_driver_posix_fbdev.h @@ -0,0 +1,50 @@ +/* MIT License - Copyright (c) 2019-2022 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifndef HASP_FBDEV_DRIVER_H +#define HASP_FBDEV_DRIVER_H + +#include "tft_driver.h" + +#if USE_FBDEV && HASP_TARGET_PC +// #warning Building H driver FBDEV + +#include "lvgl.h" + +#include + +namespace dev { + +class TftFbdevDrv : BaseTft { + public: + void init(int w, int h); + void show_info(); + void splashscreen(); + + void set_rotation(uint8_t rotation); + void set_invert(bool invert); + + void flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p); + bool is_driver_pin(uint8_t pin); + + const char* get_tft_model(); + + int32_t width(); + int32_t height(); + + public: + std::string fbdev_path; + std::vector evdev_names; + + private: + int32_t _width, _height; +}; + +} // namespace dev + +using dev::TftFbdevDrv; +extern dev::TftFbdevDrv haspTft; + +#endif // HASP_TARGET_PC + +#endif // HASP_FBDEV_DRIVER_H diff --git a/src/drv/tft/tft_driver_sdl2.cpp b/src/drv/tft/tft_driver_sdl2.cpp index a2b44233..c6c94651 100644 --- a/src/drv/tft/tft_driver_sdl2.cpp +++ b/src/drv/tft/tft_driver_sdl2.cpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2019-2022 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#if defined(WINDOWS) || defined(POSIX) +#if USE_MONITOR && HASP_TARGET_PC #include "hasplib.h" #include "lvgl.h" @@ -75,6 +75,10 @@ void TftSdl::init(int32_t w, int h) * You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about how much time were elapsed * Create an SDL thread to do this*/ SDL_CreateThread(tick_thread, "tick", NULL); + +#if HASP_USE_LVGL_TASK +#error "SDL2 LVGL task is not implemented" +#endif } void TftSdl::show_info() { @@ -115,4 +119,4 @@ const char* TftSdl::get_tft_model() dev::TftSdl haspTft; -#endif // WINDOWS || POSIX \ No newline at end of file +#endif // WINDOWS || POSIX diff --git a/src/drv/tft/tft_driver_sdl2.h b/src/drv/tft/tft_driver_sdl2.h index 85b32ee0..32ce9e6d 100644 --- a/src/drv/tft/tft_driver_sdl2.h +++ b/src/drv/tft/tft_driver_sdl2.h @@ -6,7 +6,7 @@ #include "tft_driver.h" -#if defined(WINDOWS) || defined(POSIX) +#if USE_MONITOR && HASP_TARGET_PC // #warning Building H driver TFT SDL2 #include "lvgl.h" @@ -40,6 +40,6 @@ class TftSdl : BaseTft { using dev::TftSdl; extern dev::TftSdl haspTft; -#endif // defined(WINDOWS) || defined(POSIX) +#endif // HASP_TARGET_PC #endif // HASP_SDL2_DRIVER_H diff --git a/src/drv/tft/tft_driver_win32drv.cpp b/src/drv/tft/tft_driver_win32drv.cpp new file mode 100644 index 00000000..9e32cb59 --- /dev/null +++ b/src/drv/tft/tft_driver_win32drv.cpp @@ -0,0 +1,149 @@ +/* MIT License - Copyright (c) 2019-2022 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#if USE_WIN32DRV && HASP_TARGET_PC + +#include "hasplib.h" +#include "lvgl.h" + +#include "win32drv/win32drv.h" + +#include "drv/tft/tft_driver.h" +#include "tft_driver_win32drv.h" + +#include "dev/device.h" +#include "hasp_debug.h" +#include "hasp_gui.h" + +#ifdef HASP_CUSTOMIZE_BOOTLOGO +#include "custom/bootlogo.h" // Sketch tab header for xbm images +#else +#include "custom/bootlogo_template.h" // Sketch tab header for xbm images +#endif + +namespace dev { + +/** + * A task to measure the elapsed time for LittlevGL + * @param data unused + * @return never return + */ +static DWORD tick_thread(void* data) +{ + (void)data; + + while(1) { + Sleep(5); /*Sleep for 5 millisecond*/ + lv_tick_inc(5); /*Tell LittelvGL that 5 milliseconds were elapsed*/ + } + + return 0; +} + +int32_t TftWin32Drv::width() +{ + return _width; +} +int32_t TftWin32Drv::height() +{ + return _height; +} + +static void win32_message_loop(lv_task_t* param) +{ + MSG Message; +#if HASP_USE_LVGL_TASK + while(haspDevice.pc_is_running && GetMessageW(&Message, NULL, 0, 0)) { + TranslateMessage(&Message); + DispatchMessageW(&Message); + } + // apparently GetMessageW doesn't deliver WM_QUIT + haspDevice.pc_is_running = false; +#else + BOOL Result = PeekMessageW(&Message, NULL, 0, 0, TRUE); + if(Result != 0 && Result != -1) { + TranslateMessage(&Message); + DispatchMessageW(&Message); + if(Message.message == WM_QUIT) haspDevice.pc_is_running = false; + } +#endif +} + +static DWORD gui_entrypoint(HANDLE semaphore) +{ + /* Add a display + * Use the 'win32drv' driver which creates window on PC's monitor to simulate a display + * The following input devices are handled: mouse, keyboard, mousewheel */ + lv_win32_init(0, SW_SHOWNORMAL, haspTft.width(), haspTft.height(), 0); + lv_win32_set_title(haspDevice.get_hostname()); + +#if HASP_USE_LVGL_TASK + // let the init() function continue + ReleaseSemaphore(semaphore, 1, NULL); + // run the LVGL task as a thread + HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)gui_task, NULL, 0, NULL); + // run a blocking message loop on this thread + win32_message_loop(NULL); + // wait for the LVGL task now + WaitForSingleObject(thread, 4000); +#else + // create a LVGL tick thread + CreateThread(NULL, 0, tick_thread, NULL, 0, NULL); + // create a LVGL task for the message loop + lv_task_create(win32_message_loop, 5, LV_TASK_PRIO_HIGHEST, NULL); +#endif + return 0; +} + +void TftWin32Drv::init(int32_t w, int h) +{ + _width = w; + _height = h; + +#if HASP_USE_LVGL_TASK + // run a thread for creating the window and running the message loop + HANDLE semaphore = CreateSemaphore(NULL, 0, 1, NULL); + HANDLE thread = CreateThread(NULL, 0, gui_entrypoint, semaphore, 0, NULL); + WaitForSingleObject(semaphore, INFINITE); +#else + // do not use the gui_task(), just init the GUI and return + gui_entrypoint(NULL); +#endif +} +void TftWin32Drv::show_info() +{ + splashscreen(); + + LOG_VERBOSE(TAG_TFT, F("Driver : %s"), get_tft_model()); +} + +void TftWin32Drv::splashscreen() +{ + uint8_t fg[] = logoFgColor; + uint8_t bg[] = logoBgColor; + lv_color_t fgColor = lv_color_make(fg[0], fg[1], fg[2]); + lv_color_t bgColor = lv_color_make(bg[0], bg[1], bg[2]); + lv_win32_splashscreen(logoImage, logoWidth, logoHeight, lv_color_to32(fgColor), lv_color_to32(bgColor)); +} +void TftWin32Drv::set_rotation(uint8_t rotation) +{} +void TftWin32Drv::set_invert(bool invert) +{} +void TftWin32Drv::flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) +{ + lv_disp_flush_ready(disp); +} +bool TftWin32Drv::is_driver_pin(uint8_t pin) +{ + return false; +} +const char* TftWin32Drv::get_tft_model() +{ + return "Win32Drv"; +} + +} // namespace dev + +dev::TftWin32Drv haspTft; + +#endif // WINDOWS || POSIX diff --git a/src/drv/tft/tft_driver_win32drv.h b/src/drv/tft/tft_driver_win32drv.h new file mode 100644 index 00000000..eefcd4eb --- /dev/null +++ b/src/drv/tft/tft_driver_win32drv.h @@ -0,0 +1,44 @@ +/* MIT License - Copyright (c) 2019-2022 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifndef HASP_WIN32DRV_DRIVER_H +#define HASP_WIN32DRV_DRIVER_H + +#include "tft_driver.h" + +#if USE_WIN32DRV && HASP_TARGET_PC +// #warning Building H driver WIN32DRV + +#include "lvgl.h" + +namespace dev { + +class TftWin32Drv : BaseTft { + public: + void init(int w, int h); + void show_info(); + void splashscreen(); + + void set_rotation(uint8_t rotation); + void set_invert(bool invert); + + void flush_pixels(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p); + bool is_driver_pin(uint8_t pin); + + const char* get_tft_model(); + + int32_t width(); + int32_t height(); + + private: + int32_t _width, _height; +}; + +} // namespace dev + +using dev::TftWin32Drv; +extern dev::TftWin32Drv haspTft; + +#endif // HASP_TARGET_PC + +#endif // HASP_WIN32DRV_DRIVER_H diff --git a/src/hasp/hasp.cpp b/src/hasp/hasp.cpp index e2e49f0f..35388570 100644 --- a/src/hasp/hasp.cpp +++ b/src/hasp/hasp.cpp @@ -11,7 +11,7 @@ #include "ArduinoLog.h" #endif -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #include #include #include diff --git a/src/hasp/hasp_dispatch.cpp b/src/hasp/hasp_dispatch.cpp index d828b2b8..d9555c8a 100644 --- a/src/hasp/hasp_dispatch.cpp +++ b/src/hasp/hasp_dispatch.cpp @@ -16,7 +16,7 @@ #include "../hasp_debug.h" #include "hasp_gui.h" // for screenshot -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC #include #include #include @@ -496,10 +496,12 @@ void dispatch_config(const char* topic, const char* payload, uint8_t source) } if(strcasecmp_P(topic, PSTR("debug")) == 0) { +#if HASP_TARGET_ARDUINO if(update) debugSetConfig(settings); else debugGetConfig(settings); +#endif } else if(strcasecmp_P(topic, PSTR("gui")) == 0) { @@ -770,7 +772,7 @@ void dispatch_parse_jsonl(std::istream& stream, uint8_t& saved_page_id) void dispatch_parse_jsonl(const char*, const char* payload, uint8_t source) { if(source != TAG_MQTT) saved_jsonl_page = haspPages.get(); -#if HASP_USE_CONFIG > 0 +#if HASP_USE_CONFIG > 0 && HASP_TARGET_ARDUINO CharStream stream((char*)payload); // stream.setTimeout(10); dispatch_parse_jsonl(stream, saved_jsonl_page); @@ -840,7 +842,11 @@ void dispatch_run_script(const char*, const char* payload, uint8_t source) path[0] = '.'; path[1] = '\0'; strcat(path, filename); +#if defined(WINDOWS) path[1] = '\\'; +#elif defined(POSIX) + path[1] = '/'; +#endif LOG_TRACE(TAG_HASP, F("Loading %s from disk..."), path); std::ifstream f(path); // taking file as inputstream @@ -858,6 +864,48 @@ void dispatch_run_script(const char*, const char* payload, uint8_t source) #endif } +#if HASP_TARGET_PC +static void shell_command_thread(char* cmdline) +{ + // run the command + FILE* pipe = popen(cmdline, "r"); + // free the string duplicated previously + free(cmdline); + if(!pipe) { + LOG_ERROR(TAG_MSGR, F("Couldn't execute system command")); + return; + } + // read each line, up to 1023 chars long + char command[1024]; + while(fgets(command, sizeof(command), pipe) != NULL) { + // strip newline character + char* temp = command; + while(*temp) { + if(*temp == '\r' || *temp == '\n') { + *temp = '\0'; + break; + } + temp++; + } + // run the command + LOG_INFO(TAG_MSGR, F("Running '%s'"), command); + dispatch_text_line(command, TAG_MSGR); + } + // close the pipe, check return code + int status_code = pclose(pipe); + if(status_code) { + LOG_ERROR(TAG_MSGR, F("Process exited with non-zero return code %d"), status_code); + } +} + +void dispatch_shell_execute(const char*, const char* payload, uint8_t source) +{ + // must duplicate the string for thread's own usage + char* command = strdup(payload); + haspDevice.run_thread((void (*)(void*))shell_command_thread, (void*)command); +} +#endif + void dispatch_current_page() { char topic[8]; @@ -941,7 +989,14 @@ void dispatch_page(const char*, const char* payload, uint8_t source) void dispatch_clear_page(const char*, const char* page, uint8_t source) { if(!strcasecmp(page, "all")) { +#if !HASP_TARGET_PC hasp_init(); +#else + // workaround for "clearpage all" deadlocking or crashing on PC build (when called from non-LVGL thread) + for(uint8_t pageid = 0; pageid <= HASP_NUM_PAGES; pageid++) { + haspPages.clear(pageid); + } +#endif return; } @@ -1155,7 +1210,7 @@ void dispatch_reboot(bool saveConfig) LOG_VERBOSE(TAG_MSGR, F("-------------------------------------")); LOG_TRACE(TAG_MSGR, F(D_DISPATCH_REBOOT)); -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC fflush(stdout); #else Serial.flush(); @@ -1259,7 +1314,7 @@ void dispatch_send_discovery(const char*, const char*, uint8_t source) #if HASP_USE_HTTP > 0 network_get_ipaddress(buffer, sizeof(buffer)); doc[F("uri")] = String(F("http://")) + String(buffer); -#elif defined(WINDOWS) || defined(POSIX) +#elif HASP_TARGET_PC doc[F("uri")] = "http://google.pt"; #endif @@ -1396,7 +1451,7 @@ void dispatch_idle_state(uint8_t state) { char topic[8]; char buffer[8]; - memcpy_P(topic, PSTR("idle"), 8); + memcpy_P(topic, PSTR("idle"), 5); hasp_get_sleep_payload(state, buffer); dispatch_state_subtopic(topic, buffer); } @@ -1531,6 +1586,9 @@ void dispatchSetup() dispatch_add_command(PSTR("sensors"), dispatch_send_sensordata); dispatch_add_command(PSTR("theme"), dispatch_theme); dispatch_add_command(PSTR("run"), dispatch_run_script); +#if HASP_TARGET_PC + dispatch_add_command(PSTR("shell"), dispatch_shell_execute); +#endif dispatch_add_command(PSTR("service"), dispatch_service); dispatch_add_command(PSTR("antiburn"), dispatch_antiburn); dispatch_add_command(PSTR("calibrate"), dispatch_calibrate); @@ -1552,7 +1610,7 @@ void dispatchSetup() dispatch_add_command(PSTR("unzip"), filesystemUnzip); #endif #endif -#if HASP_USE_CONFIG > 0 +#if HASP_USE_CONFIG > 0 && HASP_TARGET_ARDUINO dispatch_add_command(PSTR("setupap"), oobeFakeSetup); #endif /* WARNING: remember to expand the commands array when adding new commands */ diff --git a/src/hasp/hasp_event.cpp b/src/hasp/hasp_event.cpp index 433bc7ea..fd5eeaee 100644 --- a/src/hasp/hasp_event.cpp +++ b/src/hasp/hasp_event.cpp @@ -176,9 +176,9 @@ void event_timer_clock(lv_task_t* task) timeval curTime; int rslt = gettimeofday(&curTime, NULL); (void)rslt; // unused - time_t seconds = curTime.tv_sec; - useconds_t tv_msec = curTime.tv_usec / 1000; - tm* timeinfo = localtime(&seconds); + time_t seconds = curTime.tv_sec; + auto tv_msec = curTime.tv_usec / 1000; + tm* timeinfo = localtime(&seconds); lv_task_set_period(task, data->interval - tv_msec); char buffer[128] = {0}; @@ -835,7 +835,7 @@ void cpicker_event_handler(lv_obj_t* obj, lv_event_t event) if(!translate_event(obj, event, hasp_event_id) || event == LV_EVENT_VALUE_CHANGED) return; /* Get the new value */ - lv_color_t color = lv_cpicker_get_color(obj); + lv_color_t color = lv_cpicker_get_color(obj); lv_cpicker_color_mode_t mode = lv_cpicker_get_color_mode(obj); if(hasp_event_id == HASP_EVENT_CHANGED && last_color_sent.full == color.full) return; // same value as before @@ -853,12 +853,16 @@ void cpicker_event_handler(lv_obj_t* obj, lv_event_t event) if(const char* tag = my_obj_get_tag(obj)) snprintf_P(data, sizeof(data), - PSTR("{\"event\":\"%s\",\"color\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d,\"h\":%d,\"s\":%d,\"v\":%d,\"tag\":%s}"), - eventname, c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue, hsv.h, hsv.s, hsv.v, tag); + PSTR("{\"event\":\"%s\",\"color\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d,\"h\":%d,\"s\":%" + "d,\"v\":%d,\"tag\":%s}"), + eventname, c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue, hsv.h, + hsv.s, hsv.v, tag); else snprintf_P(data, sizeof(data), - PSTR("{\"event\":\"%s\",\"color\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d,\"h\":%d,\"s\":%d,\"v\":%d}"), eventname, - c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue, hsv.h, hsv.s, hsv.v); + PSTR("{\"event\":\"%s\",\"color\":\"#%02x%02x%02x\",\"r\":%d,\"g\":%d,\"b\":%d,\"h\":%d,\"s\":%" + "d,\"v\":%d}"), + eventname, c32.ch.red, c32.ch.green, c32.ch.blue, c32.ch.red, c32.ch.green, c32.ch.blue, hsv.h, + hsv.s, hsv.v); } event_send_object_data(obj, data); @@ -907,4 +911,4 @@ void calendar_event_handler(lv_obj_t* obj, lv_event_t event) // event_update_group(obj->user_data.groupid, obj, val, min, max); } -#endif \ No newline at end of file +#endif diff --git a/src/hasp/hasp_font.cpp b/src/hasp/hasp_font.cpp index 98a20c46..d86a4f14 100644 --- a/src/hasp/hasp_font.cpp +++ b/src/hasp/hasp_font.cpp @@ -68,7 +68,7 @@ void font_setup() } else { LOG_ERROR(TAG_FONT, F("FreeType " D_SERVICE_START_FAILED)); } -#elif defined(WINDOWS) || defined(POSIX) +#elif HASP_TARGET_PC #else #endif diff --git a/src/hasp/hasp_object.cpp b/src/hasp/hasp_object.cpp index 08fd170e..f7e436cf 100644 --- a/src/hasp/hasp_object.cpp +++ b/src/hasp/hasp_object.cpp @@ -179,7 +179,7 @@ int hasp_parse_json_attributes(lv_obj_t* obj, const JsonObject& doc) { int i = 0; -#if defined(WINDOWS) || defined(POSIX) || defined(ESP32) +#if HASP_TARGET_PC || defined(ESP32) std::string v; v.reserve(64); diff --git a/src/hasp/hasp_page.cpp b/src/hasp/hasp_page.cpp index bb970e57..50b80343 100644 --- a/src/hasp/hasp_page.cpp +++ b/src/hasp/hasp_page.cpp @@ -244,7 +244,11 @@ void Page::load_jsonl(const char* pagesfile) path[0] = '.'; path[1] = '\0'; strcat(path, pagesfile); +#if defined(WINDOWS) path[1] = '\\'; +#elif defined(POSIX) + path[1] = '/'; +#endif LOG_TRACE(TAG_HASP, F("Loading %s from disk..."), path); std::ifstream f(path); // taking file as inputstream diff --git a/src/hasp/hasp_parser.cpp b/src/hasp/hasp_parser.cpp index 0b85e415..8660060c 100644 --- a/src/hasp/hasp_parser.cpp +++ b/src/hasp/hasp_parser.cpp @@ -136,34 +136,34 @@ void Parser::get_event_name(uint8_t eventid, char* buffer, size_t size) { switch(eventid) { case HASP_EVENT_ON: - memcpy_P(buffer, PSTR("on"), size); + memcpy_P(buffer, PSTR("on"), 3); break; case HASP_EVENT_OFF: - memcpy_P(buffer, PSTR("off"), size); + memcpy_P(buffer, PSTR("off"), 4); break; case HASP_EVENT_UP: - memcpy_P(buffer, PSTR("up"), size); + memcpy_P(buffer, PSTR("up"), 3); break; case HASP_EVENT_DOWN: - memcpy_P(buffer, PSTR("down"), size); + memcpy_P(buffer, PSTR("down"), 5); break; case HASP_EVENT_RELEASE: - memcpy_P(buffer, PSTR("release"), size); + memcpy_P(buffer, PSTR("release"), 8); break; case HASP_EVENT_LONG: - memcpy_P(buffer, PSTR("long"), size); + memcpy_P(buffer, PSTR("long"), 5); break; case HASP_EVENT_HOLD: - memcpy_P(buffer, PSTR("hold"), size); + memcpy_P(buffer, PSTR("hold"), 5); break; case HASP_EVENT_LOST: - memcpy_P(buffer, PSTR("lost"), size); + memcpy_P(buffer, PSTR("lost"), 5); break; case HASP_EVENT_CHANGED: - memcpy_P(buffer, PSTR("changed"), size); + memcpy_P(buffer, PSTR("changed"), 8); break; default: - memcpy_P(buffer, PSTR("unknown"), size); + memcpy_P(buffer, PSTR("unknown"), 8); } } @@ -238,4 +238,4 @@ long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } -#endif \ No newline at end of file +#endif diff --git a/src/hasp_config.cpp b/src/hasp_config.cpp index d3e55b1e..75ac0bb5 100644 --- a/src/hasp_config.cpp +++ b/src/hasp_config.cpp @@ -8,7 +8,9 @@ #include "hasp_config.h" #include "hasp_debug.h" #include "hasp_gui.h" +#if HASP_TARGET_ARDUINO #include "hal/hasp_hal.h" +#endif // #include "hasp_ota.h" included in conf // #include "hasp_filesystem.h" included in conf @@ -21,7 +23,9 @@ #include "EEPROM.h" #endif +#if HASP_USE_EEPROM > 0 #include "StreamUtils.h" // For EEPromStream +#endif extern uint16_t dispatchTelePeriod; extern uint32_t dispatchLastMillis; @@ -29,6 +33,7 @@ extern uint32_t dispatchLastMillis; extern gui_conf_t gui_settings; extern dispatch_conf_t dispatch_settings; +#if HASP_TARGET_ARDUINO void confDebugSet(const __FlashStringHelper* fstr_name) { /*char buffer[128]; @@ -36,6 +41,7 @@ void confDebugSet(const __FlashStringHelper* fstr_name) debugPrintln(buffer);*/ LOG_VERBOSE(TAG_CONF, F(D_BULLET "%S set"), fstr_name); } +#endif void confDebugSet(const char* fstr_name) { /*char buffer[128]; @@ -44,6 +50,7 @@ void confDebugSet(const char* fstr_name) LOG_VERBOSE(TAG_CONF, F(D_BULLET "%s set"), fstr_name); } +#if HASP_TARGET_ARDUINO bool configSet(bool& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name) { if(!setting.isNull()) { @@ -130,6 +137,7 @@ bool configSet(char *value, size_t size, const JsonVariant& setting, const __Fla } return false; } +#endif bool configSet(bool& value, const JsonVariant& setting, const char* fstr_name) { @@ -207,7 +215,9 @@ bool configSet(lv_color_t& value, const JsonVariant& setting, const char* fstr_n void configSetupDebug(JsonDocument& settings) { +#if HASP_TARGET_ARDUINO debugSetup(settings[FPSTR(FP_DEBUG)]); +#endif debugStart(); // Debug started, now we can use it; HASP header sent } @@ -239,9 +249,9 @@ void configMaskPasswords(JsonDocument& settings) DeserializationError configParseFile(String& configFile, JsonDocument& settings) { + DeserializationError result = DeserializationError::InvalidInput; #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 File file = HASP_FS.open(configFile, "r"); - DeserializationError result; if(file) { // size_t size = file.size(); @@ -254,30 +264,50 @@ DeserializationError configParseFile(String& configFile, JsonDocument& settings) return result; } return DeserializationError::InvalidInput; +#elif HASP_TARGET_PC + lv_fs_file_t f; + lv_fs_res_t res; + lv_fs_open(&f, "L:/config.json", LV_FS_MODE_RD); + if(res == LV_FS_RES_OK) { + uint32_t size = 0, read = 0; + if(lv_fs_size(&f, &size) == LV_FS_RES_OK && size != 0) { + char* buf = (char*)malloc(size + 1); + if(lv_fs_read(&f, buf, size, &read) == LV_FS_RES_OK && read == size) { + result = deserializeJson(settings, buf); + } + } + lv_fs_close(&f); + return result; + } + LOG_ERROR(TAG_HASP, F("Opening config.json from FS failed %d"), res); + return result; #else - return DeserializationError::InvalidInput; + return result; #endif } DeserializationError configRead(JsonDocument& settings, bool setupdebug) { - String configFile((char*)0); + String configFile; configFile.reserve(32); configFile = String(FPSTR(FP_HASP_CONFIG_FILE)); DeserializationError error; -#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 + if(setupdebug) configSetupDebug(settings); // Now we can use log + +#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 || HASP_TARGET_PC error = configParseFile(configFile, settings); if(!error) { String output, wifiPass, mqttPass, httpPass, wgPrivKey; /* Load Debug params */ if(setupdebug) { - configSetupDebug(settings); // Now we can use log LOG_INFO(TAG_CONF, F("SPI flash FS mounted")); +#if HASP_TARGET_ARDUINO filesystemInfo(); filesystemList(); +#endif } LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), configFile.c_str()); @@ -304,9 +334,6 @@ DeserializationError configRead(JsonDocument& settings, bool setupdebug) #endif - // File does not exist or error reading file - 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 @@ -360,11 +387,11 @@ void configBackupToEeprom() */ void configWrite() { - String configFile((char*)0); + String configFile; configFile.reserve(32); configFile = String(FPSTR(FP_HASP_CONFIG_FILE)); - String settingsChanged((char*)0); + String settingsChanged; settingsChanged.reserve(128); settingsChanged = F(D_CONFIG_CHANGED); @@ -462,6 +489,7 @@ void configWrite() } #endif +#if HASP_TARGET_ARDUINO module = FPSTR(FP_DEBUG); if(settings[module].as().isNull()) settings.createNestedObject(module); changed = debugGetConfig(settings[module]); @@ -470,6 +498,7 @@ void configWrite() configOutput(settings[module], TAG_DEBG); writefile = true; } +#endif if(settings[FPSTR(FP_GUI)].as().isNull()) settings.createNestedObject(FPSTR(FP_GUI)); changed = guiGetConfig(settings[FPSTR(FP_GUI)]); @@ -561,9 +590,10 @@ void configSetup() configRead(settings, true); } - // #if HASP_USE_SPIFFS > 0 +#if HASP_TARGET_ARDUINO LOG_INFO(TAG_DEBG, F("Loading debug settings")); debugSetConfig(settings[FPSTR(FP_DEBUG)]); +#endif LOG_INFO(TAG_GPIO, F("Loading GUI settings")); guiSetConfig(settings[FPSTR(FP_GUI)]); LOG_INFO(TAG_HASP, F("Loading HASP settings")); @@ -605,6 +635,12 @@ void configSetup() gpioSetConfig(settings[FPSTR(FP_GPIO)]); #endif + // target-specific config +#if defined(POSIX) + LOG_INFO(TAG_CONF, F("Loading POSIX-specific settings")); + haspDevice.set_config(settings[F("posix")]); +#endif + LOG_INFO(TAG_CONF, F(D_CONFIG_LOADED)); } // #endif @@ -615,15 +651,15 @@ void configLoop(void) void configOutput(const JsonObject& settings, uint8_t tag) { - String output((char*)0); + String output; output.reserve(128); serializeJson(settings, output); - String passmask((char*)0); + String passmask; passmask.reserve(128); passmask = F("\"pass\":\"" D_PASSWORD_MASK "\""); - String password((char*)0); + String password; password.reserve(128); String pass = F("pass"); @@ -631,28 +667,48 @@ void configOutput(const JsonObject& settings, uint8_t tag) password = F("\"pass\":\""); password += settings[pass].as(); password += F("\""); +#if HASP_TARGET_ARDUINO output.replace(password, passmask); +#elif HASP_TARGET_PC + size_t pos = 0; + if((pos = output.find(password)) != std::string::npos) output.replace(pos, password.size(), passmask); +#endif } if(!settings[FPSTR(FP_WIFI)][pass].isNull()) { password = F("\"pass\":\""); password += settings[FPSTR(FP_WIFI)][pass].as(); password += F("\""); +#if HASP_TARGET_ARDUINO output.replace(password, passmask); +#elif HASP_TARGET_PC + size_t pos = 0; + if((pos = output.find(password)) != std::string::npos) output.replace(pos, password.size(), passmask); +#endif } if(!settings[FPSTR(FP_MQTT)][pass].isNull()) { password = F("\"pass\":\""); password += settings[FPSTR(FP_MQTT)][pass].as(); password += F("\""); +#if HASP_TARGET_ARDUINO output.replace(password, passmask); +#elif HASP_TARGET_PC + size_t pos = 0; + if((pos = output.find(password)) != std::string::npos) output.replace(pos, password.size(), passmask); +#endif } if(!settings[FPSTR(FP_HTTP)][pass].isNull()) { password = F("\"pass\":\""); password += settings[FPSTR(FP_HTTP)][pass].as(); password += F("\""); +#if HASP_TARGET_ARDUINO output.replace(password, passmask); +#elif HASP_TARGET_PC + size_t pos = 0; + if((pos = output.find(password)) != std::string::npos) output.replace(pos, password.size(), passmask); +#endif } if(!settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].isNull()) { @@ -660,7 +716,12 @@ void configOutput(const JsonObject& settings, uint8_t tag) password += settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].as(); password += F("\""); passmask = F("\"privkey\":\"" D_PASSWORD_MASK "\""); +#if HASP_TARGET_ARDUINO output.replace(password, passmask); +#elif HASP_TARGET_PC + size_t pos = 0; + if((pos = output.find(password)) != std::string::npos) output.replace(pos, password.size(), passmask); +#endif } LOG_VERBOSE(tag, output.c_str()); @@ -688,4 +749,4 @@ bool configClearEeprom() #endif } -#endif // HAS_USE_CONFIG \ No newline at end of file +#endif // HAS_USE_CONFIG diff --git a/src/hasp_config.h b/src/hasp_config.h index 8a667ed6..04160d30 100644 --- a/src/hasp_config.h +++ b/src/hasp_config.h @@ -25,13 +25,15 @@ void configOutput(const JsonObject& settings, uint8_t tag); bool configClearEeprom(void); /* ===== Getter and Setter Functions ===== */ +#if HASP_TARGET_ARDUINO bool configSet(bool& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); 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); bool configSet(int32_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); bool configSet(lv_color_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name); -bool configSet(char *value, size_t size, const JsonVariant& setting, const __FlashStringHelper* fstr_name); +bool configSet(char* value, size_t size, const JsonVariant& setting, const __FlashStringHelper* fstr_name); +#endif bool configSet(bool& value, const JsonVariant& setting, const char* fstr_name); bool configSet(int8_t& value, const JsonVariant& setting, const char* fstr_name); bool configSet(uint8_t& value, const JsonVariant& setting, const char* fstr_name); @@ -41,8 +43,10 @@ bool configSet(lv_color_t& value, const JsonVariant& setting, const char* fstr_n void configMaskPasswords(JsonDocument& settings); /* ===== Read/Write Configuration ===== */ +#if HASP_TARGET_ARDUINO void configSetConfig(JsonObject& settings); void configGetConfig(JsonDocument& settings); +#endif /* json keys used in the configfile */ const char FP_CONFIG_STARTPAGE[] PROGMEM = "startpage"; @@ -107,4 +111,4 @@ const char FP_OTA[] PROGMEM = "ota"; #endif -#endif // HASP_USE_CONFIG \ No newline at end of file +#endif // HASP_USE_CONFIG diff --git a/src/hasp_debug.cpp b/src/hasp_debug.cpp index 672c0ba2..ca648c60 100644 --- a/src/hasp_debug.cpp +++ b/src/hasp_debug.cpp @@ -8,12 +8,12 @@ #include "hasp_debug.h" #include "hasp_macro.h" -#if(!defined(WINDOWS)) && (!defined(POSIX)) +#if HASP_TARGET_ARDUINO #define debug_print(io, ...) io->printf(__VA_ARGS__) #define debug_newline(io) io->println() -#else +#elif HASP_TARGET_PC #include #include #include @@ -21,6 +21,18 @@ #define debug_print(io, ...) fprintf(stdout, __VA_ARGS__) #define debug_newline(io) fprintf(stdout, "\n") +#if defined(WINDOWS) +#include +#include +#define cwd _getcwd +#endif + +#if defined(POSIX) +#include +#include +#define cwd getcwd +#endif + #endif bool debugAnsiCodes = false; @@ -99,7 +111,7 @@ static inline void debug_flush() HASP_SERIAL.flush(); #endif -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC fflush(stdout); #endif } @@ -116,22 +128,22 @@ void debugEverySecond() void debugStart(void) { -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC debug_newline(); debugPrintHaspHeader(NULL); debug_newline(); + char curdir[PATH_MAX]; + LOG_INFO(TAG_DEBG, F("Configuration directory: %s"), cwd(curdir, sizeof(curdir))); LOG_INFO(TAG_DEBG, F("Environment: " PIOENV)); LOG_INFO(TAG_DEBG, F("Console started")); debug_flush(); -#else +#endif #if HASP_USE_CONSOLE > 0 consoleSetup(); #endif - -#endif } void debugStop() @@ -445,4 +457,4 @@ void debugPrintPrefix(uint8_t tag, int level, Print* _logOutput) #else debug_print(_logOutput, PSTR(" %s: "), buffer); #endif -} \ No newline at end of file +} diff --git a/src/hasp_debug.h b/src/hasp_debug.h index 425a7d97..c025ff99 100644 --- a/src/hasp_debug.h +++ b/src/hasp_debug.h @@ -17,7 +17,7 @@ #include "lang/lang.h" -#if(!defined(WINDOWS)) && (!defined(POSIX)) +#if HASP_TARGET_ARDUINO /* ===== Default Event Processors ===== */ void debugSetup(JsonObject settings); diff --git a/src/hasp_gui.cpp b/src/hasp_gui.cpp index b69a1ee5..13caf317 100644 --- a/src/hasp_gui.cpp +++ b/src/hasp_gui.cpp @@ -207,21 +207,8 @@ static inline void gui_init_images() static inline void gui_init_filesystems() { #if LV_USE_FS_IF != 0 - //_lv_fs_init(); // lvgl File System -- not needed, it done in lv_init() when LV_USE_FILESYSTEM is set LOG_VERBOSE(TAG_LVGL, F("Filesystem : " D_SETTING_ENABLED)); lv_fs_if_init(); // auxiliary file system drivers - // 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 : " D_SETTING_DISABLED)); #endif @@ -305,17 +292,27 @@ void guiSetup() #endif disp_drv.monitor_cb = gui_monitor_cb; + // register a touchscreen/mouse driver - only on real hardware and SDL2 + // Win32 and POSIX handles input drivers in tft_driver +#if TOUCH_DRIVER != -1 || USE_MONITOR /* Initialize the touch pad */ static lv_indev_drv_t indev_drv; lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; -#if defined(WINDOWS) || defined(POSIX) +#if USE_MONITOR && HASP_TARGET_PC indev_drv.read_cb = mouse_read; #else indev_drv.read_cb = gui_touch_read; #endif lv_indev_t* mouse_indev = lv_indev_drv_register(&indev_drv); mouse_indev->driver.type = LV_INDEV_TYPE_POINTER; +#else + // find the first registered input device to add a cursor to + lv_indev_t* mouse_indev = NULL; + while((mouse_indev = lv_indev_get_next(mouse_indev))) { + if(mouse_indev->driver.type == LV_INDEV_TYPE_POINTER) break; + } +#endif /*Set a cursor for the mouse*/ LOG_TRACE(TAG_GUI, F("Initialize Cursor")); @@ -336,16 +333,18 @@ void guiSetup() cursor = lv_img_create(mouse_layer, NULL); /*Create an image object for the cursor */ lv_img_set_src(cursor, &mouse_cursor_icon); /*Set the image source*/ #else - cursor = lv_obj_create(mouse_layer, NULL); // show cursor object on every page + cursor = lv_obj_create(mouse_layer, NULL); // show cursor object on every page lv_obj_set_size(cursor, 9, 9); lv_obj_set_style_local_radius(cursor, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); lv_obj_set_style_local_bg_color(cursor, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_opa(cursor, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); #endif gui_hide_pointer(false); - lv_indev_set_cursor(mouse_indev, cursor); /*Connect the image object to the driver*/ + if(mouse_indev != NULL) { + lv_indev_set_cursor(mouse_indev, cursor); /*Connect the image object to the driver*/ + } -#if !(defined(WINDOWS) || defined(POSIX)) +#if HASP_TARGET_ARDUINO // drv_touch_init(gui_settings.rotation); // Touch driver haspTouch.init(tft_width, tft_height); haspTouch.set_calibration(gui_settings.cal_data); @@ -386,7 +385,7 @@ IRAM_ATTR void guiLoop(void) // tick.update(); #endif -#if !(defined(WINDOWS) || defined(POSIX)) +#if HASP_TARGET_ARDUINO // haspTouch.loop(); #endif } @@ -396,23 +395,34 @@ void guiEverySecond(void) // nothing } -#if defined(ESP32) && defined(HASP_USE_ESP_MQTT) - #if HASP_USE_LVGL_TASK == 1 -static void gui_task(void* args) +void gui_task(void* args) { LOG_TRACE(TAG_GUI, "Start to run LVGL"); - while(1) { + while(haspDevice.pc_is_running) { + // no idea what MQTT has to do with LVGL - the #if is copied from the code below +#if defined(ESP32) && defined(HASP_USE_ESP_MQTT) /* Try to take the semaphore, call lvgl related function on success */ - // if(pdTRUE == xSemaphoreTake(xGuiSemaphore, pdMS_TO_TICKS(10))) { if(pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) { lv_task_handler(); xSemaphoreGive(xGuiSemaphore); vTaskDelay(pdMS_TO_TICKS(5)); } +#else + // optimize lv_task_handler() by actually using the returned delay value + auto time_start = millis(); + uint32_t sleep_time = lv_task_handler(); + delay(sleep_time); + auto time_end = millis(); + lv_tick_inc(time_end - time_start); +#endif } } +#endif // HASP_USE_LVGL_TASK +#if defined(ESP32) && defined(HASP_USE_ESP_MQTT) + +#if HASP_USE_LVGL_TASK == 1 esp_err_t gui_setup_lvgl_task() { #if CONFIG_FREERTOS_UNICORE == 0 diff --git a/src/hasp_gui.h b/src/hasp_gui.h index a95e877a..8b5b12af 100644 --- a/src/hasp_gui.h +++ b/src/hasp_gui.h @@ -60,6 +60,11 @@ uint32_t guiScreenshotEtag(); void gui_flush_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p); void gui_antiburn_cb(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p); +/* ===== Main LVGL Task ===== */ +#if HASP_USE_LVGL_TASK == 1 +void gui_task(void* args); +#endif + /* ===== Locks ===== */ #ifdef ESP32 IRAM_ATTR bool gui_acquire(TickType_t timeout); @@ -73,4 +78,4 @@ bool guiGetConfig(const JsonObject& settings); bool guiSetConfig(const JsonObject& settings); #endif // HASP_USE_CONFIG -#endif // HASP_GUI_H \ No newline at end of file +#endif // HASP_GUI_H diff --git a/src/main_arduino.cpp b/src/main.cpp similarity index 91% rename from src/main_arduino.cpp rename to src/main.cpp index 8fc378b2..b065909c 100644 --- a/src/main_arduino.cpp +++ b/src/main.cpp @@ -1,8 +1,6 @@ /* MIT License - Copyright (c) 2019-2024 Francis Van Roie For full license information read the LICENSE file in the project folder */ -#if !(defined(WINDOWS) || defined(POSIX)) - /* #ifdef CORE_DEBUG_LEVEL #undef CORE_DEBUG_LEVEL @@ -28,9 +26,9 @@ #include "hasp_gui.h" #endif -bool isConnected; -uint8_t mainLoopCounter = 0; -unsigned long mainLastLoopTime = 0; +static bool isConnected; +static uint8_t mainLoopCounter = 0; +static unsigned long mainLastLoopTime = 0; #ifdef HASP_USE_STAT_COUNTER uint16_t statLoopCounter = 0; // measures the average looptime @@ -40,11 +38,23 @@ void setup() { // hal_setup(); +#if HASP_TARGET_ARDUINO esp_log_level_set("*", ESP_LOG_NONE); // set all components to ERROR level // esp_log_level_set("wifi", ESP_LOG_NONE); // enable WARN logs from WiFi stack // esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client // esp_log_level_set("esp_crt_bundle", ESP_LOG_VERBOSE); // enable WARN logs from WiFi stack // esp_log_level_set("esp_tls", ESP_LOG_VERBOSE); // enable WARN logs from WiFi stack +#elif HASP_TARGET_PC + // Initialize lvgl environment + lv_init(); + lv_log_register_print_cb(debugLvglLogEvent); +#if HASP_USE_CONFIG + // initialize FS before running configSetup() + // normally, it's initialized in guiSetup(), but Arduino doesn't need FS in configSetup() + lv_fs_if_init(); +#endif +#endif + haspDevice.init(); /**************************** @@ -149,7 +159,7 @@ void setup() gui_setup_lvgl_task(); #endif // HASP_USE_LVGL_TASK - mainLastLoopTime = -1000; // reset loop counter + mainLastLoopTime = 0; // reset loop counter } IRAM_ATTR void loop() @@ -195,7 +205,7 @@ IRAM_ATTR void loop() /* Timer Loop */ if(millis() - mainLastLoopTime >= 1000) { - mainLastLoopTime += 1000; + mainLastLoopTime = millis(); /* Runs Every Second */ haspEverySecond(); // sleep timer & statusupdate @@ -237,10 +247,9 @@ IRAM_ATTR void loop() case 4: #if HASP_USE_WIFI > 0 || HASP_USE_ETHERNET > 0 isConnected = networkEvery5Seconds(); // Check connection - +#endif #if HASP_USE_MQTT > 0 mqttEvery5Seconds(isConnected); -#endif #endif break; @@ -270,5 +279,3 @@ IRAM_ATTR void loop() delay(2); // ms #endif } - -#endif diff --git a/src/main_pc.cpp b/src/main_pc.cpp new file mode 100644 index 00000000..40431d75 --- /dev/null +++ b/src/main_pc.cpp @@ -0,0 +1,226 @@ +/* MIT License - Copyright (c) 2019-2022 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#if HASP_TARGET_PC + +#if defined(WINDOWS) + +#include +#include +#include +#include +// MSDN recommends against using getcwd & chdir names +#define cwd _getcwd +#define cd _chdir +#endif + +#if defined(POSIX) +#include +#include +#include +#include +#include +#include +#define cwd getcwd +#define cd chdir +#endif + +#include +#include + +#include "hasplib.h" + +#if USE_MONITOR +#include "display/monitor.h" +#endif + +#include "hasp_debug.h" + +// hasp_gui.cpp +extern uint16_t tft_width; +extern uint16_t tft_height; + +// main.cpp +extern void setup(); +extern void loop(); + +#if defined(WINDOWS) +// https://gist.github.com/kingseva/a918ec66079a9475f19642ec31276a21 +void BindStdHandlesToConsole() +{ + // TODO: Add Error checking. + + // Redirect the CRT standard input, output, and error handles to the console + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stderr); + freopen("CONOUT$", "w", stdout); + + // Note that there is no CONERR$ file + HANDLE hStdout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hStdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + SetStdHandle(STD_OUTPUT_HANDLE, hStdout); + SetStdHandle(STD_ERROR_HANDLE, hStdout); + SetStdHandle(STD_INPUT_HANDLE, hStdin); + + // Clear the error state for each of the C++ standard stream objects. + std::wclog.clear(); + std::clog.clear(); + std::wcout.clear(); + std::cout.clear(); + std::wcerr.clear(); + std::cerr.clear(); + std::wcin.clear(); + std::cin.clear(); +} + +void InitializeConsoleOutput() +{ + bool isConsoleApp; + + // How to check if the program is run from a console? + // https://stackoverflow.com/questions/9009333/how-to-check-if-the-program-is-run-from-a-console + HWND consoleWnd = GetConsoleWindow(); + DWORD dwProcessId; + GetWindowThreadProcessId(consoleWnd, &dwProcessId); + + if(GetCurrentProcessId() == dwProcessId) { + isConsoleApp = true; // Opened in Console + } else { + isConsoleApp = false; // Opened in Windows + } + + // Use normal console that launched the program + AttachConsole(ATTACH_PARENT_PROCESS); + + // Test if the application was started from a Console Window + if(!isConsoleApp) { + // If started from Windows, use detached Log Console Window + AllocConsole(); + } + + // Redirect all standard output streams to the console + BindStdHandlesToConsole(); +} +#endif + +void usage(const char* progName, const char* version) +{ + std::cout << "\n" + << progName << " " << version << " [options]" << std::endl + << std::endl + << "Options:" << std::endl + << " -h | --help Print this help" << std::endl + << " -q | --quiet Suppress console output (can improve performance)" << std::endl +#if !USE_FBDEV + << " -W | --width Width of the window" << std::endl + << " -H | --height Height of the window" << std::endl +#endif + << " -c | --config Configuration/storage directory" << std::endl +#if defined(WINDOWS) + << " (default: 'AppData\\hasp\\hasp')" << std::endl +#elif defined(POSIX) + << " (default: '~/.local/share/hasp/hasp')" << std::endl +#endif + << std::endl; + fflush(stdout); +} + +int main(int argc, char* argv[]) +{ + bool showhelp = false; + bool console = true; + char config[PATH_MAX] = {'\0'}; + +#if defined(WINDOWS) + InitializeConsoleOutput(); + SetConsoleCP(65001); // 65001 = UTF-8 +#endif + + for(int arg = 1; arg < argc; arg++) { + if(strncmp(argv[arg], "--help", 6) == 0 || strncmp(argv[arg], "-h", 2) == 0) { + showhelp = true; + } else if(strncmp(argv[arg], "--quiet", 7) == 0 || strncmp(argv[arg], "-q", 2) == 0) { +#if defined(WINDOWS) + FreeConsole(); +#endif +#if defined(POSIX) + int nullfd = open("/dev/null", O_WRONLY); + dup2(nullfd, 1); + close(nullfd); +#endif +#if !USE_FBDEV + } else if(strncmp(argv[arg], "--width", 7) == 0 || strncmp(argv[arg], "-W", 2) == 0) { + if(arg + 1 < argc) { + int w = atoi(argv[arg + 1]); + if(w > 0) tft_width = w; + arg++; + } else { + std::cout << "Missing width value" << std::endl; + showhelp = true; + } + } else if(strncmp(argv[arg], "--height", 8) == 0 || strncmp(argv[arg], "-H", 2) == 0) { + if(arg + 1 < argc) { + int h = atoi(argv[arg + 1]); + if(h > 0) tft_height = h; + arg++; + } else { + std::cout << "Missing height value" << std::endl; + showhelp = true; + } +#endif + } else if(strncmp(argv[arg], "--config", 8) == 0 || strncmp(argv[arg], "-c", 2) == 0) { + if(arg + 1 < argc) { + strcpy(config, argv[arg + 1]); + arg++; + } else { + std::cout << "Missing config directory" << std::endl; + showhelp = true; + } + } else { + std::cout << "Unrecognized command line parameter: " << argv[arg] << std::endl; + showhelp = true; + } + } + + if(showhelp) { + usage("openHASP", haspDevice.get_version()); + goto end; + } + + if(config[0] == '\0') { +#if USE_MONITOR + SDL_Init(0); // Needs to be initialized for GetPerfPath + strcpy(config, SDL_GetPrefPath("hasp", "hasp")); + SDL_Quit(); // We'll properly init later +#elif defined(WINDOWS) + if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, config))) { + PathAppendA(config, "hasp"); + PathAppendA(config, "hasp"); + } +#elif defined(POSIX) + struct passwd* pw = getpwuid(getuid()); + strcpy(config, pw->pw_dir); + strcat(config, "/.local/share/hasp/hasp"); +#endif + } + cd(config); + + setup(); + while(haspDevice.pc_is_running) { + loop(); + } + +end: +#if defined(WINDOWS) + std::cout << std::endl << std::flush; + fflush(stdout); + FreeConsole(); + exit(0); +#endif + return 0; +} + +#endif diff --git a/src/main_sdl2.cpp b/src/main_sdl2.cpp deleted file mode 100644 index d2942333..00000000 --- a/src/main_sdl2.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* MIT License - Copyright (c) 2019-2022 Francis Van Roie - For full license information read the LICENSE file in the project folder */ - -#if defined(WINDOWS) || defined(POSIX) - -#if defined(WINDOWS) - -#include -#include -// MSDN recommends against using getcwd & chdir names -#define cwd _getcwd -#define cd _chdir -#endif - -#if defined(POSIX) -#include -#include -#define cwd getcwd -#define cd chdir -#endif - -#include -#include - -#include "hasplib.h" - -// #include "app_hal.h" -#include "display/monitor.h" - -#include "hasp_debug.h" -#include "hasp_gui.h" - -#include "dev/device.h" - -bool isConnected; -bool isRunning = 1; - -uint8_t mainLoopCounter = 0; -unsigned long mainLastLoopTime = 0; - -#ifdef HASP_USE_STAT_COUNTER -uint16_t statLoopCounter = 0; // measures the average looptime -#endif - -extern uint16_t tft_width; -extern uint16_t tft_height; - -#if defined(WINDOWS) -// https://gist.github.com/kingseva/a918ec66079a9475f19642ec31276a21 -void BindStdHandlesToConsole() -{ - // TODO: Add Error checking. - - // Redirect the CRT standard input, output, and error handles to the console - freopen("CONIN$", "r", stdin); - freopen("CONOUT$", "w", stderr); - freopen("CONOUT$", "w", stdout); - - // Note that there is no CONERR$ file - HANDLE hStdout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - HANDLE hStdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - SetStdHandle(STD_OUTPUT_HANDLE, hStdout); - SetStdHandle(STD_ERROR_HANDLE, hStdout); - SetStdHandle(STD_INPUT_HANDLE, hStdin); - - // Clear the error state for each of the C++ standard stream objects. - std::wclog.clear(); - std::clog.clear(); - std::wcout.clear(); - std::cout.clear(); - std::wcerr.clear(); - std::cerr.clear(); - std::wcin.clear(); - std::cin.clear(); -} - -void InitializeConsoleOutput() -{ - bool isConsoleApp; - - // How to check if the program is run from a console? - // https://stackoverflow.com/questions/9009333/how-to-check-if-the-program-is-run-from-a-console - HWND consoleWnd = GetConsoleWindow(); - DWORD dwProcessId; - GetWindowThreadProcessId(consoleWnd, &dwProcessId); - - if(GetCurrentProcessId() == dwProcessId) { - isConsoleApp = true; // Opened in Console - } else { - isConsoleApp = false; // Opened in Windows - } - - // Use normal console that launched the program - AttachConsole(ATTACH_PARENT_PROCESS); - - // Test if the application was started from a Console Window - if(!isConsoleApp) { - // If started from Windows, use detached Log Console Window - AllocConsole(); - } - - // Redirect all standard output streams to the console - BindStdHandlesToConsole(); -} -#endif - -void setup() -{ - // Load Settings - - // Init debug log - // debug_init(); - - // Initialize lvgl environment - lv_init(); - lv_log_register_print_cb(debugLvglLogEvent); - - haspDevice.init(); // hardware setup - haspDevice.show_info(); // debug info - // hal_setup(); - guiSetup(); - - LOG_DEBUG(TAG_MAIN, "%s %d", __FILE__, __LINE__); - dispatchSetup(); // for hasp and oobe - haspSetup(); - -#if HASP_USE_MQTT > 0 - LOG_DEBUG(TAG_MAIN, "%s %d", __FILE__, __LINE__); - mqttSetup(); // Hasp must be running - mqttStart(); -#endif - -#if HASP_USE_GPIO > 0 - LOG_DEBUG(TAG_MAIN, "%s %d", __FILE__, __LINE__); - gpioSetup(); -#endif - -#if defined(HASP_USE_CUSTOM) - custom_setup(); -#endif - - mainLastLoopTime = millis(); // - 1000; // reset loop counter - LOG_DEBUG(TAG_MAIN, "%s %d", __FILE__, __LINE__); - // delay(250); -} - -void loop() -{ - haspLoop(); - mqttLoop(); - - // debugLoop(); // Console - haspDevice.loop(); - guiLoop(); - -#if HASP_USE_GPIO > 0 - gpioLoop(); -#endif - -#if defined(HASP_USE_CUSTOM) - custom_loop(); -#endif - -#ifdef HASP_USE_STAT_COUNTER - statLoopCounter++; // measures the average looptime -#endif - - /* Timer Loop */ - if(millis() - mainLastLoopTime >= 1000) { - /* Runs Every Second */ - haspEverySecond(); // sleep timer - dispatchEverySecond(); // sleep timer - -#if HASP_USE_ARDUINOOTA > 0 - otaEverySecond(); // progressbar -#endif - -#if defined(HASP_USE_CUSTOM) - custom_every_second(); -#endif - - /* Runs Every 5 Seconds */ - if(mainLoopCounter == 0 || mainLoopCounter == 5) { - - haspDevice.loop_5s(); - gpioEvery5Seconds(); - -#if defined(HASP_USE_MQTT) - mqttEvery5Seconds(true); -#endif - -#if defined(HASP_USE_CUSTOM) - custom_every_5seconds(); -#endif - } - - /* Reset loop counter every 10 seconds */ - if(mainLoopCounter >= 9) { - mainLoopCounter = 0; - } else { - mainLoopCounter++; - } - mainLastLoopTime += 1000; - } - // delay(6); -} - -void usage(const char* progName, const char* version) -{ - std::cout << "\n\n" - << progName << " " << version << " [options]" << std::endl - << std::endl - << "Options:" << std::endl - << " -? | --help Print this help" << std::endl - << " -w | --width Width of the window" << std::endl - << " -h | --height Height of the window" << std::endl - << " --mqttname MQTT device name topic (default: computer hostname)" << std::endl - << " --mqtthost MQTT broker hostname or IP address" << std::endl - << " --mqttport MQTT broker port (default: 1883)" << std::endl - << " --mqttuser MQTT username" << std::endl - << " --mqttpass MQTT password" << std::endl - << " --mqttgroup MQTT groupname (default: plates)" << std::endl - << std::endl - // << " -t | --topic Base topic of the mqtt messages (default: hasp)" << std::endl - // << std::endl - // << " -f | --fullscreen Open the application fullscreen" << std::endl - // << " -v | --verbose Verbosity level" << std::endl - << std::endl; - fflush(stdout); -#if defined(WINDOWS) - static const char s[] = "\n"; - DWORD slen = lstrlen(s); - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), s, slen, &slen, NULL); -#endif -} - -int main(int argc, char* argv[]) -{ - bool showhelp = false; - int count; - -#if defined(WINDOWS) - InitializeConsoleOutput(); - SetConsoleCP(65001); // 65001 = UTF-8 - static const char s[] = "tränenüberströmt™\n"; - DWORD slen = lstrlen(s); - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), s, slen, &slen, NULL); - - HANDLE std_out = GetStdHandle(STD_OUTPUT_HANDLE); - if(std_out == INVALID_HANDLE_VALUE) { - return 66; - } - if(!WriteConsole(std_out, "Hello World!\n", 13, NULL, NULL)) { - return 67; - } -#endif - - SDL_Init(0); // Needs to be initialized for GetPerfPath - char buf[4096]; // never know how much is needed - std::cout << "CWD: " << cwd(buf, sizeof buf) << std::endl; - cd(SDL_GetPrefPath("hasp", "hasp")); - std::cout << "CWD changed to: " << cwd(buf, sizeof buf) << std::endl; - SDL_Quit(); // We'll properly init later - - // Change to preferences dir - std::cout << "\nCommand-line arguments:\n"; - for(count = 0; count < argc; count++) - std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; - - StaticJsonDocument<1024> settings; - - for(count = 0; count < argc; count++) { - if(argv[count][0] == '-') { - - if(strncmp(argv[count], "--help", 6) == 0 || strncmp(argv[count], "-?", 2) == 0) { - showhelp = true; - } - - if(strncmp(argv[count], "--width", 7) == 0 || strncmp(argv[count], "-w", 2) == 0) { - int w = atoi(argv[count + 1]); - if(w > 0) tft_width = w; - } - - if(strncmp(argv[count], "--height", 8) == 0 || strncmp(argv[count], "-h", 2) == 0) { - int h = atoi(argv[count + 1]); - if(h > 0) tft_height = h; - } - - if(strncmp(argv[count], "--mqttname", 10) == 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]); - settings["mqtt"]["name"] = argv[count + 1]; - } else { - showhelp = true; - } - } - - if(strncmp(argv[count], "--mqtthost", 10) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; - fflush(stdout); - if(count + 1 < argc) { - settings["mqtt"]["host"] = argv[count + 1]; - } else { - showhelp = true; - } - } - - if(strncmp(argv[count], "--mqttport", 10) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; - fflush(stdout); - if(count + 1 < argc) { - settings["mqtt"]["port"] = atoi(argv[count + 1]); - } else { - showhelp = true; - } - } - - if(strncmp(argv[count], "--mqttuser", 10) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; - fflush(stdout); - if(count + 1 < argc) { - settings["mqtt"]["user"] = argv[count + 1]; - } else { - showhelp = true; - } - } - - if(strncmp(argv[count], "--mqttpass", 10) == 0) { - std::cout << " argv[" << count << "] " << argv[count] << std::endl << std::flush; - fflush(stdout); - if(count + 1 < argc) { - settings["mqtt"]["pass"] = argv[count + 1]; - } else { - showhelp = true; - } - } - } - } - - if(showhelp) { - usage("openHASP", haspDevice.get_version()); - -#if defined(WINDOWS) - WriteConsole(std_out, "bye\n\n", 3, NULL, NULL); - std::cout << std::endl << std::flush; - fflush(stdout); - FreeConsole(); - exit(0); -#endif - return 0; - } - - char buffer[2048]; - serializeJson(settings, buffer, sizeof(buffer)); - std::cout << buffer << std::endl << std::flush; - fflush(stdout); - mqttSetConfig(settings["mqtt"]); - // printf("%s %d\n", __FILE__, __LINE__); - // fflush(stdout); - - debugPrintHaspHeader(stdout); - LOG_INFO(TAG_MAIN, "resolution %d x %d", tft_width, tft_height); - LOG_INFO(TAG_MAIN, "pre setup"); - - setup(); - - LOG_TRACE(TAG_MAIN, "loop started"); - while(isRunning) { - loop(); - } - LOG_TRACE(TAG_MAIN, "main loop completed"); - -#if defined(WINDOWS) - WriteConsole(std_out, "bye\n\n", 3, NULL, NULL); - std::cout << std::endl << std::flush; - fflush(stdout); - FreeConsole(); - exit(0); -#endif - - return 0; -} - -#endif diff --git a/src/mqtt/hasp_mqtt_ha.cpp b/src/mqtt/hasp_mqtt_ha.cpp index bf6edb1c..f7040e07 100644 --- a/src/mqtt/hasp_mqtt_ha.cpp +++ b/src/mqtt/hasp_mqtt_ha.cpp @@ -16,7 +16,7 @@ #define RETAINED true -#if defined(WINDOWS) || defined(POSIX) +#if HASP_TARGET_PC extern std::string mqttNodeTopic; extern std::string mqttGroupTopic; #else @@ -35,7 +35,7 @@ const char FP_MQTT_HA_NAME[] PROGMEM = "name"; const char FP_MQTT_HA_MODEL[] PROGMEM = "mdl"; const char FP_MQTT_HA_MANUFACTURER[] PROGMEM = "mf"; -#if !(defined(WINDOWS) || defined(POSIX)) +#if HASP_TARGET_ARDUINO #include "hal/hasp_hal.h" diff --git a/src/mqtt/hasp_mqtt_paho_async.cpp b/src/mqtt/hasp_mqtt_paho_async.cpp index b56783fa..71136d07 100644 --- a/src/mqtt/hasp_mqtt_paho_async.cpp +++ b/src/mqtt/hasp_mqtt_paho_async.cpp @@ -10,6 +10,15 @@ #if HASP_USE_MQTT_ASYNC > 0 #ifdef HASP_USE_PAHO +#if !HASP_USE_CONFIG +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_GROUP[] PROGMEM = "group"; +#endif + /******************************************************************************* * Copyright (c) 2012, 2020 IBM Corp. * @@ -38,7 +47,7 @@ #include "hasp_mqtt.h" // functions to implement here #include "hasp/hasp_dispatch.h" // for dispatch_topic_payload -#include "hasp_debug.h" // for logging +#include "hasp_debug.h" // for logging #if !defined(_WIN32) #include @@ -50,78 +59,131 @@ #include #endif -#define ADDRESS "10.4.0.5:1883" -#define CLIENTID "ExampleClientSub" -#define TOPIC "hasp/plate35/" +// #define ADDRESS "10.4.0.5:1883" +// #define CLIENTID "ExampleClientSub" +// #define TOPIC "hasp/plate35/" #define QOS 1 #define TIMEOUT 10000L -const char* mqttNodeTopic = TOPIC; -const char* mqttGroupTopic = TOPIC; -// 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; std::recursive_mutex dispatch_mtx; std::recursive_mutex publish_mtx; -char mqttServer[MAX_HOSTNAME_LENGTH] = MQTT_HOSTNAME; -char mqttUser[MAX_USERNAME_LENGTH] = MQTT_USERNAME; -char mqttPassword[MAX_PASSWORD_LENGTH] = MQTT_PASSWORD; -// char mqttNodeName[16] = MQTT_NODENAME; -char mqttGroupName[16] = MQTT_GROUPNAME; -uint16_t mqttPort = MQTT_PORT; +std::string mqttServer = MQTT_HOSTNAME; +std::string mqttUsername = MQTT_USERNAME; +std::string mqttPassword = MQTT_PASSWORD; +std::string mqttGroupName = MQTT_GROUPNAME; +uint16_t mqttPort = MQTT_PORT; MQTTAsync mqtt_client; -int disc_finished = 0; -int subscribed = 0; -int connected = 0; +static bool mqttConnecting = false; +static bool mqttConnected = false; -static bool mqttPublish(const char* topic, const char* payload, size_t len, bool retain = false); +int mqttPublish(const char* topic, const char* payload, size_t len, bool retain = false); /* ===== Paho event callbacks ===== */ -void connlost(void* context, char* cause) +static void onConnectFailure(void* context, MQTTAsync_failureData* response) { - printf("\nConnection lost\n"); - if(cause) printf(" cause: %s\n", cause); +#if HASP_TARGET_PC + dispatch_run_script(NULL, "L:/offline.cmd", TAG_HASP); +#endif + mqttConnecting = false; + mqttConnected = false; + LOG_ERROR(TAG_MQTT, "Connection failed, return code %d (%s)", response->code, response->message); +} - printf("Reconnecting\n"); - mqttStart(); +static void onDisconnect(void* context, MQTTAsync_successData* response) +{ +#if HASP_TARGET_PC + dispatch_run_script(NULL, "L:/offline.cmd", TAG_HASP); +#endif + mqttConnecting = false; + mqttConnected = false; +} + +static void onDisconnectFailure(void* context, MQTTAsync_failureData* response) +{ + mqttConnecting = false; + mqttConnected = false; + LOG_ERROR(TAG_MQTT, "Disconnection failed, return code %d (%s)", response->code, response->message); +} + +static void onSendFailure(void* context, MQTTAsync_failureData* response) +{ + LOG_ERROR(TAG_MQTT, "Send failed, return code %d (%s)", response->code, response->message); +} + +static void onSubscribeFailure(void* context, MQTTAsync_failureData* response) +{ + LOG_ERROR(TAG_MQTT, "Subscribe failed, return code %d (%s)", response->code, response->message); +} + +static void connlost(void* context, char* cause) +{ +#if HASP_TARGET_PC + dispatch_run_script(NULL, "L:/offline.cmd", TAG_HASP); +#endif + LOG_WARNING(TAG_MQTT, F(D_MQTT_DISCONNECTED ": %s"), cause); + mqttConnecting = false; + mqttConnected = false; } // Receive incoming messages static void mqtt_message_cb(char* topic, char* payload, size_t length) { // Handle incoming commands from MQTT if(length + 1 >= MQTT_MAX_PACKET_SIZE) { + 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 + topic += mqttGroupTopic.length(); // shorten topic dispatch_mtx.lock(); - dispatch_topic_payload(topic, (const char*)payload); + dispatch_topic_payload(topic, (const char*)payload, length > 0, TAG_MQTT); dispatch_mtx.unlock(); 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_mtx.lock(); + dispatch_topic_payload(topic, (const char*)payload, length > 0, TAG_MQTT); + dispatch_mtx.unlock(); + 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"))) { - dispatch_current_state(); + dispatch_mtx.lock(); + dispatch_current_state(TAG_MQTT); + dispatch_mtx.unlock(); mqtt_ha_register_auto_discovery(); } return; @@ -138,11 +200,8 @@ static void mqtt_message_cb(char* topic, char* payload, size_t length) 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")); - - mqttPublish(tmp_topic, msg, true); + mqttPublish(mqttLwtTopic.c_str(), msg, strlen(msg), true); } } else { @@ -150,175 +209,151 @@ static void mqtt_message_cb(char* topic, char* payload, size_t length) } } else { dispatch_mtx.lock(); - dispatch_topic_payload(topic, (const char*)payload); + dispatch_topic_payload(topic, (const char*)payload, length > 0, TAG_MQTT); dispatch_mtx.unlock(); } } -int msgarrvd(void* context, char* topicName, int topicLen, MQTTAsync_message* message) +static int mqtt_message_arrived(void* context, char* topicName, int topicLen, MQTTAsync_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'; - mqtt_message_cb(topicName, (char*)message->payload, message->payloadlen); + mqtt_message_cb(topicName, msg, message->payloadlen); MQTTAsync_freeMessage(&message); MQTTAsync_free(topicName); - return 1; + return 1; // the message was received properly } -void onDisconnectFailure(void* context, MQTTAsync_failureData* response) -{ - printf("Disconnect failed, rc %d\n", response->code); - disc_finished = 1; -} - -void onDisconnect(void* context, MQTTAsync_successData* response) -{ - printf("Successful disconnection\n"); - disc_finished = 1; - connected = 0; -} - -void onSubscribe(void* context, MQTTAsync_successData* response) -{ - printf("Subscribe succeeded %d\n", response->token); - subscribed = 1; -} - -void onSubscribeFailure(void* context, MQTTAsync_failureData* response) -{ - printf("Subscribe failed, rc %d\n", response->code); -} - -void onConnectFailure(void* context, MQTTAsync_failureData* response) -{ - connected = 0; - printf("Connect failed, rc %d\n", response->code); -} - -void mqtt_subscribe(void* context, const char* topic) +static void mqtt_subscribe(void* context, const char* topic) { MQTTAsync client = (MQTTAsync)context; MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; int rc; - printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n", topic, CLIENTID, QOS); - opts.onSuccess = onSubscribe; opts.onFailure = onSubscribeFailure; opts.context = client; if((rc = MQTTAsync_subscribe(client, topic, QOS, &opts)) != MQTTASYNC_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); } } -void onConnect(void* context, MQTTAsync_successData* response) -{ - MQTTAsync client = (MQTTAsync)context; - connected = 1; - - printf("Successful connection\n"); - - 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 MQTT_TOPIC_LWT, "online", false); - - mqtt_send_object_state(0, 0, "connected"); - std::cout << std::endl; -} - -void onSendFailure(void* context, MQTTAsync_failureData* response) -{ - MQTTAsync client = (MQTTAsync)context; - MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer; - int rc; - - printf("Message send failed token %d error code %d\n", response->token, response->code); - opts.onSuccess = onDisconnect; - opts.onFailure = onDisconnectFailure; - opts.context = client; - if((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS) { - printf("Failed to start disconnect, return code %d\n", rc); - // exit(EXIT_FAILURE); - } -} - -void onSend(void* context, MQTTAsync_successData* response) -{ - MQTTAsync client = (MQTTAsync)context; - MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer; - int rc; - - // printf("Message with token value %d delivery confirmed\n", response->token); - - // opts.onSuccess = onDisconnect; - // opts.onFailure = onDisconnectFailure; - // opts.context = client; - // if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS) - // { - // printf("Failed to start disconnect, return code %d\n", rc); - // exit(EXIT_FAILURE); - // } -} - /* ===== Local HASP MQTT functions ===== */ -static bool mqttPublish(const char* topic, const char* payload, size_t len, bool retain) +int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) { - if(mqttIsConnected()) { - MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; - MQTTAsync_message pubmsg = MQTTAsync_message_initializer; - int rc; + if(!mqttEnabled) return MQTT_ERR_DISABLED; - opts.onSuccess = onSend; - opts.onFailure = onSendFailure; - opts.context = mqtt_client; - pubmsg.payload = (char*)payload; - pubmsg.payloadlen = (int)strlen(payload); - pubmsg.qos = QOS; - pubmsg.retained = 0; - dispatch_mtx.lock(); - if((rc = MQTTAsync_sendMessage(mqtt_client, topic, &pubmsg, &opts)) != MQTTASYNC_SUCCESS) { - dispatch_mtx.unlock(); - LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " %s => %s"), topic, payload); - } else { - dispatch_mtx.unlock(); - LOG_TRACE(TAG_MQTT_PUB, F("%s => %s"), topic, payload); - return true; - } - } else { - LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED)); + if(!mqttIsConnected()) { + mqttFailedCount++; + return MQTT_ERR_NO_CONN; + } + + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + opts.onFailure = onSendFailure; + opts.context = mqtt_client; + pubmsg.payload = (char*)payload; + pubmsg.payloadlen = (int)strlen(payload); + pubmsg.qos = QOS; + pubmsg.retained = 0; + + dispatch_mtx.lock(); + int rc = MQTTAsync_sendMessage(mqtt_client, topic, &pubmsg, &opts); + + if(rc != MQTTASYNC_SUCCESS) { + dispatch_mtx.unlock(); + mqttFailedCount++; + LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " '%s' => %s"), topic, payload); + return MQTT_ERR_PUB_FAIL; + } else { + dispatch_mtx.unlock(); + mqttPublishCount++; + // LOG_TRACE(TAG_MQTT_PUB, F("'%s' => %s OK"), topic, payload); + return MQTT_ERR_OK; } - return false; } /* ===== Public HASP MQTT functions ===== */ bool mqttIsConnected() { - return connected == 1; + return mqttConnected; // MQTTAsync_isConnected(mqtt_client); // <- deadlocking on Linux } -void mqtt_send_state(const __FlashStringHelper* subtopic, const char* payload) +int mqtt_send_state(const __FlashStringHelper* subtopic, const char* payload) { - char tmp_topic[strlen(mqttNodeTopic) + 20]; - 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); + char tmp_topic[mqttNodeTopic.length() + 20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), ("%s" MQTT_TOPIC_STATE "/%s"), mqttNodeTopic.c_str(), subtopic); + return mqttPublish(tmp_topic, payload, strlen(payload), false); } -void mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload) +int mqtt_send_discovery(const char* payload, size_t len) { - char tmp_topic[strlen(mqttNodeTopic) + 20]; - snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" MQTT_TOPIC_STATE "/p%ub%u"), mqttNodeTopic, pageid, btnid); - mqttPublish(tmp_topic, payload, false); + char tmp_topic[128]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR(MQTT_PREFIX "/" MQTT_TOPIC_DISCOVERY "/%s"), + haspDevice.get_hardware_id()); + return mqttPublish(tmp_topic, payload, len, false); +} + +// int mqtt_send_object_state(uint8_t pageid, uint8_t btnid, const char* payload) +// { +// 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); +// } + +static void onConnect(void* context, MQTTAsync_successData* response) +{ + mqttConnecting = false; + mqttConnected = true; + MQTTAsync client = (MQTTAsync)context; + std::string topic; + + LOG_VERBOSE(TAG_MQTT, D_MQTT_CONNECTED, mqttServer.c_str(), haspDevice.get_hostname()); + + 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()); + +#if defined(HASP_USE_CUSTOM) + topic = mqttGroupTopic + MQTT_TOPIC_CUSTOM "/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); + + topic = mqttNodeTopic + MQTT_TOPIC_CUSTOM "/#"; + mqtt_subscribe(mqtt_client, topic.c_str()); +#endif + +#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 + topic = "homeassistant/status"; + mqtt_subscribe(mqtt_client, topic.c_str()); +#endif + + mqttPublish(mqttLwtTopic.c_str(), "online", 6, true); + +#if HASP_TARGET_PC + dispatch_run_script(NULL, "L:/online.cmd", TAG_HASP); +#endif } void mqttStart() @@ -328,47 +363,51 @@ void mqttStart() int rc; int ch; - if((rc = MQTTAsync_create(&mqtt_client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != + if((rc = MQTTAsync_create(&mqtt_client, mqttServer.c_str(), haspDevice.get_hostname(), MQTTCLIENT_PERSISTENCE_NONE, + NULL)) != MQTTASYNC_SUCCESS) { + LOG_ERROR(TAG_MQTT, "Failed to create client, return code %d", rc); + rc = EXIT_FAILURE; + return; + } + + if((rc = MQTTAsync_setCallbacks(mqtt_client, mqtt_client, connlost, mqtt_message_arrived, NULL)) != MQTTASYNC_SUCCESS) { - printf("Failed to create client, return code %d\n", rc); + LOG_ERROR(TAG_MQTT, "Failed to set callbacks, return code %d", rc); rc = EXIT_FAILURE; return; } - if((rc = MQTTAsync_setCallbacks(mqtt_client, mqtt_client, connlost, msgarrvd, NULL)) != MQTTASYNC_SUCCESS) { - printf("Failed to set callbacks, return code %d\n", rc); - rc = EXIT_FAILURE; - return; - } + mqttEnabled = mqttServer.length() > 0 && mqttPort > 0; - conn_opts.will = &will_opts; - conn_opts.will->message = "offline"; - conn_opts.will->qos = 1; - conn_opts.will->retained = 0; - conn_opts.will->topicName = "hasp/plate35/LWT"; + 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.keepAliveInterval = 20; - conn_opts.cleansession = 1; - conn_opts.onSuccess = onConnect; - conn_opts.onFailure = onConnectFailure; - conn_opts.context = mqtt_client; + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.connectTimeout = 2; // seconds + conn_opts.retryInterval = 15; // 0 = no retry + conn_opts.onSuccess = onConnect; + conn_opts.onFailure = onConnectFailure; + conn_opts.context = mqtt_client; - if((rc = MQTTAsync_connect(mqtt_client, &conn_opts)) != MQTTASYNC_SUCCESS) { - printf("Failed to start connect, return code %d\n", rc); - rc = EXIT_FAILURE; - // goto destroy_exit; + conn_opts.username = mqttUsername.c_str(); + conn_opts.password = mqttPassword.c_str(); + + mqttConnecting = true; + if((rc = MQTTAsync_connect(mqtt_client, &conn_opts)) != MQTTASYNC_SUCCESS) { + mqttConnecting = false; + LOG_ERROR(TAG_MQTT, "Failed to connect, return code %d", rc); + rc = EXIT_FAILURE; + // goto destroy_exit; + } } else { + rc = EXIT_FAILURE; + LOG_WARNING(TAG_MQTT, "Mqtt server not configured"); } - - // while (!subscribed && !finished) - // #if defined(_WIN32) - // Sleep(100); - // #else - // usleep(10000L); - // #endif - - // if (finished) - // goto exit; } void mqttStop() @@ -378,30 +417,146 @@ void mqttStop() disc_opts.onSuccess = onDisconnect; disc_opts.onFailure = onDisconnectFailure; if((rc = MQTTAsync_disconnect(mqtt_client, &disc_opts)) != MQTTASYNC_SUCCESS) { - printf("Failed to start disconnect, return code %d\n", rc); + LOG_ERROR(TAG_MQTT, "Failed to disconnect, return code %d", rc); rc = EXIT_FAILURE; - // goto destroy_exit; } - // while (!disc_finished) - // { - // #if defined(_WIN32) - // Sleep(100); - // #else - // usleep(10000L); - // #endif - // } - - // destroy_exit: - // MQTTAsync_destroy(&client); - // exit: - // return rc; } -void mqttSetup(){}; +void mqttSetup() +{ + mqttNodeTopic = MQTT_PREFIX; + mqttNodeTopic += "/"; + mqttNodeTopic += haspDevice.get_hostname(); + mqttNodeTopic += "/"; + + mqttGroupTopic = MQTT_PREFIX; + mqttGroupTopic += "/"; + mqttGroupTopic += mqttGroupName; + mqttGroupTopic += "/"; + + mqttLwtTopic = mqttNodeTopic; + mqttLwtTopic += MQTT_TOPIC_LWT; +} IRAM_ATTR void mqttLoop(){}; -void mqttEvery5Seconds(bool wifiIsConnected){}; +void mqttEvery5Seconds(bool wifiIsConnected) +{ + if(!mqttIsConnected() && !mqttConnecting && mqttServer.length() > 0 && mqttPort > 0) { + LOG_WARNING(TAG_MQTT, F(D_MQTT_RECONNECTING)); + mqttStart(); + } +}; + +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)] = mqttUsername; + info[F(D_INFO_CLIENTID)] = haspDevice.get_hostname(); + info[F(D_INFO_STATUS)] = mqttIsConnected() ? F(D_SERVICE_CONNECTED) : F(D_SERVICE_DISCONNECTED); + info[F(D_INFO_RECEIVED)] = mqttReceiveCount; + info[F(D_INFO_PUBLISHED)] = mqttPublishCount; + info[F(D_INFO_FAILED)] = mqttFailedCount; +} + +bool mqttGetConfig(const JsonObject& settings) +{ + bool changed = false; + + 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; + settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName; + + if(mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as()) 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(mqttUsername != settings[FPSTR(FP_CONFIG_USER)].as()) changed = true; + settings[FPSTR(FP_CONFIG_USER)] = mqttUsername; + + if(mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as()) changed = true; + settings[FPSTR(FP_CONFIG_PASS)] = mqttPassword; + + if(changed) configOutput(settings, TAG_MQTT); + return changed; +} + +/** Set MQTT Configuration. + * + * Read the settings from json and sets the application variables. + * + * @note: data pixel should be formatted to uint32_t RGBA. Imagemagick requirements. + * + * @param[in] settings JsonObject with the config settings. + **/ +bool mqttSetConfig(const JsonObject& settings) +{ + // configOutput(settings, TAG_MQTT); + bool changed = false; + + if(!settings[FPSTR(FP_CONFIG_PORT)].isNull()) { + // changed |= configSet(mqttPort, settings[FPSTR(FP_CONFIG_PORT)], F("mqttPort")); + changed |= mqttPort != settings[FPSTR(FP_CONFIG_PORT)]; + mqttPort = settings[FPSTR(FP_CONFIG_PORT)]; + } + + if(!settings[FPSTR(FP_CONFIG_NAME)].isNull()) { + LOG_VERBOSE(TAG_MQTT, "%s => %s", FP_CONFIG_NAME, settings[FPSTR(FP_CONFIG_NAME)].as()); + changed |= strcmp(haspDevice.get_hostname(), settings[FPSTR(FP_CONFIG_NAME)]) != 0; + // strncpy(mqttNodeName, settings[FPSTR(FP_CONFIG_NAME)], sizeof(mqttNodeName)); + haspDevice.set_hostname(settings[FPSTR(FP_CONFIG_NAME)].as()); + } + // Prefill node name + // if(strlen(haspDevice.get_hostname()) == 0) { + // char mqttNodeName[64]; + // std::string mac = halGetMacAddress(3, ""); + // mac.toLowerCase(); + // snprintf_P(mqttNodeName, sizeof(mqttNodeName), PSTR(D_MQTT_DEFAULT_NAME), mac.c_str()); + // haspDevice.set_hostname(mqttNodeName); + // changed = true; + // } + + if(!settings[FPSTR(FP_CONFIG_GROUP)].isNull()) { + changed |= mqttGroupName != settings[FPSTR(FP_CONFIG_GROUP)]; + mqttGroupName = settings[FPSTR(FP_CONFIG_GROUP)].as(); + } + + if(mqttGroupName.length() == 0) { + mqttGroupName = "plates"; + changed = true; + } + + if(!settings[FPSTR(FP_CONFIG_HOST)].isNull()) { + LOG_VERBOSE(TAG_MQTT, "%s => %s", FP_CONFIG_HOST, settings[FPSTR(FP_CONFIG_HOST)].as()); + changed |= mqttServer != settings[FPSTR(FP_CONFIG_HOST)]; + mqttServer = settings[FPSTR(FP_CONFIG_HOST)].as(); + } + + if(!settings[FPSTR(FP_CONFIG_USER)].isNull()) { + changed |= mqttUsername != settings[FPSTR(FP_CONFIG_USER)]; + mqttUsername = settings[FPSTR(FP_CONFIG_USER)].as(); + } + + if(!settings[FPSTR(FP_CONFIG_PASS)].isNull() && + settings[FPSTR(FP_CONFIG_PASS)].as() != D_PASSWORD_MASK) { + changed |= mqttPassword != settings[FPSTR(FP_CONFIG_PASS)]; + mqttPassword = settings[FPSTR(FP_CONFIG_PASS)].as(); + } + + mqttNodeTopic = MQTT_PREFIX; + mqttNodeTopic += haspDevice.get_hostname(); + mqttGroupTopic = MQTT_PREFIX; + mqttGroupTopic += mqttGroupName; + + return changed; +} #endif // HASP_USE_PAHO #endif // USE_MQTT diff --git a/src/mqtt/hasp_mqtt_paho_single.cpp b/src/mqtt/hasp_mqtt_paho_single.cpp index 17804f89..653a884f 100644 --- a/src/mqtt/hasp_mqtt_paho_single.cpp +++ b/src/mqtt/hasp_mqtt_paho_single.cpp @@ -5,15 +5,17 @@ #include "hasplib.h" -#if HASP_USE_MQTT > 0 +#if HASP_USE_MQTT > 0 && !HASP_USE_MQTT_ASYNC #ifdef HASP_USE_PAHO +#if !HASP_USE_CONFIG 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_GROUP[] PROGMEM = "group"; +#endif /******************************************************************************* * Copyright (c) 2012, 2020 IBM Corp. @@ -411,6 +413,32 @@ void mqtt_get_info(JsonDocument& doc) info[F(D_INFO_FAILED)] = mqttFailedCount; } +bool mqttGetConfig(const JsonObject& settings) +{ + bool changed = false; + + 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; + settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName; + + if(mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as()) 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(mqttUsername != settings[FPSTR(FP_CONFIG_USER)].as()) changed = true; + settings[FPSTR(FP_CONFIG_USER)] = mqttUsername; + + if(mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as()) changed = true; + settings[FPSTR(FP_CONFIG_PASS)] = mqttPassword; + + if(changed) configOutput(settings, TAG_MQTT); + return changed; +} + /** Set MQTT Configuration. * * Read the settings from json and sets the application variables. diff --git a/src/sys/svc/hasp_console.cpp b/src/sys/svc/hasp_console.cpp index 66312569..04ab243f 100644 --- a/src/sys/svc/hasp_console.cpp +++ b/src/sys/svc/hasp_console.cpp @@ -5,8 +5,10 @@ #if HASP_USE_CONSOLE > 0 +#if HASP_TARGET_ARDUINO #include "ConsoleInput.h" #include +#endif #include "hasp_debug.h" #include "hasp_console.h" @@ -17,15 +19,18 @@ extern hasp_http_config_t http_config; #endif +#if HASP_TARGET_ARDUINO // Create a new Stream that buffers all writes to serialClient HardwareSerial* bufferedSerialClient = (HardwareSerial*)&HASP_SERIAL; +ConsoleInput* console; +#endif uint8_t consoleLoginState = CONSOLE_UNAUTHENTICATED; uint16_t serialPort = 0; uint8_t consoleEnabled = true; // Enable serial debug output uint8_t consoleLoginAttempt = 0; // Initial attempt -ConsoleInput* console; +#if HASP_TARGET_ARDUINO void console_update_prompt() { if(console) console->update(__LINE__); @@ -101,9 +106,21 @@ static void console_process_line(const char* input) } } } +#elif HASP_TARGET_PC +static bool console_running = true; +static void console_thread(void* arg) +{ + while(console_running) { + std::string input; + std::getline(std::cin, input); + dispatch_text_line(input.c_str(), TAG_CONS); + } +} +#endif void consoleStart() { +#if HASP_TARGET_ARDUINO LOG_TRACE(TAG_MSGR, F(D_SERVICE_STARTING)); console = new ConsoleInput(bufferedSerialClient, HASP_CONSOLE_BUFFER); if(console) { @@ -126,16 +143,27 @@ void consoleStart() console_logoff(); LOG_ERROR(TAG_CONS, F(D_SERVICE_START_FAILED)); } +#elif HASP_TARGET_PC + LOG_TRACE(TAG_MSGR, F(D_SERVICE_STARTING)); + haspDevice.run_thread(console_thread, NULL); +#endif } void consoleStop() { +#if HASP_TARGET_ARDUINO console_logoff(); Log.unregisterOutput(0); // serialClient HASP_SERIAL.end(); - delete console; console = NULL; +#elif HASP_TARGET_PC +#if defined(WINDOWS) + +#elif defined(POSIX) + +#endif +#endif } void consoleSetup() @@ -147,6 +175,7 @@ void consoleSetup() IRAM_ATTR void consoleLoop() { +#if HASP_TARGET_ARDUINO if(!console) return; bool update = false; @@ -168,13 +197,14 @@ IRAM_ATTR void consoleLoop() case 0: case -1: break; - + default: { update = true; } } } if(update) console_update_prompt(); +#endif } #if HASP_USE_CONFIG > 0 @@ -201,4 +231,4 @@ bool consoleSetConfig(const JsonObject& settings) } #endif // HASP_USE_CONFIG -#endif \ No newline at end of file +#endif diff --git a/src/sys/svc/hasp_console.h b/src/sys/svc/hasp_console.h index 10c17b69..18834508 100644 --- a/src/sys/svc/hasp_console.h +++ b/src/sys/svc/hasp_console.h @@ -4,10 +4,10 @@ #ifndef HASP_CONSOLE_H #define HASP_CONSOLE_H -#if HASP_USE_CONSOLE > 0 - #include "hasplib.h" +#if HASP_USE_CONSOLE > 0 + /* ===== Default Event Processors ===== */ void consoleSetup(); IRAM_ATTR void consoleLoop(void); diff --git a/src/sys/svc/hasp_http.cpp b/src/sys/svc/hasp_http.cpp index 1b91f801..eba778bb 100644 --- a/src/sys/svc/hasp_http.cpp +++ b/src/sys/svc/hasp_http.cpp @@ -2,6 +2,9 @@ For full license information read the LICENSE file in the project folder */ #include "hasplib.h" + +#if HASP_USE_HTTP > 0 + #include "ArduinoLog.h" #define HTTP_LEGACY @@ -21,7 +24,6 @@ #include "hasp_gui.h" #include "hasp_debug.h" -#if HASP_USE_HTTP > 0 #include "sys/net/hasp_network.h" #include "sys/net/hasp_time.h" diff --git a/src/sys/svc/hasp_http_async.cpp b/src/sys/svc/hasp_http_async.cpp index 2c8ec707..683d2f50 100644 --- a/src/sys/svc/hasp_http_async.cpp +++ b/src/sys/svc/hasp_http_async.cpp @@ -3,6 +3,9 @@ //#include "webServer.h" #include "hasplib.h" + +#if HASP_USE_HTTP_ASYNC > 0 + #include "ArduinoLog.h" #if defined(ARDUINO_ARCH_ESP32) @@ -16,7 +19,6 @@ #include "hasp_gui.h" #include "hasp_debug.h" -#if HASP_USE_HTTP_ASYNC > 0 #include "sys/net/hasp_network.h" /* clang-format off */ diff --git a/user_setups/darwin_sdl/darwin_sdl_64bits.ini b/user_setups/darwin/darwin_sdl.ini similarity index 73% rename from user_setups/darwin_sdl/darwin_sdl_64bits.ini rename to user_setups/darwin/darwin_sdl.ini index a1e64062..2b3a3642 100644 --- a/user_setups/darwin_sdl/darwin_sdl_64bits.ini +++ b/user_setups/darwin/darwin_sdl.ini @@ -1,4 +1,4 @@ -[env:darwin_sdl_64bits] +[env:darwin_sdl] lib_archive = false platform = native@^1.1.4 extra_scripts = @@ -6,7 +6,8 @@ extra_scripts = tools/linux_build_extra.py build_flags = ${env.build_flags} - -D HASP_MODEL="MacOS X App" + -D HASP_MODEL="MacOS X App" + -D HASP_TARGET_PC=1 ; ----- Monitor -D TFT_WIDTH=240 @@ -25,8 +26,8 @@ build_flags = -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=1 - -D HASP_USE_CONFIG=0 ; Standalone application, as library + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=1 -D HASP_USE_DEBUG=1 -D HASP_USE_PNGDECODE=1 -D HASP_USE_BMPDECODE=1 @@ -48,8 +49,8 @@ build_flags = -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ;-D NO_PERSISTENCE - -I.pio/libdeps/darwin_sdl_64bits/paho/src - -I.pio/libdeps/darwin_sdl_64bits/ArduinoJson/src + -I.pio/libdeps/darwin_sdl/paho/src + -I.pio/libdeps/darwin_sdl/ArduinoJson/src -I lib/ArduinoJson/src -I lib/lv_fs_if !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" @@ -84,21 +85,20 @@ lib_ignore = build_src_filter = +<*> -<*.h> - +<../hal/sdl2> - +<../.pio/libdeps/darwin_sdl_64bits/paho/src/*.c> - +<../.pio/libdeps/darwin_sdl_64bits/paho/src/MQTTClient.c> - +<../.pio/libdeps/darwin_sdl_64bits/paho/src/MQTTClient.h> - -<../.pio/libdeps/darwin_sdl_64bits/paho/src/MQTTAsync.c> - -<../.pio/libdeps/darwin_sdl_64bits/paho/src/MQTTAsyncUtils.c> - -<../.pio/libdeps/darwin_sdl_64bits/paho/src/MQTTVersion.c> - -<../.pio/libdeps/darwin_sdl_64bits/paho/src/SSLSocket.c> - + - - - - + +<../.pio/libdeps/darwin_sdl/paho/src/*.c> + -<../.pio/libdeps/darwin_sdl/paho/src/MQTTClient.c> + +<../.pio/libdeps/darwin_sdl/paho/src/MQTTAsync.c> + +<../.pio/libdeps/darwin_sdl/paho/src/MQTTAsyncUtils.c> + -<../.pio/libdeps/darwin_sdl/paho/src/MQTTVersion.c> + -<../.pio/libdeps/darwin_sdl/paho/src/SSLSocket.c> + - + + + + - - - + + + - + - @@ -112,4 +112,4 @@ build_src_filter = + - + - +<../.pio/libdeps/darwin_sdl_64bits/ArduinoJson/src/ArduinoJson.h> + +<../.pio/libdeps/darwin_sdl/ArduinoJson/src/ArduinoJson.h> diff --git a/user_setups/esp32/_esp32.ini b/user_setups/esp32/_esp32.ini index 93c493ed..aabb2678 100644 --- a/user_setups/esp32/_esp32.ini +++ b/user_setups/esp32/_esp32.ini @@ -35,6 +35,7 @@ files = build_flags = ${env.build_flags} + -D HASP_TARGET_ARDUINO=1 -D HTTP_UPLOAD_BUFLEN=1024 ; lower http upload buffer -D MQTT_MAX_PACKET_SIZE=2048 ; longer PubSubClient messages -D HASP_CONSOLE_BUFFER=256 ; maximum length of a console/telnet command diff --git a/user_setups/esp8266/_esp8266.ini b/user_setups/esp8266/_esp8266.ini index ad231166..e9ffe212 100644 --- a/user_setups/esp8266/_esp8266.ini +++ b/user_setups/esp8266/_esp8266.ini @@ -12,6 +12,7 @@ board_build.f_cpu = 160000000L ; set frequency to 160MHz monitor_filters = esp8266_exception_decoder build_flags= + -D HASP_TARGET_ARDUINO=1 -D HTTP_UPLOAD_BUFLEN=512 ; lower http upload buffer -D MQTT_MAX_PACKET_SIZE=1024 ; longer PubSubClient messages -D HASP_CONSOLE_BUFFER=160 ; maximum length of a console/telnet command diff --git a/user_setups/linux/linux_fbdev.ini b/user_setups/linux/linux_fbdev.ini new file mode 100644 index 00000000..c0d70a27 --- /dev/null +++ b/user_setups/linux/linux_fbdev.ini @@ -0,0 +1,101 @@ +[env:linux_fbdev] +platform = native@^1.1.4 +extra_scripts = + tools/linux_build_extra.py +build_flags = + ${env.build_flags} + -D HASP_MODEL="Linux App" + -D HASP_TARGET_PC=1 + + ; ----- Monitor + -D TFT_WIDTH=240 + -D TFT_HEIGHT=320 + ; SDL drivers options + ;-D LV_LVGL_H_INCLUDE_SIMPLE + ;-D LV_DRV_NO_CONF + -D USE_FBDEV + -D USE_EVDEV + ; ----- ArduinoJson + -D ARDUINOJSON_DECODE_UNICODE=1 + -D HASP_NUM_PAGES=12 + -D HASP_USE_SPIFFS=0 + -D HASP_USE_LITTLEFS=0 + -D LV_USE_FS_IF=1 + -D HASP_USE_EEPROM=0 + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=1 + -D HASP_USE_DEBUG=1 + -D HASP_USE_PNGDECODE=1 + -D HASP_USE_BMPDECODE=1 + -D HASP_USE_GIFDECODE=0 + -D HASP_USE_JPGDECODE=0 + -D HASP_USE_MQTT=1 + -D HASP_USE_LVGL_TASK=1 + -D MQTT_MAX_PACKET_SIZE=2048 + -D HASP_ATTRIBUTE_FAST_MEM= + -D IRAM_ATTR= ; No IRAM_ATTR available + -D PROGMEM= ; No PROGMEM available + ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO + ;-D LV_LOG_PRINTF=1 + ; Add recursive dirs for hal headers search + -D POSIX + -D PAHO_MQTT_STATIC + -DPAHO_WITH_SSL=TRUE + -DPAHO_BUILD_DOCUMENTATION=FALSE + -DPAHO_BUILD_SAMPLES=FALSE + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_VERBOSE_MAKEFILE=TRUE + ;-D NO_PERSISTENCE + -I.pio/libdeps/linux_fbdev/paho/src + -I.pio/libdeps/linux_fbdev/ArduinoJson/src + + ; ----- Statically linked libraries -------------------- + -lm + -lpthread + +lib_deps = + ${env.lib_deps} + ;lv_drivers@~7.9.0 + ;lv_drivers=https://github.com/littlevgl/lv_drivers/archive/7d71907c1d6b02797d066f50984b866e080ebeed.zip + https://github.com/eclipse/paho.mqtt.c.git + bblanchon/ArduinoJson@^6.21.4 ; Json(l) parser + https://github.com/fvanroie/lv_drivers + +lib_ignore = + paho + AXP192 + ArduinoLog + lv_lib_qrcode + ETHSPI + +build_src_filter = + +<*> + -<*.h> + +<../.pio/libdeps/linux_fbdev/paho/src/*.c> + -<../.pio/libdeps/linux_fbdev/paho/src/MQTTClient.c> + +<../.pio/libdeps/linux_fbdev/paho/src/MQTTAsync.c> + +<../.pio/libdeps/linux_fbdev/paho/src/MQTTAsyncUtils.c> + -<../.pio/libdeps/linux_fbdev/paho/src/MQTTVersion.c> + -<../.pio/libdeps/linux_fbdev/paho/src/SSLSocket.c> + - + + + + + - + - + - + + + + + - + + + - + + + + + - + - + - + + + + + + + - + + + +<../.pio/libdeps/linux_fbdev/ArduinoJson/src/ArduinoJson.h> diff --git a/user_setups/linux_sdl/linux_sdl_64bits.ini b/user_setups/linux/linux_sdl.ini similarity index 74% rename from user_setups/linux_sdl/linux_sdl_64bits.ini rename to user_setups/linux/linux_sdl.ini index e68c716b..e1239f7d 100644 --- a/user_setups/linux_sdl/linux_sdl_64bits.ini +++ b/user_setups/linux/linux_sdl.ini @@ -1,4 +1,4 @@ -[env:linux_sdl_64bits] +[env:linux_sdl] platform = native@^1.1.4 extra_scripts = tools/sdl2_build_extra.py @@ -6,6 +6,7 @@ extra_scripts = build_flags = ${env.build_flags} -D HASP_MODEL="Linux App" + -D HASP_TARGET_PC=1 ; ----- Monitor -D TFT_WIDTH=240 @@ -25,8 +26,8 @@ build_flags = -D HASP_USE_LITTLEFS=0 -D LV_USE_FS_IF=1 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=1 - -D HASP_USE_CONFIG=0 ; Standalone application, as library + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=1 -D HASP_USE_DEBUG=1 -D HASP_USE_PNGDECODE=1 -D HASP_USE_BMPDECODE=1 @@ -48,8 +49,8 @@ build_flags = -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ;-D NO_PERSISTENCE - -I.pio/libdeps/linux_sdl_64bits/paho/src - -I.pio/libdeps/linux_sdl_64bits/ArduinoJson/src + -I.pio/libdeps/linux_sdl/paho/src + -I.pio/libdeps/linux_sdl/ArduinoJson/src !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" ; ----- Statically linked libraries -------------------- @@ -75,20 +76,20 @@ lib_ignore = build_src_filter = +<*> -<*.h> - +<../hal/sdl2> - +<../.pio/libdeps/linux_sdl_64bits/paho/src/*.c> - +<../.pio/libdeps/linux_sdl_64bits/paho/src/MQTTClient.c> - -<../.pio/libdeps/linux_sdl_64bits/paho/src/MQTTAsync.c> - -<../.pio/libdeps/linux_sdl_64bits/paho/src/MQTTAsyncUtils.c> - -<../.pio/libdeps/linux_sdl_64bits/paho/src/MQTTVersion.c> - -<../.pio/libdeps/linux_sdl_64bits/paho/src/SSLSocket.c> - + - - - - + +<../.pio/libdeps/linux_sdl/paho/src/*.c> + -<../.pio/libdeps/linux_sdl/paho/src/MQTTClient.c> + +<../.pio/libdeps/linux_sdl/paho/src/MQTTAsync.c> + +<../.pio/libdeps/linux_sdl/paho/src/MQTTAsyncUtils.c> + -<../.pio/libdeps/linux_sdl/paho/src/MQTTVersion.c> + -<../.pio/libdeps/linux_sdl/paho/src/SSLSocket.c> + - + + + + - - - + + + - + - @@ -102,4 +103,4 @@ build_src_filter = + - + - +<../.pio/libdeps/linux_sdl_64bits/ArduinoJson/src/ArduinoJson.h> + +<../.pio/libdeps/linux_sdl/ArduinoJson/src/ArduinoJson.h> diff --git a/user_setups/stm32f4xx/_stm32f4.ini b/user_setups/stm32f4xx/_stm32f4.ini index 20a755d3..7ec27e01 100644 --- a/user_setups/stm32f4xx/_stm32f4.ini +++ b/user_setups/stm32f4xx/_stm32f4.ini @@ -3,6 +3,7 @@ framework = arduino platform = ststm32 build_flags= + -D HASP_TARGET_ARDUINO=1 -I include/stm32f4 -D MQTT_MAX_PACKET_SIZE=2048 ; longer PubSubClient messages -D HASP_CONSOLE_BUFFER=220 ; maximum length of a console/telnet command @@ -21,4 +22,4 @@ build_flags= lib_deps = stm32duino/STM32duino LwIP @ ^2.1.2 - ;https://github.com/stm32duino/LwIP.git \ No newline at end of file + ;https://github.com/stm32duino/LwIP.git diff --git a/user_setups/stm32f7xx/_stm32f7.ini b/user_setups/stm32f7xx/_stm32f7.ini index 192119d5..17fa84a6 100644 --- a/user_setups/stm32f7xx/_stm32f7.ini +++ b/user_setups/stm32f7xx/_stm32f7.ini @@ -3,6 +3,7 @@ framework = arduino platform = ststm32 build_flags= + -D HASP_TARGET_ARDUINO=1 ; -I include/stm32f4 -D MQTT_MAX_PACKET_SIZE=2048 ; longer PubSubClient messages -D HASP_CONSOLE_BUFFER=220 ; maximum length of a console/telnet command diff --git a/user_setups/win32/windows_gdi.ini b/user_setups/win32/windows_gdi.ini new file mode 100644 index 00000000..00b6ee1f --- /dev/null +++ b/user_setups/win32/windows_gdi.ini @@ -0,0 +1,131 @@ +[env:windows_gdi] +platform = native@^1.1.4 +extra_scripts = + tools/windows_build_extra.py +build_flags = + ${env.build_flags} + -D HASP_MODEL="Windows App" + -D HASP_TARGET_PC=1 + + ; ----- Monitor + -D TFT_WIDTH=240 + -D TFT_HEIGHT=320 + ; SDL drivers options + ;-D LV_LVGL_H_INCLUDE_SIMPLE + ;-D LV_DRV_NO_CONF + -D USE_WIN32DRV + ; ----- ArduinoJson + -D ARDUINOJSON_DECODE_UNICODE=1 + -D HASP_NUM_PAGES=12 + -D HASP_USE_SPIFFS=0 + -D HASP_USE_LITTLEFS=0 + -D HASP_USE_EEPROM=0 + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=1 + -D HASP_USE_DEBUG=1 + -D HASP_USE_PNGDECODE=1 + -D HASP_USE_BMPDECODE=1 + -D HASP_USE_GIFDECODE=0 + -D HASP_USE_JPGDECODE=0 + -D HASP_USE_MQTT=1 + -D HASP_USE_SYSLOG=0 + -D HASP_USE_LVGL_TASK=1 + -D MQTT_MAX_PACKET_SIZE=2048 + -D HASP_ATTRIBUTE_FAST_MEM= + -D IRAM_ATTR= ; No IRAM_ATTR available + -D PROGMEM= ; No PROGMEM available + + ; -- FreeType build options ------------------------ + -D LV_USE_FT_CACHE_MANAGER=1 ; crashes without cache + -D LVGL_FREETYPE_MAX_FACES=64 ; max number of FreeType faces in cache + -D LVGL_FREETYPE_MAX_SIZES=4 ; max number of sizes in cache + -D LVGL_FREETYPE_MAX_BYTES=16384 ; max bytes in cache + -D LVGL_FREETYPE_MAX_BYTES_PSRAM=65536 ; max bytes in cache when using PSRAM + + ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO + ;-D LV_LOG_PRINTF=1 + ; Add recursive dirs for hal headers search + -D _WIN64 + -D WINDOWS ; We add this ourselves for code branching in hasp + -D WIN32_LEAN_AND_MEAN ; exclude a bunch of Windows header files from windows.h + -D PAHO_MQTT_STATIC + -DPAHO_WITH_SSL=TRUE + -DPAHO_BUILD_DOCUMENTATION=FALSE + -DPAHO_BUILD_SAMPLES=FALSE + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_VERBOSE_MAKEFILE=TRUE + ;-D NO_PERSISTENCE + -I.pio/libdeps/windows_gdi/paho/src + -I.pio/libdeps/windows_gdi/ArduinoJson/src + -I lib/lv_fs_if + -I lib/lv_datetime + -mconsole + ; ----- Statically linked libraries -------------------- + -l"ws2_32" ;windsock2 + -lrpcrt4 + -lcrypt32 + -lmingw32 + -lm + -ldinput8 + ;-ldxguid + ;-ldxerr8 + ;-luser32 + -lgdi32 + -lwinmm + -limm32 + -lole32 + -loleaut32 + ;-lshell32 + -lversion + ;-luuid + -lsetupapi + -lshlwapi + ;-lhid + +lib_deps = + ${env.lib_deps} + ;lv_drivers@~7.9.0 + ;lv_drivers=https://github.com/littlevgl/lv_drivers/archive/7d71907c1d6b02797d066f50984b866e080ebeed.zip + https://github.com/eclipse/paho.mqtt.c.git + bblanchon/ArduinoJson@^6.21.4 ; Json(l) parser + https://github.com/fvanroie/lv_drivers + +lib_ignore = + paho + AXP192 + ArduinoLog + lv_lib_qrcode + ETHSPI + +build_src_filter = + +<*> + -<*.h> + +<../.pio/libdeps/windows_gdi/paho/src/*.c> + -<../.pio/libdeps/windows_gdi/paho/src/MQTTClient.c> + +<../.pio/libdeps/windows_gdi/paho/src/MQTTAsync.c> + +<../.pio/libdeps/windows_gdi/paho/src/MQTTAsyncUtils.c> + -<../.pio/libdeps/windows_gdi/paho/src/MQTTVersion.c> + -<../.pio/libdeps/windows_gdi/paho/src/SSLSocket.c> + - + + + + + - + - + - + + + + + - + + + - + + + + + - + - + - + + + + + + + - + + + +<../.pio/libdeps/windows_gdi/ArduinoJson/src/ArduinoJson.h> + + diff --git a/user_setups/win32/windows_sdl_64bits.ini b/user_setups/win32/windows_sdl.ini similarity index 79% rename from user_setups/win32/windows_sdl_64bits.ini rename to user_setups/win32/windows_sdl.ini index babadf77..30364c1c 100644 --- a/user_setups/win32/windows_sdl_64bits.ini +++ b/user_setups/win32/windows_sdl.ini @@ -1,4 +1,4 @@ -[env:windows_sdl_64bits] +[env:windows_sdl] platform = native@^1.1.4 extra_scripts = tools/sdl2_build_extra.py @@ -6,6 +6,7 @@ extra_scripts = build_flags = ${env.build_flags} -D HASP_MODEL="Windows App" + -D HASP_TARGET_PC=1 ; ----- Monitor -D TFT_WIDTH=240 @@ -24,8 +25,8 @@ build_flags = -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 -D HASP_USE_EEPROM=0 - -D HASP_USE_GPIO=1 - -D HASP_USE_CONFIG=0 ; Standalone application, as library + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=1 -D HASP_USE_DEBUG=1 -D HASP_USE_PNGDECODE=1 -D HASP_USE_BMPDECODE=1 @@ -58,8 +59,8 @@ build_flags = -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ;-D NO_PERSISTENCE - -I.pio/libdeps/windows_sdl_64bits/paho/src - -I.pio/libdeps/windows_sdl_64bits/ArduinoJson/src + -I.pio/libdeps/windows_sdl/paho/src + -I.pio/libdeps/windows_sdl/ArduinoJson/src -I lib/lv_fs_if -I lib/lv_datetime !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" @@ -71,13 +72,12 @@ build_flags = -lmingw32 -lSDL2main -lSDL2 - -mwindows -lm -ldinput8 ;-ldxguid ;-ldxerr8 ;-luser32 - ;-lgdi32 + -lgdi32 -lwinmm -limm32 -lole32 @@ -106,20 +106,20 @@ lib_ignore = build_src_filter = +<*> -<*.h> - +<../hal/sdl2> - +<../.pio/libdeps/windows_sdl_64bits/paho/src/*.c> - +<../.pio/libdeps/windows_sdl_64bits/paho/src/MQTTClient.c> - -<../.pio/libdeps/windows_sdl_64bits/paho/src/MQTTAsync.c> - -<../.pio/libdeps/windows_sdl_64bits/paho/src/MQTTAsyncUtils.c> - -<../.pio/libdeps/windows_sdl_64bits/paho/src/MQTTVersion.c> - -<../.pio/libdeps/windows_sdl_64bits/paho/src/SSLSocket.c> - + - - - - + +<../.pio/libdeps/windows_sdl/paho/src/*.c> + -<../.pio/libdeps/windows_sdl/paho/src/MQTTClient.c> + +<../.pio/libdeps/windows_sdl/paho/src/MQTTAsync.c> + +<../.pio/libdeps/windows_sdl/paho/src/MQTTAsyncUtils.c> + -<../.pio/libdeps/windows_sdl/paho/src/MQTTVersion.c> + -<../.pio/libdeps/windows_sdl/paho/src/SSLSocket.c> + - + + + + - - - + + + - + - @@ -133,5 +133,5 @@ build_src_filter = + - + - +<../.pio/libdeps/windows_sdl_64bits/ArduinoJson/src/ArduinoJson.h> + +<../.pio/libdeps/windows_sdl/ArduinoJson/src/ArduinoJson.h> +