Merge pull request #631 from kuba2k2/feature/pc-port

Improve PC port feature support (Win32/Linux)
This commit is contained in:
fvanroie 2024-02-22 16:02:30 +01:00 committed by GitHub
commit 837061b380
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 1987 additions and 869 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,4 @@
#if USE_MONITOR
#include <unistd.h>
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/
#include <SDL2/SDL.h>
@ -53,3 +54,4 @@ void hal_loop(void)
// lv_task_handler();
// }
}
#endif

View File

@ -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<const __FlashStringHelper*>(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 <iostream>
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<const __FlashStringHelper*>(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 <Windows.h>
#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 <string.h>
#include <strings.h>
#include <stdio.h>
#if USE_MONITOR
#include <SDL2/SDL.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -5,7 +5,7 @@
* INCLUDES
*********************/
#if !(defined(WINDOWS) || defined(POSIX) || defined(STM32F7xx))
#if !(HASP_TARGET_PC || defined(STM32F7xx))
#include <Arduino.h>
#include <stdio.h>

View File

@ -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

View File

@ -8,7 +8,7 @@
#include "Arduino.h"
#endif
#if defined(WINDOWS) || defined(POSIX)
#if HASP_TARGET_PC
#include <cstdint>
#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()
{}

View File

@ -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 <fstream>
#include <unistd.h>
// 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<std::string>()) {
haspTft.fbdev_path = "/dev/" + settings["fbdev"].as<std::string>();
}
#if USE_EVDEV
if(settings["evdev"].is<std::string>()) {
haspTft.evdev_names.push_back(settings["evdev"].as<std::string>());
}
if(settings["evdevs"].is<JsonArray>()) {
for(auto v : settings["evdevs"].as<JsonArray>()) {
haspTft.evdev_names.push_back(v.as<std::string>());
}
}
#endif
if(settings["bldev"].is<std::string>()) {
haspDevice.backlight_device = settings["bldev"].as<std::string>();
}
if(settings["blmax"].is<int>()) {
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

View File

@ -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;

View File

@ -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
#endif // WINDOWS

View File

@ -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
#endif // HASP_DEVICE_WINDOWS_H

View File

@ -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;

View File

@ -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 <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <algorithm>
#include <fstream>
#include <linux/vt.h>
#if USE_BSD_EVDEV
#include <dev/evdev/input.h>
#else
#include <linux/input.h>
#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

View File

@ -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 <vector>
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<std::string> 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

View File

@ -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
#endif // WINDOWS || POSIX

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -11,7 +11,7 @@
#include "ArduinoLog.h"
#endif
#if defined(WINDOWS) || defined(POSIX)
#if HASP_TARGET_PC
#include <iostream>
#include <fstream>
#include <sstream>

View File

@ -16,7 +16,7 @@
#include "../hasp_debug.h"
#include "hasp_gui.h" // for screenshot
#if defined(WINDOWS) || defined(POSIX)
#if HASP_TARGET_PC
#include <iostream>
#include <fstream>
#include <sstream>
@ -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 */

View File

@ -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
#endif

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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
#endif

View File

@ -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<JsonObject>().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<JsonObject>().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<String>();
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<String>();
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<String>();
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<String>();
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<String>();
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
#endif // HAS_USE_CONFIG

View File

@ -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
#endif // HASP_USE_CONFIG

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <iostream>
@ -21,6 +21,18 @@
#define debug_print(io, ...) fprintf(stdout, __VA_ARGS__)
#define debug_newline(io) fprintf(stdout, "\n")
#if defined(WINDOWS)
#include <windows.h>
#include <direct.h>
#define cwd _getcwd
#endif
#if defined(POSIX)
#include <unistd.h>
#include <linux/limits.h>
#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
}
}

View File

@ -17,7 +17,7 @@
#include "lang/lang.h"
#if(!defined(WINDOWS)) && (!defined(POSIX))
#if HASP_TARGET_ARDUINO
/* ===== Default Event Processors ===== */
void debugSetup(JsonObject settings);

