From a70eb1fd5fd4dfafd2f04b03a8b2ff9504bebdc7 Mon Sep 17 00:00:00 2001 From: fvanroie <15969459+fvanroie@users.noreply.github.com> Date: Mon, 15 Feb 2021 03:09:17 +0100 Subject: [PATCH] Add Windows client --- hal/sdl2/app_hal.c | 75 +++ hal/sdl2/app_hal.h | 17 + hal/stm32f407_btt/app_hal.c | 50 ++ hal/stm32f407_btt/app_hal.h | 17 + hal/stm32f407_btt/tft.c | 65 +++ hal/stm32f407_btt/tft.h | 37 ++ hal/stm32f429_disco/app_hal.c | 123 +++++ hal/stm32f429_disco/app_hal.h | 17 + hal/stm32f429_disco/tft.c | 236 +++++++++ hal/stm32f429_disco/tft.h | 37 ++ hal/stm32f429_disco/touchpad.c | 144 ++++++ hal/stm32f429_disco/touchpad.h | 32 ++ include/VersionInfo.h | 7 + include/hasp_conf.h | 73 ++- include/hasp_macro.h | 57 ++- include/lv_conf_v7.h | 14 +- platformio.ini | 135 +++++- src/{main.cpp => main_arduino.cpp} | 23 +- src/main_windows.cpp | 120 +++++ src/{svc => mqtt}/hasp_mqtt.h | 8 +- src/{svc/hasp_mqtt.cpp => mqtt/hasp_mqtt.old} | 2 +- src/{svc => mqtt}/hasp_mqtt_ha.cpp | 3 +- src/{svc => mqtt}/hasp_mqtt_ha.h | 0 src/mqtt/hasp_mqtt_paho.cpp | 423 ++++++++++++++++ src/mqtt/hasp_mqtt_pubsubclient.cpp | 455 ++++++++++++++++++ tools/sdl2_build_extra.py | 33 ++ 26 files changed, 2155 insertions(+), 48 deletions(-) create mode 100644 hal/sdl2/app_hal.c create mode 100644 hal/sdl2/app_hal.h create mode 100644 hal/stm32f407_btt/app_hal.c create mode 100644 hal/stm32f407_btt/app_hal.h create mode 100644 hal/stm32f407_btt/tft.c create mode 100644 hal/stm32f407_btt/tft.h create mode 100644 hal/stm32f429_disco/app_hal.c create mode 100644 hal/stm32f429_disco/app_hal.h create mode 100644 hal/stm32f429_disco/tft.c create mode 100644 hal/stm32f429_disco/tft.h create mode 100644 hal/stm32f429_disco/touchpad.c create mode 100644 hal/stm32f429_disco/touchpad.h create mode 100644 include/VersionInfo.h rename src/{main.cpp => main_arduino.cpp} (92%) create mode 100644 src/main_windows.cpp rename src/{svc => mqtt}/hasp_mqtt.h (84%) rename src/{svc/hasp_mqtt.cpp => mqtt/hasp_mqtt.old} (99%) rename src/{svc => mqtt}/hasp_mqtt_ha.cpp (99%) rename src/{svc => mqtt}/hasp_mqtt_ha.h (100%) create mode 100644 src/mqtt/hasp_mqtt_paho.cpp create mode 100644 src/mqtt/hasp_mqtt_pubsubclient.cpp create mode 100644 tools/sdl2_build_extra.py diff --git a/hal/sdl2/app_hal.c b/hal/sdl2/app_hal.c new file mode 100644 index 00000000..326c8282 --- /dev/null +++ b/hal/sdl2/app_hal.c @@ -0,0 +1,75 @@ +#include +#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/ +#include +#include "display/monitor.h" +#include "indev/mouse.h" +#include "indev/mousewheel.h" +#include "indev/keyboard.h" + + +/** + * A task to measure the elapsed time for LittlevGL + * @param data unused + * @return never return + */ +static int tick_thread(void * data) +{ + (void)data; + + while(1) { + SDL_Delay(5); /*Sleep for 5 millisecond*/ + lv_tick_inc(5); /*Tell LittelvGL that 5 milliseconds were elapsed*/ + } + + return 0; +} + + +void hal_setup(void) +{ + // Workaround for sdl2 `-m32` crash + // https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1775067/comments/7 + #ifndef WIN32 + setenv("DBUS_FATAL_WARNINGS", "0", 1); + #endif + + /* Add a display + * Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/ + monitor_init(); + /* Tick init. + * 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); + + lv_init(); + static lv_disp_buf_t disp_buf; + static lv_color_t buf[LV_HOR_RES_MAX * 10]; /*Declare a buffer for 10 lines*/ + lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/ + + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); /*Basic initialization*/ + disp_drv.flush_cb = monitor_flush; /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/ + disp_drv.buffer = &disp_buf; + //disp_drv.disp_fill = monitor_fill; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ + //disp_drv.disp_map = monitor_map; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ + lv_disp_drv_register(&disp_drv); + + /* Add the mouse as input device + * Use the 'mouse' driver which reads the PC's mouse*/ + mouse_init(); + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); /*Basic initialization*/ + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = mouse_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ + lv_indev_drv_register(&indev_drv); + + +} + +void hal_loop(void) +{ + /*while(1)*/ { + SDL_Delay(5); + lv_task_handler(); + } +} diff --git a/hal/sdl2/app_hal.h b/hal/sdl2/app_hal.h new file mode 100644 index 00000000..58a5b9df --- /dev/null +++ b/hal/sdl2/app_hal.h @@ -0,0 +1,17 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + + +void hal_setup(void); +void hal_loop(void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DRIVER_H*/ diff --git a/hal/stm32f407_btt/app_hal.c b/hal/stm32f407_btt/app_hal.c new file mode 100644 index 00000000..639a3b04 --- /dev/null +++ b/hal/stm32f407_btt/app_hal.c @@ -0,0 +1,50 @@ + +#include "Arduino.h" + +#include "stm32f4xx.h" +//#include "stm32f429i_discovery.h" +#include "tft.h" +//#include "touchpad.h" + +#ifdef USE_RTOS_SYSTICK +#include +#endif + +void hal_setup(void) +{ + pinMode(PD12, OUTPUT); + digitalWrite(PD12, HIGH); + + // HAL_Init(); + + // /* Configure the system clock to 180 MHz */ + // SystemClock_Config(); + + // /* Start up indication */ + // BSP_LED_Init(LED3); + // for (uint8_t i = 0; i < 8; i++) { BSP_LED_Toggle(LED3); delay(50); } + + tft_init(); + //touchpad_init(); +} + +// void SysTick_Handler(void) +// { +// HAL_IncTick(); +// HAL_SYSTICK_IRQHandler(); + +// lv_tick_inc(1); + +// #ifdef USE_RTOS_SYSTICK +// osSystickHandler(); +// #endif +// } + +void hal_loop(void) +{ + //while (1) + { + delay(5); + lv_task_handler(); + } +} diff --git a/hal/stm32f407_btt/app_hal.h b/hal/stm32f407_btt/app_hal.h new file mode 100644 index 00000000..58a5b9df --- /dev/null +++ b/hal/stm32f407_btt/app_hal.h @@ -0,0 +1,17 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + + +void hal_setup(void); +void hal_loop(void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DRIVER_H*/ diff --git a/hal/stm32f407_btt/tft.c b/hal/stm32f407_btt/tft.c new file mode 100644 index 00000000..76ab6061 --- /dev/null +++ b/hal/stm32f407_btt/tft.c @@ -0,0 +1,65 @@ +/** + * @file disp.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include + +#include "tft.h" +#include "stm32f4xx.h" +#include "fsmc_ssd1963.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static lv_disp_drv_t disp_drv; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +/** + * Initialize your display here + */ +void tft_init(void) +{ + static lv_color_t disp_buf1[TFT_HOR_RES * 40]; + static lv_disp_buf_t buf; + lv_disp_buf_init(&buf, disp_buf1, NULL, TFT_HOR_RES * 40); + + lv_disp_drv_init(&disp_drv); + fsmc_ssd1963_init(0, false); + + disp_drv.buffer = &buf; + disp_drv.flush_cb = fsmc_ssd1963_flush; + disp_drv.hor_res = TFT_HOR_RES; + disp_drv.ver_res = TFT_VER_RES; +#if TFT_USE_GPU != 0 + DMA2D_Config(); + disp_drv.gpu_blend_cb = gpu_mem_blend; + disp_drv.gpu_fill_cb = gpu_mem_fill; +#endif + lv_disp_drv_register(&disp_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/hal/stm32f407_btt/tft.h b/hal/stm32f407_btt/tft.h new file mode 100644 index 00000000..fc54bb03 --- /dev/null +++ b/hal/stm32f407_btt/tft.h @@ -0,0 +1,37 @@ +/** + * @file disp.h + * + */ + +#ifndef DISP_H +#define DISP_H + +/********************* + * INCLUDES + *********************/ +#include +#include "lvgl.h" + +/********************* + * DEFINES + *********************/ +#define TFT_HOR_RES TFT_WIDTH +#define TFT_VER_RES TFT_HEIGHT + +#define TFT_EXT_FB 1 /*Frame buffer is located into an external SDRAM*/ +#define TFT_USE_GPU 0 /*Enable hardware accelerator*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void tft_init(void); + +/********************** + * MACROS + **********************/ + +#endif diff --git a/hal/stm32f429_disco/app_hal.c b/hal/stm32f429_disco/app_hal.c new file mode 100644 index 00000000..c7aa120f --- /dev/null +++ b/hal/stm32f429_disco/app_hal.c @@ -0,0 +1,123 @@ + +#include "stm32f4xx.h" +#include "stm32f429i_discovery.h" +#include "tft.h" +#include "touchpad.h" + +#ifdef USE_RTOS_SYSTICK +#include +#endif + + +/** + * @brief System Clock Configuration + * The system Clock is configured as follow : + * System Clock source = PLL (HSE) + * SYSCLK(Hz) = 180000000 + * HCLK(Hz) = 180000000 + * AHB Prescaler = 1 + * APB1 Prescaler = 4 + * APB2 Prescaler = 2 + * HSE Frequency(Hz) = 8000000 + * PLL_M = 8 + * PLL_N = 360 + * PLL_P = 2 + * PLL_Q = 7 + * VDD(V) = 3.3 + * Main regulator output voltage = Scale1 mode + * Flash Latency(WS) = 5 + * The LTDC Clock is configured as follow : + * PLLSAIN = 192 + * PLLSAIR = 4 + * PLLSAIDivR = 8 + * @param None + * @retval None + */ +static void SystemClock_Config(void) +{ + RCC_ClkInitTypeDef RCC_ClkInitStruct; + RCC_OscInitTypeDef RCC_OscInitStruct; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + + /* Enable Power Control clock */ + __HAL_RCC_PWR_CLK_ENABLE(); + + /* The voltage scaling allows optimizing the power consumption when the device is + clocked below the maximum system frequency, to update the voltage scaling value + regarding system frequency refer to product datasheet. */ + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + + /*##-1- System Clock Configuration #########################################*/ + /* Enable HSE Oscillator and activate PLL with HSE as source */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 8; + RCC_OscInitStruct.PLL.PLLN = 360; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 7; + HAL_RCC_OscConfig(&RCC_OscInitStruct); + + /* Activate the Over-Drive mode */ + HAL_PWREx_EnableOverDrive(); + + /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 + clocks dividers */ + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); + + /*##-2- LTDC Clock Configuration ###########################################*/ + /* LCD clock configuration */ + /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 MHz */ + /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 192 MHz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 192/4 = 48 MHz */ + /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDIVR_8 = 48/8 = 6 MHz */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; + PeriphClkInitStruct.PLLSAI.PLLSAIN = 192; + PeriphClkInitStruct.PLLSAI.PLLSAIR = 4; + PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8; + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); +} + + +void hal_setup(void) +{ + HAL_Init(); + + /* Configure the system clock to 180 MHz */ + SystemClock_Config(); + + /* Start up indication */ + BSP_LED_Init(LED3); + for (uint8_t i = 0; i < 8; i++) { BSP_LED_Toggle(LED3); HAL_Delay(50); } + + tft_init(); + touchpad_init(); +} + + +void SysTick_Handler(void) +{ + HAL_IncTick(); + HAL_SYSTICK_IRQHandler(); + + lv_tick_inc(1); + +#ifdef USE_RTOS_SYSTICK + osSystickHandler(); +#endif +} + + +void hal_loop(void) +{ + while(1) { + HAL_Delay(5); + lv_task_handler(); + } +} diff --git a/hal/stm32f429_disco/app_hal.h b/hal/stm32f429_disco/app_hal.h new file mode 100644 index 00000000..58a5b9df --- /dev/null +++ b/hal/stm32f429_disco/app_hal.h @@ -0,0 +1,17 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + + +void hal_setup(void); +void hal_loop(void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DRIVER_H*/ diff --git a/hal/stm32f429_disco/tft.c b/hal/stm32f429_disco/tft.c new file mode 100644 index 00000000..d6a463c2 --- /dev/null +++ b/hal/stm32f429_disco/tft.c @@ -0,0 +1,236 @@ +/** + * @file disp.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include + +#include "tft.h" +#include "stm32f4xx.h" +#include "stm32f429i_discovery_lcd.h" +#include "ili9341.h" + +/********************* + * DEFINES + *********************/ + +#define SDRAM_BANK_ADDR ((uint32_t)0xD0000000) + +#define DMA_STREAM DMA2_Stream0 +#define DMA_CHANNEL DMA_CHANNEL_0 +#define DMA_STREAM_IRQ DMA2_Stream0_IRQn +#define DMA_STREAM_IRQHANDLER DMA2_Stream0_IRQHandler + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p); + +/********************** + * STATIC VARIABLES + **********************/ +extern LTDC_HandleTypeDef LtdcHandler; + +#if TFT_USE_GPU != 0 +static DMA2D_HandleTypeDef Dma2dHandle; +#endif + +#if TFT_EXT_FB != 0 +static __IO uint16_t *my_fb = (__IO uint16_t *)(SDRAM_BANK_ADDR); +#else +static uint16_t my_fb[TFT_HOR_RES * TFT_VER_RES]; +#endif + +/*DMA to flush to frame buffer*/ +static void DMA_Config(void); +static void DMA_TransferComplete(DMA_HandleTypeDef *han); +static void DMA_TransferError(DMA_HandleTypeDef *han); + +DMA_HandleTypeDef DmaHandle; +static lv_disp_drv_t disp_drv; +static int32_t x1_flush; +static int32_t y1_flush; +static int32_t x2_flush; +static int32_t y2_fill; +static int32_t y_fill_act; +static const lv_color_t *buf_to_flush; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +/** + * Initialize your display here + */ +void tft_init(void) +{ + static lv_color_t disp_buf1[TFT_HOR_RES * 40]; + static lv_disp_buf_t buf; + lv_disp_buf_init(&buf, disp_buf1, NULL, TFT_HOR_RES * 40); + + lv_disp_drv_init(&disp_drv); + + BSP_LCD_Init(); + BSP_LCD_LayerDefaultInit(0, (uint32_t)my_fb); + HAL_LTDC_SetPixelFormat(&LtdcHandler, LTDC_PIXEL_FORMAT_RGB565, 0); + DMA_Config(); + disp_drv.buffer = &buf; + disp_drv.flush_cb = tft_flush; + disp_drv.hor_res = TFT_HOR_RES; + disp_drv.ver_res = TFT_VER_RES; +#if TFT_USE_GPU != 0 + DMA2D_Config(); + disp_drv.gpu_blend_cb = gpu_mem_blend; + disp_drv.gpu_fill_cb = gpu_mem_fill; +#endif + lv_disp_drv_register(&disp_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Flush a color buffer + * @param x1 left coordinate of the rectangle + * @param x2 right coordinate of the rectangle + * @param y1 top coordinate of the rectangle + * @param y2 bottom coordinate of the rectangle + * @param color_p pointer to an array of colors + */ +static void tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) +{ + /*Return if the area is out the screen*/ + if (area->x2 < 0) + return; + if (area->y2 < 0) + return; + if (area->x1 > TFT_HOR_RES - 1) + return; + if (area->y1 > TFT_VER_RES - 1) + return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; + int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; + int32_t act_x2 = area->x2 > TFT_HOR_RES - 1 ? TFT_HOR_RES - 1 : area->x2; + int32_t act_y2 = area->y2 > TFT_VER_RES - 1 ? TFT_VER_RES - 1 : area->y2; + + x1_flush = act_x1; + y1_flush = act_y1; + x2_flush = act_x2; + y2_fill = act_y2; + y_fill_act = act_y1; + buf_to_flush = color_p; + + /*##-7- Start the DMA transfer using the interrupt mode #*/ + /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */ + /* Enable All the DMA interrupts */ + HAL_StatusTypeDef err; + err = HAL_DMA_Start_IT(&DmaHandle, (uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * TFT_HOR_RES + x1_flush], + (x2_flush - x1_flush + 1)); + if (err != HAL_OK) + { + while (1) + ; /*Halt on error*/ + } +} + +static void DMA_Config(void) +{ + /*## -1- Enable DMA2 clock #################################################*/ + __HAL_RCC_DMA2_CLK_ENABLE(); + + /*##-2- Select the DMA functional Parameters ###############################*/ + DmaHandle.Init.Channel = DMA_CHANNEL; /* DMA_CHANNEL_0 */ + DmaHandle.Init.Direction = DMA_MEMORY_TO_MEMORY; /* M2M transfer mode */ + DmaHandle.Init.PeriphInc = DMA_PINC_ENABLE; /* Peripheral increment mode Enable */ + DmaHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */ + DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* Peripheral data alignment : 16bit */ + DmaHandle.Init.MemDataAlignment = DMA_PDATAALIGN_HALFWORD; /* memory data alignment : 16bit */ + DmaHandle.Init.Mode = DMA_NORMAL; /* Normal DMA mode */ + DmaHandle.Init.Priority = DMA_PRIORITY_HIGH; /* priority level : high */ + DmaHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* FIFO mode enabled */ + DmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL; /* FIFO threshold: 1/4 full */ + DmaHandle.Init.MemBurst = DMA_MBURST_SINGLE; /* Memory burst */ + DmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Peripheral burst */ + + /*##-3- Select the DMA instance to be used for the transfer : DMA2_Stream0 #*/ + DmaHandle.Instance = DMA_STREAM; + + /*##-4- Initialize the DMA stream ##########################################*/ + if (HAL_DMA_Init(&DmaHandle) != HAL_OK) + { + while (1) + ; + } + + /*##-5- Select Callbacks functions called after Transfer complete and Transfer error */ + HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_CPLT_CB_ID, DMA_TransferComplete); + HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_ERROR_CB_ID, DMA_TransferError); + + /*##-6- Configure NVIC for DMA transfer complete/error interrupts ##########*/ + HAL_NVIC_SetPriority(DMA_STREAM_IRQ, 0, 0); + HAL_NVIC_EnableIRQ(DMA_STREAM_IRQ); +} + +/** + * @brief DMA conversion complete callback + * @note This function is executed when the transfer complete interrupt + * is generated + * @retval None + */ +static void DMA_TransferComplete(DMA_HandleTypeDef *han) +{ + y_fill_act++; + + if (y_fill_act > y2_fill) + { + lv_disp_flush_ready(&disp_drv); + } + else + { + buf_to_flush += x2_flush - x1_flush + 1; + /*##-7- Start the DMA transfer using the interrupt mode ####################*/ + /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */ + /* Enable All the DMA interrupts */ + if (HAL_DMA_Start_IT(han, (uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * TFT_HOR_RES + x1_flush], + (x2_flush - x1_flush + 1)) != HAL_OK) + { + while (1) + ; /*Halt on error*/ + } + } +} + +/** + * @brief DMA conversion error callback + * @note This function is executed when the transfer error interrupt + * is generated during DMA transfer + * @retval None + */ +static void DMA_TransferError(DMA_HandleTypeDef *han) +{ +} + +/** + * @brief This function handles DMA Stream interrupt request. + * @param None + * @retval None + */ +void DMA_STREAM_IRQHANDLER(void) +{ + /* Check the interrupt and clear flag */ + HAL_DMA_IRQHandler(&DmaHandle); +} \ No newline at end of file diff --git a/hal/stm32f429_disco/tft.h b/hal/stm32f429_disco/tft.h new file mode 100644 index 00000000..e9bbdfbb --- /dev/null +++ b/hal/stm32f429_disco/tft.h @@ -0,0 +1,37 @@ +/** + * @file disp.h + * + */ + +#ifndef DISP_H +#define DISP_H + +/********************* + * INCLUDES + *********************/ +#include +#include "lvgl.h" + +/********************* + * DEFINES + *********************/ +#define TFT_HOR_RES 240 +#define TFT_VER_RES 320 + +#define TFT_EXT_FB 1 /*Frame buffer is located into an external SDRAM*/ +#define TFT_USE_GPU 0 /*Enable hardware accelerator*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void tft_init(void); + +/********************** + * MACROS + **********************/ + +#endif diff --git a/hal/stm32f429_disco/touchpad.c b/hal/stm32f429_disco/touchpad.c new file mode 100644 index 00000000..cba8bd77 --- /dev/null +++ b/hal/stm32f429_disco/touchpad.c @@ -0,0 +1,144 @@ +/** + * @file indev.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "tft.h" +#include "lvgl.h" + +#include "stm32f4xx.h" +#include "stm32f429i_discovery.h" +#include "stmpe811.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t *data); +static bool touchpad_get_xy(int16_t *x, int16_t *y); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize your input devices here + */ +void touchpad_init(void) +{ + stmpe811_Init(TS_I2C_ADDRESS); + stmpe811_TS_Start(TS_I2C_ADDRESS); + + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); + indev_drv.read_cb = touchpad_read; + indev_drv.type = LV_INDEV_TYPE_POINTER; + lv_indev_drv_register(&indev_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Read an input device + * @param indev_id id of the input device to read + * @param x put the x coordinate here + * @param y put the y coordinate here + * @return true: the device is pressed, false: released + */ +static bool touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t *data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + + bool detected; + int16_t x; + int16_t y; + detected = touchpad_get_xy(&x, &y); + if(detected) { + data->point.x = x; + data->point.y = y; + last_x = data->point.x; + last_y = data->point.y; + + data->state = LV_INDEV_STATE_PR; + } else { + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; + } + + return false; +} + + +static bool touchpad_get_xy(int16_t *x, int16_t *y) +{ + static int32_t _x = 0, _y = 0; + int16_t xDiff, yDiff, xr, yr, x_raw, y_raw;; + + bool detected; + detected = stmpe811_TS_DetectTouch(TS_I2C_ADDRESS); + + if(!detected) return false; + + + stmpe811_TS_GetXY(TS_I2C_ADDRESS, &x_raw, &y_raw); + + /* Y value first correction */ + y_raw -= 360; + + /* Y value second correction */ + yr = y_raw / 11; + + /* Return y_raw position value */ + if(yr <= 0) yr = 0; + else if (yr > TFT_VER_RES) yr = TFT_VER_RES - 1; + + y_raw = yr; + + /* X value first correction */ + if(x_raw <= 3000) x_raw = 3870 - x_raw; + else x_raw = 3800 - x_raw; + + /* X value second correction */ + xr = x_raw / 15; + + /* Return X position value */ + if(xr <= 0) xr = 0; + else if (xr > TFT_HOR_RES) xr = TFT_HOR_RES - 1; + + x_raw = xr; + xDiff = x_raw > _x? (x_raw - _x): (_x - x_raw); + yDiff = y_raw > _y? (y_raw - _y): (_y - y_raw); + + if (xDiff + yDiff > 5) { + _x = x_raw; + _y = y_raw; + } + + /* Update the X and Y position */ + *x = _x; + *y = _y; + + return true; +} diff --git a/hal/stm32f429_disco/touchpad.h b/hal/stm32f429_disco/touchpad.h new file mode 100644 index 00000000..bfd61c2d --- /dev/null +++ b/hal/stm32f429_disco/touchpad.h @@ -0,0 +1,32 @@ +/** + * @file indev.h + * + */ + +#ifndef INDEV_H +#define INDEV_H + +/********************* + * INCLUDES + *********************/ +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void touchpad_init(void); + +/********************** + * MACROS + **********************/ + +#endif diff --git a/include/VersionInfo.h b/include/VersionInfo.h new file mode 100644 index 00000000..5b91bf3a --- /dev/null +++ b/include/VersionInfo.h @@ -0,0 +1,7 @@ +#ifndef VERSIONINFO_H +#define VERSIONINFO_H + +#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@" +#define CLIENT_VERSION "@CLIENT_VERSION@" + +#endif /* VERSIONINFO_H */ diff --git a/include/hasp_conf.h b/include/hasp_conf.h index 2ac6c842..c0e2f847 100644 --- a/include/hasp_conf.h +++ b/include/hasp_conf.h @@ -1,6 +1,9 @@ #ifndef HASP_CONF_H #define HASP_CONF_H +// language specific defines +#include "lang/lang.h" + #define HASP_USE_APP 1 #ifndef HASP_USE_DEBUG @@ -101,7 +104,12 @@ #define HASP_OBJECT_NOTATION "p%ub%u" /* Includes */ -#include +#ifdef WINDOWS + #include "winsock2.h" + #include "Windows.h" +#else + #include "Arduino.h" +#endif #if HASP_USE_SPIFFS > 0 // #if defined(ARDUINO_ARCH_ESP32) @@ -132,7 +140,7 @@ #endif #if HASP_USE_WIFI > 0 - #include "net/hasp_wifi.h" + #include "sys/net/hasp_wifi.h" #if defined(STM32F4xx) #include "WiFiSpi.h" @@ -173,11 +181,18 @@ static WiFiSpiClass WiFi; #endif #if HASP_USE_MQTT > 0 - #include "svc/hasp_mqtt.h" + #include "mqtt/hasp_mqtt.h" + + #ifdef WINDOWS + #define USE_PAHO + #else + #define USE_PUBSUBCLIENT + #endif + #endif #if HASP_USE_GPIO > 0 - #include "hasp_gpio.h" + #include "sys/gpio/hasp_gpio.h" #endif #if HASP_USE_HTTP > 0 @@ -215,4 +230,54 @@ static WiFiSpiClass WiFi; #define PGM_P const char * #endif +#ifndef __FlashStringHelper + #define __FlashStringHelper char +#endif + +#ifndef FPSTR + #define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) +#endif + +#ifndef PGM_P + #define PGM_P const char * +#endif + +#ifndef F + #define F(x) (x) +#endif + +#ifndef PSTR + #define PSTR(x) x +#endif + +#ifndef PROGMEM + #define PROGMEM +#endif + +#ifdef WINDOWS + #include + #include + #include + #include + + #define snprintf_P snprintf + #define memcpy_P memcpy + #define strcasecmp_P strcmp // TODO: should be strcasecmp + #define strcmp_P strcmp + #define strstr_P strstr + #define halRestartMcu() + #define delay Sleep + #define millis SDL_GetTicks + + #define DEC 10 + #define HEX 16 + #define BIN 2 + + #define guiGetDim() 255 + #define guiSetDim(x) + #define guiGetBacklight() 1 + #define guiSetBacklight(x) + #define guiCalibrate() +#endif + #endif // HASP_CONF_H \ No newline at end of file diff --git a/include/hasp_macro.h b/include/hasp_macro.h index 0e7c636a..a129cbe0 100644 --- a/include/hasp_macro.h +++ b/include/hasp_macro.h @@ -1,65 +1,72 @@ #ifndef HASP_MACRO_H #define HASP_MACRO_H -#if HASP_LOG_LEVEL > LOG_LEVEL_FATAL - #define LOG_FATAL(...) \ - Log.fatal(__VA_ARGS__); \ - while(true) { \ - } +#ifdef WINDOWS +#define LOG_OUTPUT(x, ...) printf(__VA_ARGS__) #else - #define LOG_FATAL(...) \ - do { \ - } while(0) +#define LOG_OUTPUT(...) Log.output(...) + +#if HASP_LOG_LEVEL > LOG_LEVEL_FATAL +#define LOG_FATAL(...) \ + Log.fatal(__VA_ARGS__); \ + while (true) \ + { \ + } +#else +#define LOG_FATAL(...) \ + do \ + { \ + } while (0) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_ALERT - #define LOG_ALERT(...) Log.alert(__VA_ARGS__) +#define LOG_ALERT(...) Log.alert(__VA_ARGS__) #else - #define LOG_ALERT(...) +#define LOG_ALERT(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_CRITICAL - #define LOG_CRITICAL(...) Log.critical(__VA_ARGS__) +#define LOG_CRITICAL(...) Log.critical(__VA_ARGS__) #else - #define LOG_CRITICAL(...) +#define LOG_CRITICAL(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_ERROR - #define LOG_ERROR(...) Log.error(__VA_ARGS__) +#define LOG_ERROR(...) Log.error(__VA_ARGS__) #else - #define LOG_ERROR(...) +#define LOG_ERROR(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_WARNING - #define LOG_WARNING(...) Log.warning(__VA_ARGS__) +#define LOG_WARNING(...) Log.warning(__VA_ARGS__) #else - #define LOG_WARNING(...) +#define LOG_WARNING(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_INFO - #define LOG_INFO(...) Log.notice(__VA_ARGS__) +#define LOG_INFO(...) Log.notice(__VA_ARGS__) #else - #define LOG_INFO(...) +#define LOG_INFO(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_TRACE - #define LOG_TRACE(...) Log.trace(__VA_ARGS__) +#define LOG_TRACE(...) Log.trace(__VA_ARGS__) #else - #define LOG_TRACE(...) +#define LOG_TRACE(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_VERBOSE - #define LOG_VERBOSE(...) Log.verbose(__VA_ARGS__) +#define LOG_VERBOSE(...) Log.verbose(__VA_ARGS__) #else - #define LOG_VERBOSE(...) +#define LOG_VERBOSE(...) #endif #if HASP_LOG_LEVEL > LOG_LEVEL_DEBUG - #define LOG_DEBUG(...) Log.debug(__VA_ARGS__) +#define LOG_DEBUG(...) Log.debug(__VA_ARGS__) #else - #define LOG_DEBUG(...) +#define LOG_DEBUG(...) #endif -#define LOG_OUTPUT(...) Log.output(...) +#endif #endif \ No newline at end of file diff --git a/include/lv_conf_v7.h b/include/lv_conf_v7.h index abb7c91e..be0aa63e 100644 --- a/include/lv_conf_v7.h +++ b/include/lv_conf_v7.h @@ -26,6 +26,8 @@ /* Maximal horizontal and vertical resolution to support by the library.*/ #define LV_HOR_RES_MAX (TFT_WIDTH) #define LV_VER_RES_MAX (TFT_HEIGHT) +#define LV_HOR_RES (TFT_WIDTH) +#define LV_VER_RES (TFT_HEIGHT) /* Color depth: * - 1: 1 byte per pixel @@ -181,7 +183,7 @@ typedef void* lv_group_user_data_t; typedef void* lv_fs_drv_user_data_t; /*File system interface*/ -#define LV_USE_FS_IF 1 +#define LV_USE_FS_IF 0 #if LV_USE_FS_IF # define LV_FS_IF_FATFS '\0' #if defined(ARDUINO_ARCH_ESP32) // || defined(ARDUINO_ARCH_ESP8266) @@ -251,12 +253,18 @@ typedef void* lv_img_decoder_user_data_t; /* 1: use a custom tick source. * It removes the need to manually update the tick with `lv_tick_inc`) */ +#ifdef ARDUINO + #define LV_TICK_CUSTOM 1 #if LV_TICK_CUSTOM == 1 #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the sys time function*/ #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current systime in ms*/ #endif /*LV_TICK_CUSTOM*/ +#else +#define LV_TICK_CUSTOM 0 +#endif + typedef void* lv_disp_drv_user_data_t; /*Type of user data in the display driver*/ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/ @@ -280,7 +288,7 @@ typedef void* lv_indev_drv_user_data_t; /*Type of user data in the in /* 1: Print the log with 'printf'; * 0: user need to register a callback with `lv_log_register_print_cb`*/ -# define LV_LOG_PRINTF 0 +# define LV_LOG_PRINTF 1 #endif /*LV_USE_LOG*/ /*================= @@ -431,7 +439,7 @@ typedef void* lv_font_user_data_t; /*Always enable at least on theme*/ #define LV_USE_THEME_MATERIAL 1 /*A fast and impressive theme*/ -#define LV_THEME_DEFAULT_INIT lv_theme_hasp_init // We init the theme ourselves +#define LV_THEME_DEFAULT_INIT lv_theme_material_init // lv_theme_hasp_init // We init the theme ourselves #define LV_THEME_DEFAULT_COLOR_PRIMARY LV_COLOR_RED #define LV_THEME_DEFAULT_COLOR_SECONDARY LV_COLOR_BLUE #define LV_THEME_DEFAULT_FLAG 0 //LV_THEME_MATERIAL_FLAG_NONE diff --git a/platformio.ini b/platformio.ini index 7b671f8f..4dc6ca5c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,6 @@ extra_default_envs = ; Common environment settings ;*************************************************** [env] -framework = arduino upload_speed = 921600 monitor_speed = 115200 @@ -49,21 +48,20 @@ build_flags = -D LV_CONF_INCLUDE_SIMPLE ; for lvgl -D LV_LVGL_H_INCLUDE_SIMPLE ; for lv_drivers -D LV_COMP_CONF_INCLUDE_SIMPLE ; for components - ; -- littlevgl build options ------------------------------ + ; -- ESP build options ------------------------------------ -D SPIFFS_TEMPORAL_FD_CACHE ; speedup opening recent files ; -- ArduinoJson build options ---------------------------- -D ARDUINOJSON_DECODE_UNICODE=1 ; for utf-8 symbols - -D ARDUINOJSON_ENABLE_PROGMEM=1 ; for PROGMEM arguments + ;-D ARDUINOJSON_ENABLE_PROGMEM=1 ; for PROGMEM arguments ; -- StreamUtils build options ---------------------------- -D STREAMUTILS_ENABLE_EEPROM=1 ; for STM32, it also supports EEPROM ; -- Hasp build options ---------------------------- -D HASP_VER_MAJ=0 - -D HASP_VER_MIN=3 - -D HASP_VER_REV=3 + -D HASP_VER_MIN=4 + -D HASP_VER_REV=0 -D HASP_LOG_LEVEL=9 - -D HASP_USE_CONFIG=1 ; Native application, not library ${override.build_flags} ; -- Shared library dependencies in all environments @@ -93,6 +91,7 @@ extra_scripts = tools/copy_fw.py ; tools/pre:extra_script.py ; -- Platform specific build flags [esp32] +framework = arduino build_flags = ${env.build_flags} -D HTTP_UPLOAD_BUFLEN=1024 ; lower http upload buffer @@ -108,6 +107,7 @@ build_flags = ;-D HASP_USE_SPIFFS=1 -D HASP_USE_LITTLEFS=1 ;-D HASP_USE_EEPROM=1 + -D HASP_USE_CONFIG=1 ; Native application, not library ; -- LittleFS build options ------------------------ -D CONFIG_LITTLEFS_FOR_IDF_3_2 @@ -146,6 +146,7 @@ hspi = -D TFT_SCLK=14 [esp8266] +framework = arduino build_flags= -D HTTP_UPLOAD_BUFLEN=512 ; lower http upload buffer -D MQTT_MAX_PACKET_SIZE=1024 ; longer PubSubClient messages @@ -163,6 +164,7 @@ build_flags= -D HASP_USE_LITTLEFS=1 -D HASP_USE_EEPROM=1 -D HASP_USE_ETHERNET=0 + -D HASP_USE_CONFIG=1 ; Native application, not library lib_ignore = ESP32 BLE Arduino @@ -174,6 +176,7 @@ lib_ignore = lib_deps = [stm32f4] +framework = arduino build_flags= -I include/stm32f4 -D MQTT_MAX_PACKET_SIZE=2048 ; longer PubSubClient messages @@ -187,6 +190,7 @@ build_flags= -D HASP_USE_SYSLOG=0 ; Needs UDP -D HASP_USE_SPIFFS=0 -D HASP_USE_LITTLEFS=0 + -D HASP_USE_CONFIG=1 ; Native application, not library lib_deps = ; sstaub/Ticker @ ^3.2.0 @@ -219,3 +223,122 @@ lib_deps = ; ;lv_drivers=https://github.com/littlevgl/lv_drivers/archive/master.zip ; lv_drivers@^6.0.2 ;src_filter = +<*> +<../drivers/sdl2> + +[env:emulator_64bits] +platform = native@^1.1.3 +extra_scripts = tools/sdl2_build_extra.py +build_flags = + ${env.build_flags} + ; ----- Monitor + -D TFT_WIDTH=800 + -D TFT_HEIGHT=480 + ; SDL drivers options + ;-D LV_LVGL_H_INCLUDE_SIMPLE + ;-D LV_DRV_NO_CONF + -D USE_MONITOR + -D MONITOR_ZOOM=1 ; 2 + -D USE_MOUSE + -D USE_MOUSEWHEEL + -D USE_KEYBOARD + ; ----- ArduinoJson + -D ARDUINOJSON_DECODE_UNICODE=1 + -D HASP_NUM_PAGES=4 + -D HASP_USE_SPIFFS=0 + -D HASP_USE_LITTLEFS=0 + -D HASP_USE_EEPROM=0 + -D HASP_USE_GPIO=0 + -D HASP_USE_CONFIG=0 ; Standalone application, as library + -D HASP_USE_DEBUG=1 + -D HASP_USE_MQTT=1 + -D MQTT_MAX_PACKET_SIZE=2048 + ;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO + ;-D LV_LOG_PRINTF=1 + ; Add recursive dirs for hal headers search + !python -c "import os; print(' '.join(['-I {}'.format(i[0].replace('\x5C','/')) for i in os.walk('hal/sdl2')]))" + -mconsole + -lSDL2 + -D PAHO_MQTT_STATIC + -D _WIN64 + -D WINDOWS ; We add this for code branching in hasp + -D WIN32_LEAN_AND_MEAN + -DPAHO_WITH_SSL=FALSE + -DPAHO_BUILD_DOCUMENTATION=FALSE + -DPAHO_BUILD_SAMPLES=FALSE + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_VERBOSE_MAKEFILE=TRUE + ;-D NO_PERSISTENCE + -I.pio/libdeps/emulator_64bits/paho/src + -I.pio/libdeps/emulator_64bits/ArduinoJson/src + -I lib/ArduinoJson/src + -I lib/lv_fs_if + -l"ws2_32" + -lrpcrt4 + -lcrypt32 + -lmingw32 + -lSDL2main + -lSDL2 + -mwindows + -lm + -ldinput8 + -ldxguid + -ldxerr8 + -luser32 + -lgdi32 + -lwinmm + -limm32 + -lole32 + -loleaut32 + -lshell32 + -lversion + -luuid + -lsetupapi + -lhid + ;-v + + +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.17.2 ; Json(l) parser + +lib_ignore = paho + +src_filter = + +<*> + -<*.h> + +<../hal/sdl2> + +<../.pio/libdeps/emulator_64bits/paho/src/*.c> + -<../.pio/libdeps/emulator_64bits/paho/src/MQTTClient.c> + -<../.pio/libdeps/emulator_64bits/paho/src/MQTTVersion.c> + -<../.pio/libdeps/emulator_64bits/paho/src/SSLSocket.c> + - + - + - + -<../.pio/libdeps/emulator_64bits/lv_fs_if/lv_fs_pc.c> + -<../.pio/libdeps/emulator_64bits/ArduinoLog/ArduinoLog.cpp> + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + + + - + - + -<../lib/lv_fs_if> + -<../lib/lv_fs_if/lv_fs_if.cpp> + -<../lib/lv_fs_if/lv_fs_if.h> + -<../lib/lv_fs_if/lv_fs_spiffs.cpp> + -<../lib/lv_fs_if/lv_fs_spiffs.h> + +<../.pio/libdeps/emulator_64bits/ArduinoJson/src/ArduinoJson.h> diff --git a/src/main.cpp b/src/main_arduino.cpp similarity index 92% rename from src/main.cpp rename to src/main_arduino.cpp index 818f0919..b8eb8087 100644 --- a/src/main.cpp +++ b/src/main_arduino.cpp @@ -1,18 +1,26 @@ /* MIT License - Copyright (c) 2020 Francis Van Roie For full license information read the LICENSE file in the project folder */ +#ifdef ARDUINO + #include #include "hasp_conf.h" // load first -#include "hasp_debug.h" -#include "hasp_config.h" -#include "hasp_gui.h" +#if HASP_USE_CONFIG > 0 + #include "hasp_debug.h" +#endif + +#if HASP_USE_CONFIG > 0 + #include "hasp_config.h" + #include "hasp_gui.h" +#endif + #include "hasp_oobe.h" #include "hasp/hasp_dispatch.h" #include "hasp/hasp.h" -#include "net/hasp_network.h" +#include "sys/net/hasp_network.h" #include "dev/device.h" @@ -49,6 +57,7 @@ void setup() dispatchSetup(); guiSetup(); debugSetup(); // Init the console + #if HASP_USE_GPIO > 0 gpioSetup(); #endif @@ -137,7 +146,7 @@ void loop() /* Timer Loop */ if(millis() - mainLastLoopTime >= 1000) { /* Runs Every Second */ - haspEverySecond(); + haspEverySecond(); // sleep timer debugEverySecond(); // statusupdate #if HASP_USE_OTA > 0 @@ -177,4 +186,6 @@ void loop() #else delay(6); #endif -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/main_windows.cpp b/src/main_windows.cpp new file mode 100644 index 00000000..b3b021cd --- /dev/null +++ b/src/main_windows.cpp @@ -0,0 +1,120 @@ +/* MIT License - Copyright (c) 2020 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#ifdef WINDOWS + + #include "lvgl.h" + #include "app_hal.h" + + #include "hasp_conf.h" + #include "hasp_debug.h" + + #include "hasp/hasp_dispatch.h" + #include "hasp/hasp.h" + + #include "dev/device.h" + #include "app_hal.h" + +bool isConnected; +uint8_t mainLoopCounter = 0; +unsigned long mainLastLoopTime = 0; + +void debugLvglLogEvent(lv_log_level_t level, const char * file, uint32_t line, const char * funcname, + const char * descr) +{ + printf("%s %d\n", file, line); +} + +void setup() +{ + printf("%s %d\n", __FILE__, __LINE__); + fflush(stdout); + lv_init(); + lv_log_register_print_cb(debugLvglLogEvent); + + printf("%s %d\n", __FILE__, __LINE__); + fflush(stdout); + hal_setup(); + + printf("%s %d\n", __FILE__, __LINE__); + haspDevice.pre_setup(); + + printf("%s %d\n", __FILE__, __LINE__); + dispatchSetup(); + // debugSetup(); // Init the console + + #if HASP_USE_MQTT > 0 + printf("%s %d\n", __FILE__, __LINE__); + mqttSetup(); // Load Hostname before starting WiFi + #endif + + printf("%s %d\n", __FILE__, __LINE__); + haspSetup(); + mainLastLoopTime = millis() - 1000; // reset loop counter + delay(250); + + mqttStart(); +} + +void loop() +{ + haspLoop(); + + // debugLoop(); // Console + haspDevice.loop(); + + /* Timer Loop */ + if(millis() - mainLastLoopTime >= 1000) { + /* Runs Every Second */ + haspEverySecond(); // sleep timer + + #if HASP_USE_OTA > 0 + otaEverySecond(); // progressbar + #endif + + /* Runs Every 5 Seconds */ + if(mainLoopCounter == 0 || mainLoopCounter == 5) { + + haspDevice.loop_5s(); + } + + /* Reset loop counter every 10 seconds */ + if(mainLoopCounter >= 9) { + mainLoopCounter = 0; + } else { + mainLoopCounter++; + } + mainLastLoopTime += 1000; + } + + delay(6); +} + + #ifdef WINDOWS +int main(int argv, char ** args) +{ + printf("%s %d\n", __FILE__, __LINE__); + fflush(stdout); + setup(); + std::cout << "HSetup OK\n"; + + while(1) { + SDL_Delay(5); + lv_task_handler(); + fflush(stdout); + } + std::cout << "Hloop OK\n"; + + return 0; +} + + #else +void loop() +{ + delay(5); + lv_task_handler(); +} + + #endif + +#endif \ No newline at end of file diff --git a/src/svc/hasp_mqtt.h b/src/mqtt/hasp_mqtt.h similarity index 84% rename from src/svc/hasp_mqtt.h rename to src/mqtt/hasp_mqtt.h index c1343a3c..6d3dcbde 100644 --- a/src/svc/hasp_mqtt.h +++ b/src/mqtt/hasp_mqtt.h @@ -4,7 +4,11 @@ #ifndef HASP_MQTT_H #define HASP_MQTT_H -#include "ArduinoJson.h" +#include + +#include "hasp_conf.h" + +#define __FlashStringHelper char void mqttSetup(); void mqttLoop(); @@ -22,6 +26,6 @@ bool mqttGetConfig(const JsonObject & settings); bool mqttSetConfig(const JsonObject & settings); #endif -String mqttGetNodename(void); +//String mqttGetNodename(void); #endif \ No newline at end of file diff --git a/src/svc/hasp_mqtt.cpp b/src/mqtt/hasp_mqtt.old similarity index 99% rename from src/svc/hasp_mqtt.cpp rename to src/mqtt/hasp_mqtt.old index 044e68a0..0c67bc24 100644 --- a/src/svc/hasp_mqtt.cpp +++ b/src/mqtt/hasp_mqtt.old @@ -35,7 +35,7 @@ EthernetClient mqttNetworkClient; #endif #include "hasp_hal.h" - #include "hasp_debug.h" + #include "log/hasp_debug.h" #include "hasp_config.h" #include "../hasp/hasp_dispatch.h" diff --git a/src/svc/hasp_mqtt_ha.cpp b/src/mqtt/hasp_mqtt_ha.cpp similarity index 99% rename from src/svc/hasp_mqtt_ha.cpp rename to src/mqtt/hasp_mqtt_ha.cpp index 90069126..a5e1bfe7 100644 --- a/src/svc/hasp_mqtt_ha.cpp +++ b/src/mqtt/hasp_mqtt_ha.cpp @@ -3,7 +3,8 @@ #include "ArduinoJson.h" #include "hasp_conf.h" -#if HASP_USE_MQTT > 0 + +#if 0 && HASP_USE_MQTT > 0 #include "PubSubClient.h" diff --git a/src/svc/hasp_mqtt_ha.h b/src/mqtt/hasp_mqtt_ha.h similarity index 100% rename from src/svc/hasp_mqtt_ha.h rename to src/mqtt/hasp_mqtt_ha.h diff --git a/src/mqtt/hasp_mqtt_paho.cpp b/src/mqtt/hasp_mqtt_paho.cpp new file mode 100644 index 00000000..07ebb2b5 --- /dev/null +++ b/src/mqtt/hasp_mqtt_paho.cpp @@ -0,0 +1,423 @@ +/* MIT License - Copyright (c) 2020 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#include + +#include "hasp_conf.h" + +#if HASP_USE_MQTT > 0 + #ifdef USE_PAHO + + /******************************************************************************* + * Copyright (c) 2012, 2020 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial contribution + *******************************************************************************/ + + #include + #include + #include + #include + #include + + #include "MQTTAsync.h" + + #include "hasp_mqtt.h" // functions to implement here + + #include "hasp/hasp_dispatch.h" // for dispatch_topic_payload + #include "hasp_debug.h" // for logging + + #if !defined(_WIN32) + #include + #else + #include + #endif + + #if defined(_WRS_KERNEL) + #include + #endif + + #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]; +bool mqttEnabled = false; +bool mqttHAautodiscover = true; + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // These defaults may be overwritten with values saved by the web interface + #ifndef MQTT_HOST + #define MQTT_HOST ""; + #endif + + #ifndef MQTT_PORT + #define MQTT_PORT 1883; + #endif + + #ifndef MQTT_USER + #define MQTT_USER ""; + #endif + + #ifndef MQTT_PASSW + #define MQTT_PASSW ""; + #endif + #ifndef MQTT_NODENAME + #define MQTT_NODENAME ""; + #endif + #ifndef MQTT_GROUPNAME + #define MQTT_GROUPNAME ""; + #endif + + #ifndef MQTT_PREFIX + #define MQTT_PREFIX "hasp" + #endif + + #define LWT_TOPIC "LWT" + +char mqttServer[16] = MQTT_HOST; +char mqttUser[23] = MQTT_USER; +char mqttPassword[32] = MQTT_PASSW; +char mqttNodeName[16] = MQTT_NODENAME; +char mqttGroupName[16] = MQTT_GROUPNAME; +uint16_t mqttPort = MQTT_PORT; + +MQTTAsync mqtt_client; + +int disc_finished = 0; +int subscribed = 0; +int connected = 0; + +static bool mqttPublish(const char * topic, const char * payload, size_t len, bool retain = false); + +/* ===== Paho event callbacks ===== */ + +void connlost(void * context, char * cause) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + connected = 0; + + printf("\nConnection lost\n"); + if(cause) printf(" cause: %s\n", cause); + + printf("Reconnecting\n"); + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) { + printf("Failed to start connect, return code %d\n", rc); + } +} + +// Receive incoming messages +static void mqtt_message_cb(char * topic, char * payload, unsigned int length) +{ // Handle incoming commands from MQTT + if(length + 1 >= MQTT_MAX_PACKET_SIZE) { + LOG_ERROR(TAG_MQTT_RCV, F("Payload too long (%d bytes)"), length); + return; + } else { + payload[length] = '\0'; + } + + LOG_TRACE(TAG_MQTT_RCV, F("%s = %s"), topic, (char *)payload); + + if(topic == strstr(topic, mqttNodeTopic)) { // startsWith mqttNodeTopic + + // Node topic + topic += strlen(mqttNodeTopic); // shorten topic + + } else if(topic == strstr(topic, mqttGroupTopic)) { // startsWith mqttGroupTopic + + // Group topic + topic += strlen(mqttGroupTopic); // shorten topic + dispatch_topic_payload(topic, (const char *)payload); + return; + + } else if(topic == strstr_P(topic, PSTR("homeassistant/status"))) { // HA discovery topic + if(mqttHAautodiscover && !strcasecmp_P((char *)payload, PSTR("online"))) { + // dispatch_current_state(); + // mqtt_ha_register_auto_discovery(); + } + return; + + } else { + // Other topic + LOG_ERROR(TAG_MQTT, F(D_MQTT_INVALID_TOPIC)); + return; + } + + // catch a dangling LWT from a previous connection if it appears + if(!strcmp_P(topic, PSTR(LWT_TOPIC))) { // endsWith LWT + if(!strcasecmp_P((char *)payload, PSTR("offline"))) { + { + char msg[8]; + char tmp_topic[strlen(mqttNodeTopic) + 8]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); + snprintf_P(msg, sizeof(msg), PSTR("online")); + + // /*bool res =*/mqttClient.publish(tmp_topic, msg, true); + mqttPublish(tmp_topic, msg, true); + } + + } else { + // LOG_TRACE(TAG_MQTT, F("ignoring LWT = online")); + } + } else { + dispatch_topic_payload(topic, (const char *)payload); + } +} + +int msgarrvd(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); + + MQTTAsync_freeMessage(&message); + MQTTAsync_free(topicName); + return 1; +} + +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) +{ + 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); + } +} + +void onConnect(void * context, MQTTAsync_successData * response) +{ + MQTTAsync client = (MQTTAsync)context; + connected = 1; + + printf("Successful connection\n"); + + mqtt_subscribe(context, TOPIC "command/#"); + mqtt_subscribe(context, TOPIC "command"); + mqtt_subscribe(context, TOPIC "light"); + mqtt_subscribe(context, TOPIC "dim"); + + 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) +{ + if(mqttIsConnected()) { + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + int rc; + + 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; + if((rc = MQTTAsync_sendMessage(mqtt_client, topic, &pubmsg, &opts)) != MQTTASYNC_SUCCESS) { + LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " %s => %s"), topic, payload); + } else { + LOG_TRACE(TAG_MQTT_PUB, F("%s => %s"), topic, payload); + return true; + } + } else { + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED)); + } + return false; +} + +/* ===== Public HASP MQTT functions ===== */ + +bool mqttIsConnected() +{ + return connected == 1; +} + +void mqtt_send_state(const __FlashStringHelper * subtopic, const char * payload) +{ + char tmp_topic[strlen(mqttNodeTopic) + 20]; + printf(("%sstate/%s\n"), mqttNodeTopic, subtopic); + snprintf_P(tmp_topic, sizeof(tmp_topic), ("%sstate/%s"), mqttNodeTopic, subtopic); + mqttPublish(tmp_topic, payload, false); +} + +void mqtt_send_object_state(uint8_t pageid, uint8_t btnid, char * payload) +{ + char tmp_topic[strlen(mqttNodeTopic) + 20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/p%ub%u"), mqttNodeTopic, pageid, btnid); + mqttPublish(tmp_topic, payload, false); +} + +void mqttStart() +{ + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + int ch; + + if((rc = MQTTAsync_create(&mqtt_client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != + MQTTASYNC_SUCCESS) { + printf("Failed to create client, return code %d\n", 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; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + 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; + } else { + } + + // while (!subscribed && !finished) + // #if defined(_WIN32) + // Sleep(100); + // #else + // usleep(10000L); + // #endif + + // if (finished) + // goto exit; +} + +void mqttStop() +{ + int rc; + MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer; + 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); + 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 mqttLoop(){}; + +void mqttEvery5Seconds(bool wifiIsConnected){}; + + // String mqttGetNodename(void){return "palte35"}; + + #endif // USE_PAHO +#endif // USE_MQTT \ No newline at end of file diff --git a/src/mqtt/hasp_mqtt_pubsubclient.cpp b/src/mqtt/hasp_mqtt_pubsubclient.cpp new file mode 100644 index 00000000..70701959 --- /dev/null +++ b/src/mqtt/hasp_mqtt_pubsubclient.cpp @@ -0,0 +1,455 @@ +/* MIT License - Copyright (c) 2020 Francis Van Roie + For full license information read the LICENSE file in the project folder */ + +#include "hasp_conf.h" + +#if HASP_USE_MQTT > 0 + #ifdef USE_PUBSUBCLIENT + + #include "PubSubClient.h" + + #include "hasp/hasp.h" + #include "hasp_mqtt.h" + #include "hasp_mqtt_ha.h" + + #if defined(ARDUINO_ARCH_ESP32) + #include +WiFiClient mqttNetworkClient; + #elif defined(ARDUINO_ARCH_ESP8266) + #include + #include + #include +WiFiClient mqttNetworkClient; + #else + #if defined(STM32F4xx) && HASP_USE_WIFI > 0 +// #include +WiFiSpiClient mqttNetworkClient; + #else + #if defined(W5500_MOSI) && defined(W5500_MISO) && defined(W5500_SCLK) + #define W5500_LAN + #include + #else + #include + #endif + +EthernetClient mqttNetworkClient; + #endif + #endif + + #include "hasp_hal.h" + #include "log/hasp_debug.h" + #include "hasp_config.h" + + #include "../hasp/hasp_dispatch.h" + + #ifdef USE_CONFIG_OVERRIDE + #include "user_config_override.h" + #endif + +char mqttNodeTopic[24]; +char mqttGroupTopic[24]; +bool mqttEnabled = false; +bool mqttHAautodiscover = true; + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // These defaults may be overwritten with values saved by the web interface + #ifndef MQTT_HOST + #define MQTT_HOST ""; + #endif + + #ifndef MQTT_PORT + #define MQTT_PORT 1883; + #endif + + #ifndef MQTT_USER + #define MQTT_USER ""; + #endif + + #ifndef MQTT_PASSW + #define MQTT_PASSW ""; + #endif + #ifndef MQTT_NODENAME + #define MQTT_NODENAME ""; + #endif + #ifndef MQTT_GROUPNAME + #define MQTT_GROUPNAME ""; + #endif + + #ifndef MQTT_PREFIX + #define MQTT_PREFIX "hasp" + #endif + + #define LWT_TOPIC "LWT" + +char mqttServer[16] = MQTT_HOST; +char mqttUser[23] = MQTT_USER; +char mqttPassword[32] = MQTT_PASSW; +char mqttNodeName[16] = MQTT_NODENAME; +char mqttGroupName[16] = MQTT_GROUPNAME; +uint16_t mqttPort = MQTT_PORT; +PubSubClient mqttClient(mqttNetworkClient); + +static bool mqttPublish(const char * topic, const char * payload, size_t len, bool retain = false) +{ + if(mqttIsConnected()) { + if(mqttClient.beginPublish(topic, len, retain)) { + mqttClient.write((uint8_t *)payload, len); + mqttClient.endPublish(); + + LOG_TRACE(TAG_MQTT_PUB, F("%s => %s"), topic, payload); + return true; + } else { + LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " %s => %s"), topic, payload); + } + } else { + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_CONNECTED)); + } + return false; +} + +static bool mqttPublish(const char * topic, const char * payload, bool retain = false) +{ + return mqttPublish(topic, payload, strlen(payload), retain); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Send changed values OUT + +bool mqttIsConnected() +{ + return mqttEnabled && mqttClient.connected(); +} + +void mqtt_send_lwt(bool online) +{ + char tmp_payload[8]; + char tmp_topic[strlen(mqttNodeTopic) + 4]; + strncpy(tmp_topic, mqttNodeTopic, sizeof(tmp_topic)); + strncat_P(tmp_topic, PSTR(LWT_TOPIC), sizeof(tmp_topic)); + // snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); + + size_t len = snprintf_P(tmp_payload, sizeof(tmp_payload), online ? PSTR("online") : PSTR("offline")); + bool res = mqttPublish(tmp_topic, tmp_payload, len, true); +} + +void mqtt_send_object_state(uint8_t pageid, uint8_t btnid, char * payload) +{ + char tmp_topic[strlen(mqttNodeTopic) + 16]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/" HASP_OBJECT_NOTATION), mqttNodeTopic, pageid, btnid); + mqttPublish(tmp_topic, payload); +} + +void mqtt_send_state(const __FlashStringHelper * subtopic, const char * payload) +{ + char tmp_topic[strlen(mqttNodeTopic) + 20]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%sstate/%s"), mqttNodeTopic, subtopic); + mqttPublish(tmp_topic, payload); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Receive incoming messages +static void mqtt_message_cb(char * topic, byte * payload, unsigned int length) +{ // Handle incoming commands from MQTT + if(length + 1 >= mqttClient.getBufferSize()) { + LOG_ERROR(TAG_MQTT_RCV, F("Payload too long (%d bytes)"), length); + return; + } else { + payload[length] = '\0'; + } + + LOG_TRACE(TAG_MQTT_RCV, F("%s = %s"), topic, (char *)payload); + + if(topic == strstr(topic, mqttNodeTopic)) { // startsWith mqttNodeTopic + + // Node topic + topic += strlen(mqttNodeTopic); // shorten topic + + } else if(topic == strstr(topic, mqttGroupTopic)) { // startsWith mqttGroupTopic + + // Group topic + topic += strlen(mqttGroupTopic); // shorten topic + dispatch_topic_payload(topic, (const char *)payload); + return; + + } else if(topic == strstr_P(topic, PSTR("homeassistant/status"))) { // HA discovery topic + if(mqttHAautodiscover && !strcasecmp_P((char *)payload, PSTR("online"))) { + dispatch_current_state(); + mqtt_ha_register_auto_discovery(); + } + return; + + } else { + // Other topic + LOG_ERROR(TAG_MQTT, F(D_MQTT_INVALID_TOPIC)); + return; + } + + // catch a dangling LWT from a previous connection if it appears + if(!strcmp_P(topic, PSTR(LWT_TOPIC))) { // endsWith LWT + if(!strcasecmp_P((char *)payload, PSTR("offline"))) { + { + char msg[8]; + char tmp_topic[strlen(mqttNodeTopic) + 8]; + snprintf_P(tmp_topic, sizeof(tmp_topic), PSTR("%s" LWT_TOPIC), mqttNodeTopic); + snprintf_P(msg, sizeof(msg), PSTR("online")); + + // /*bool res =*/mqttClient.publish(tmp_topic, msg, true); + mqttPublish(tmp_topic, msg, true); + } + + } else { + // LOG_TRACE(TAG_MQTT, F("ignoring LWT = online")); + } + } else { + dispatch_topic_payload(topic, (const char *)payload); + } +} + +static void mqttSubscribeTo(const __FlashStringHelper * format, const char * data) +{ + char tmp_topic[strlen_P((PGM_P)format) + 2 + strlen(data)]; + snprintf_P(tmp_topic, sizeof(tmp_topic), (PGM_P)format, data); + if(mqttClient.subscribe(tmp_topic)) { + LOG_VERBOSE(TAG_MQTT, F(D_BULLET D_MQTT_SUBSCRIBED), tmp_topic); + } else { + LOG_ERROR(TAG_MQTT, F(D_MQTT_NOT_SUBSCRIBED), tmp_topic); + } +} + +void mqttStart() +{ + char buffer[64]; + char mqttClientId[64]; + char lastWillPayload[8]; + static uint8_t mqttReconnectCount = 0; + // bool mqttFirstConnect = true; + + mqttClient.setServer(mqttServer, 1883); + // mqttClient.setSocketTimeout(10); //in seconds + + /* Construct unique Client ID*/ + { + String mac = halGetMacAddress(3, ""); + mac.toLowerCase(); + memset(mqttClientId, 0, sizeof(mqttClientId)); + snprintf_P(mqttClientId, sizeof(mqttClientId), PSTR(D_MQTT_DEFAULT_NAME), mac.c_str()); + LOG_INFO(TAG_MQTT, mqttClientId); + } + + // Attempt to connect and set LWT and Clean Session + snprintf_P(buffer, sizeof(buffer), PSTR("%s" LWT_TOPIC), mqttNodeTopic); // lastWillTopic + snprintf_P(lastWillPayload, sizeof(lastWillPayload), PSTR("offline")); // lastWillPayload + + haspProgressMsg(F(D_MQTT_CONNECTING)); + haspProgressVal(mqttReconnectCount * 5); + if(!mqttClient.connect(mqttClientId, mqttUser, mqttPassword, buffer, 0, true, lastWillPayload, true)) { + // Retry until we give up and restart after connectTimeout seconds + mqttReconnectCount++; + + switch(mqttClient.state()) { + case MQTT_CONNECTION_TIMEOUT: + LOG_WARNING(TAG_MQTT, F("Connection timeout")); + break; + case MQTT_CONNECTION_LOST: + LOG_WARNING(TAG_MQTT, F("Connection lost")); + break; + case MQTT_CONNECT_FAILED: + LOG_WARNING(TAG_MQTT, F("Connection failed")); + break; + case MQTT_DISCONNECTED: + snprintf_P(buffer, sizeof(buffer), PSTR(D_MQTT_DISCONNECTED)); + break; + case MQTT_CONNECTED: + break; + case MQTT_CONNECT_BAD_PROTOCOL: + LOG_WARNING(TAG_MQTT, F("MQTT version not suported")); + break; + case MQTT_CONNECT_BAD_CLIENT_ID: + LOG_WARNING(TAG_MQTT, F("Client ID rejected")); + break; + case MQTT_CONNECT_UNAVAILABLE: + LOG_WARNING(TAG_MQTT, F("Server unavailable")); + break; + case MQTT_CONNECT_BAD_CREDENTIALS: + LOG_WARNING(TAG_MQTT, F("Bad credentials")); + break; + case MQTT_CONNECT_UNAUTHORIZED: + LOG_WARNING(TAG_MQTT, F("Unauthorized")); + break; + default: + LOG_WARNING(TAG_MQTT, F("Unknown failure")); + } + + if(mqttReconnectCount > 20) { + LOG_ERROR(TAG_MQTT, F("Retry count exceeded, rebooting...")); + dispatch_reboot(false); + } + return; + } + + LOG_INFO(TAG_MQTT, F(D_MQTT_CONNECTED), mqttServer, mqttClientId); + + // Subscribe to our incoming topics + const __FlashStringHelper * F_topic; + F_topic = F("%scommand/#"); + mqttSubscribeTo(F_topic, mqttGroupTopic); + mqttSubscribeTo(F_topic, mqttNodeTopic); + F_topic = F("%sconfig/#"); + mqttSubscribeTo(F_topic, mqttGroupTopic); + mqttSubscribeTo(F_topic, mqttNodeTopic); + mqttSubscribeTo(F("%slight/#"), mqttNodeTopic); + mqttSubscribeTo(F("%sbrightness/#"), mqttNodeTopic); + // mqttSubscribeTo(F("%s"LWT_TOPIC), mqttNodeTopic); + mqttSubscribeTo(F("hass/status"), mqttClientId); + + /* Home Assistant auto-configuration */ + if(mqttHAautodiscover) mqttSubscribeTo(F("homeassistant/status"), mqttClientId); + + // Force any subscribed clients to toggle offline/online when we first connect to + // make sure we get a full panel refresh at power on. Sending offline, + // "online" will be sent by the mqttStatusTopic subscription action. + mqtt_send_lwt(true); + + // mqttFirstConnect = false; + mqttReconnectCount = 0; + + haspReconnect(); + haspProgressVal(255); + + dispatch_current_state(); +} + +void mqttSetup() +{ + mqttEnabled = strlen(mqttServer) > 0 && mqttPort > 0; + if(mqttEnabled) { + mqttClient.setServer(mqttServer, mqttPort); + mqttClient.setCallback(mqtt_message_cb); + // if(!mqttClient.setBufferSize(1024)) { + // LOG_ERROR(TAG_MQTT, F("Buffer allocation failed")); + // } else { + LOG_INFO(TAG_MQTT, F(D_MQTT_STARTED), mqttClient.getBufferSize()); + // } + } else { + LOG_WARNING(TAG_MQTT, F(D_MQTT_NOT_CONFIGURED)); + } +} + +void mqttLoop(void) +{ + if(mqttEnabled) mqttClient.loop(); +} + +void mqttEvery5Seconds(bool networkIsConnected) +{ + if(mqttEnabled && networkIsConnected && !mqttClient.connected()) { + LOG_TRACE(TAG_MQTT, F(D_MQTT_RECONNECTING)); + mqttStart(); + } +} + +String mqttGetNodename() +{ + return mqttNodeName; +} + +void mqttStop() +{ + if(mqttEnabled && mqttClient.connected()) { + LOG_TRACE(TAG_MQTT, F(D_MQTT_DISCONNECTING)); + mqtt_send_lwt(false); + mqttClient.disconnect(); + LOG_INFO(TAG_MQTT, F(D_MQTT_DISCONNECTED)); + } +} + + #if HASP_USE_CONFIG > 0 +bool mqttGetConfig(const JsonObject & settings) +{ + bool changed = false; + + if(strcmp(mqttNodeName, settings[FPSTR(FP_CONFIG_NAME)].as().c_str()) != 0) changed = true; + settings[FPSTR(FP_CONFIG_NAME)] = mqttNodeName; + + if(strcmp(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)].as().c_str()) != 0) changed = true; + settings[FPSTR(FP_CONFIG_GROUP)] = mqttGroupName; + + if(strcmp(mqttServer, settings[FPSTR(FP_CONFIG_HOST)].as().c_str()) != 0) changed = true; + settings[FPSTR(FP_CONFIG_HOST)] = mqttServer; + + if(mqttPort != settings[FPSTR(FP_CONFIG_PORT)].as()) changed = true; + settings[FPSTR(FP_CONFIG_PORT)] = mqttPort; + + if(strcmp(mqttUser, settings[FPSTR(FP_CONFIG_USER)].as().c_str()) != 0) changed = true; + settings[FPSTR(FP_CONFIG_USER)] = mqttUser; + + if(strcmp(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)].as().c_str()) != 0) changed = true; + settings[FPSTR(FP_CONFIG_PASS)] = mqttPassword; + + if(changed) configOutput(settings, TAG_MQTT); + return changed; +} + +/** Set MQTT Configuration. + * + * Read the settings from json and sets the application variables. + * + * @note: data pixel should be formated 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; + + changed |= configSet(mqttPort, settings[FPSTR(FP_CONFIG_PORT)], F("mqttPort")); + + if(!settings[FPSTR(FP_CONFIG_NAME)].isNull()) { + changed |= strcmp(mqttNodeName, settings[FPSTR(FP_CONFIG_NAME)]) != 0; + strncpy(mqttNodeName, settings[FPSTR(FP_CONFIG_NAME)], sizeof(mqttNodeName)); + } + // Prefill node name + if(strlen(mqttNodeName) == 0) { + String mac = halGetMacAddress(3, ""); + mac.toLowerCase(); + snprintf_P(mqttNodeName, sizeof(mqttNodeName), PSTR(D_MQTT_DEFAULT_NAME), mac.c_str()); + changed = true; + } + + if(!settings[FPSTR(FP_CONFIG_GROUP)].isNull()) { + changed |= strcmp(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)]) != 0; + strncpy(mqttGroupName, settings[FPSTR(FP_CONFIG_GROUP)], sizeof(mqttGroupName)); + } + + if(strlen(mqttGroupName) == 0) { + strcpy_P(mqttGroupName, PSTR("plates")); + changed = true; + } + + if(!settings[FPSTR(FP_CONFIG_HOST)].isNull()) { + changed |= strcmp(mqttServer, settings[FPSTR(FP_CONFIG_HOST)]) != 0; + strncpy(mqttServer, settings[FPSTR(FP_CONFIG_HOST)], sizeof(mqttServer)); + } + + if(!settings[FPSTR(FP_CONFIG_USER)].isNull()) { + changed |= strcmp(mqttUser, settings[FPSTR(FP_CONFIG_USER)]) != 0; + strncpy(mqttUser, settings[FPSTR(FP_CONFIG_USER)], sizeof(mqttUser)); + } + + if(!settings[FPSTR(FP_CONFIG_PASS)].isNull() && + settings[FPSTR(FP_CONFIG_PASS)].as() != String(FPSTR(D_PASSWORD_MASK))) { + changed |= strcmp(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)]) != 0; + strncpy(mqttPassword, settings[FPSTR(FP_CONFIG_PASS)], sizeof(mqttPassword)); + } + + snprintf_P(mqttNodeTopic, sizeof(mqttNodeTopic), PSTR(MQTT_PREFIX "/%s/"), mqttNodeName); + snprintf_P(mqttGroupTopic, sizeof(mqttGroupTopic), PSTR(MQTT_PREFIX "/%s/"), mqttGroupName); + + return changed; +} + #endif // HASP_USE_CONFIG + + #endif // PUBSUBCLIENT + +#endif // HASP_USE_MQTT diff --git a/tools/sdl2_build_extra.py b/tools/sdl2_build_extra.py new file mode 100644 index 00000000..34ec79ce --- /dev/null +++ b/tools/sdl2_build_extra.py @@ -0,0 +1,33 @@ +Import("env", "projenv") + +for e in [ env, projenv ]: + # If compiler uses `-m32`, propagate it to linker. + # Add via script, because `-Wl,-m32` does not work. + if "-m32" in e['CCFLAGS']: + e.Append(LINKFLAGS = ["-m32"]) + +env.Append( + LINKFLAGS=[ + "-static", + "-static-libgcc", + "-static-libstdc++" + ] +) + +# Override unused "upload" to execute compiled binary +from SCons.Script import AlwaysBuild +AlwaysBuild(env.Alias("build", "$BUILD_DIR/${PROGNAME}", "$BUILD_DIR/${PROGNAME}")) + +# Add custom target to explorer +env.AddTarget( + name = "execute", + dependencies = "$BUILD_DIR\${PROGNAME}.exe", + actions = "$BUILD_DIR\${PROGNAME}.exe", +# actions = 'cmd.exe /C "start cmd.exe /C $BUILD_DIR\${PROGNAME}.exe"', + title = "Execute", + description = "Build and execute", + group="General" +) + +#print('=====================================') +#print(env.Dump())