View File

@ -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

View File

@ -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
#endif // HASP_GUI_H

View File

@ -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

226
src/main_pc.cpp Normal file
View File

@ -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 <windows.h>
#include <direct.h>
#include <shlobj.h>
#include <shlwapi.h>
// MSDN recommends against using getcwd & chdir names
#define cwd _getcwd
#define cd _chdir
#endif
#if defined(POSIX)
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sys/types.h>
#include <pwd.h>
#define cwd getcwd
#define cd chdir
#endif
#include <cstdlib>
#include <iostream>
#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

View File

@ -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 <windows.h>
#include <direct.h>
// MSDN recommends against using getcwd & chdir names
#define cwd _getcwd
#define cd _chdir
#endif
#if defined(POSIX)
#include <netdb.h>
#include <unistd.h>
#define cwd getcwd
#define cd chdir
#endif
#include <cstdlib>
#include <iostream>
#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

View File

@ -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"

View File

@ -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 <unistd.h>
@ -50,78 +59,131 @@
#include <OsWrapper.h>
#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<String>().c_str()) != 0) changed = true;
settings[FPSTR(FP_CONFIG_NAME)] = haspDevice.get_hostname();
if(mqttGroupName != settings[FPSTR(FP_CONFIG_GROUP)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName;
if(mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_HOST)] = mqttServer;
if(mqttPort != settings[FPSTR(FP_CONFIG_PORT)].as<uint16_t>()) changed = true;
settings[FPSTR(FP_CONFIG_PORT)] = mqttPort;
if(mqttUsername != settings[FPSTR(FP_CONFIG_USER)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_USER)] = mqttUsername;
if(mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as<String>()) 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<const char*>());
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<const char*>());
}
// 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<std::string>();
}
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<const char*>());
changed |= mqttServer != settings[FPSTR(FP_CONFIG_HOST)];
mqttServer = settings[FPSTR(FP_CONFIG_HOST)].as<const char*>();
}
if(!settings[FPSTR(FP_CONFIG_USER)].isNull()) {
changed |= mqttUsername != settings[FPSTR(FP_CONFIG_USER)];
mqttUsername = settings[FPSTR(FP_CONFIG_USER)].as<const char*>();
}
if(!settings[FPSTR(FP_CONFIG_PASS)].isNull() &&
settings[FPSTR(FP_CONFIG_PASS)].as<std::string>() != D_PASSWORD_MASK) {
changed |= mqttPassword != settings[FPSTR(FP_CONFIG_PASS)];
mqttPassword = settings[FPSTR(FP_CONFIG_PASS)].as<const char*>();
}
mqttNodeTopic = MQTT_PREFIX;
mqttNodeTopic += haspDevice.get_hostname();
mqttGroupTopic = MQTT_PREFIX;
mqttGroupTopic += mqttGroupName;
return changed;
}
#endif // HASP_USE_PAHO
#endif // USE_MQTT

View File

@ -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<String>().c_str()) != 0) changed = true;
settings[FPSTR(FP_CONFIG_NAME)] = haspDevice.get_hostname();
if(mqttGroupName != settings[FPSTR(FP_CONFIG_GROUP)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName;
if(mqttServer != settings[FPSTR(FP_CONFIG_HOST)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_HOST)] = mqttServer;
if(mqttPort != settings[FPSTR(FP_CONFIG_PORT)].as<uint16_t>()) changed = true;
settings[FPSTR(FP_CONFIG_PORT)] = mqttPort;
if(mqttUsername != settings[FPSTR(FP_CONFIG_USER)].as<String>()) changed = true;
settings[FPSTR(FP_CONFIG_USER)] = mqttUsername;
if(mqttPassword != settings[FPSTR(FP_CONFIG_PASS)].as<String>()) 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.

View File

@ -5,8 +5,10 @@
#if HASP_USE_CONSOLE > 0
#if HASP_TARGET_ARDUINO
#include "ConsoleInput.h"
#include <StreamUtils.h>
#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
#endif

View File

@ -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);

View File

@ -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"

View File

@ -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 */

View File

@ -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>
+<MQTTClient.c>
-<MQTTAsync.c>
-<MQTTAsyncUtils.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>
-<MQTTClient.c>
+<MQTTAsync.c>
+<MQTTAsyncUtils.c>
-<MQTTVersion.c>
-<SSLSocket.c>
-<sys/>
+<sys/gpio/>
+<sys/svc/>
-<hal/>
+<drv/>
-<drv/touch>
@ -112,4 +112,4 @@ build_src_filter =
+<lang/>
-<log/>
+<mqtt/>
+<../.pio/libdeps/darwin_sdl_64bits/ArduinoJson/src/ArduinoJson.h>
+<../.pio/libdeps/darwin_sdl/ArduinoJson/src/ArduinoJson.h>

View File

@ -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

View File

@ -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

View File

@ -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>
-<MQTTClient.c>
+<MQTTAsync.c>
+<MQTTAsyncUtils.c>
-<MQTTVersion.c>
-<SSLSocket.c>
-<sys/>
+<sys/gpio/>
+<sys/svc/>
-<hal/>
+<drv/>
-<drv/touch>
+<drv/tft>
+<dev/>
-<hal/>
-<svc/>
-<hasp_filesystem.cpp>
+<font/>
+<hasp/>
+<lang/>
-<log/>
+<mqtt/>
+<../.pio/libdeps/linux_fbdev/ArduinoJson/src/ArduinoJson.h>

View File

@ -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>
+<MQTTClient.c>
-<MQTTAsync.c>
-<MQTTAsyncUtils.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>
-<MQTTClient.c>
+<MQTTAsync.c>
+<MQTTAsyncUtils.c>
-<MQTTVersion.c>
-<SSLSocket.c>
-<sys/>
+<sys/gpio/>
+<sys/svc/>
-<hal/>
+<drv/>
-<drv/touch>
@ -102,4 +103,4 @@ build_src_filter =
+<lang/>
-<log/>
+<mqtt/>
+<../.pio/libdeps/linux_sdl_64bits/ArduinoJson/src/ArduinoJson.h>
+<../.pio/libdeps/linux_sdl/ArduinoJson/src/ArduinoJson.h>

View File

@ -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
;https://github.com/stm32duino/LwIP.git

View File

@ -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

View File

@ -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>
-<MQTTClient.c>
+<MQTTAsync.c>
+<MQTTAsyncUtils.c>
-<MQTTVersion.c>
-<SSLSocket.c>
-<sys/>
+<sys/gpio/>
+<sys/svc/>
-<hal/>
+<drv/>
-<drv/touch>
+<drv/tft>
+<dev/>
-<hal/>
-<svc/>
-<hasp_filesystem.cpp>
+<font/>
+<hasp/>
+<lang/>
-<log/>
+<mqtt/>
+<../.pio/libdeps/windows_gdi/ArduinoJson/src/ArduinoJson.h>
+<lib/lv_datetime>

View File

@ -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>
+<MQTTClient.c>
-<MQTTAsync.c>
-<MQTTAsyncUtils.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>
-<MQTTClient.c>
+<MQTTAsync.c>
+<MQTTAsyncUtils.c>
-<MQTTVersion.c>
-<SSLSocket.c>
-<sys/>
+<sys/gpio/>
+<sys/svc/>
-<hal/>
+<drv/>
-<drv/touch>
@ -133,5 +133,5 @@ build_src_filter =
+<lang/>
-<log/>
+<mqtt/>
+<../.pio/libdeps/windows_sdl_64bits/ArduinoJson/src/ArduinoJson.h>
+<../.pio/libdeps/windows_sdl/ArduinoJson/src/ArduinoJson.h>
+<lib/lv_datetime>