optimized flush procedure.

Signed-off-by: imi415 <imi415@imi.moe>
This commit is contained in:
imi415 2022-10-03 15:14:45 +08:00
parent d378c2d50d
commit 9cd7aba20b
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A
19 changed files with 213315 additions and 111 deletions

@ -1 +1 @@
Subproject commit 3e2c515e99c310d3e0ecfcf336c96ef19893aa54
Subproject commit 562432a46e54d93b115f1b2132e32544e683e043

View File

@ -24,7 +24,7 @@
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 32
#define LV_COLOR_DEPTH 8
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
@ -46,10 +46,10 @@
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#define LV_MEM_CUSTOM 1
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
#define LV_MEM_SIZE (40U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/
@ -78,7 +78,7 @@
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 500 /*[ms]*/
#define LV_DISP_DEF_REFR_PERIOD 1 /*[ms] !note: this is not the actual time, set as small as possible. */
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
@ -219,7 +219,7 @@
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 0
#define LV_USE_LOG 1
#if LV_USE_LOG
/*How important log should be added:
@ -229,21 +229,21 @@
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
#define LV_LOG_LEVEL LV_LOG_LEVEL_ERROR
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
#define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
#define LV_LOG_TRACE_MEM 1
#define LV_LOG_TRACE_TIMER 1
#define LV_LOG_TRACE_INDEV 1
#define LV_LOG_TRACE_DISP_REFR 1
#define LV_LOG_TRACE_EVENT 1
#define LV_LOG_TRACE_OBJ_CREATE 1
#define LV_LOG_TRACE_LAYOUT 1
#define LV_LOG_TRACE_ANIM 1
#define LV_LOG_TRACE_MEM 0
#define LV_LOG_TRACE_TIMER 0
#define LV_LOG_TRACE_INDEV 0
#define LV_LOG_TRACE_DISP_REFR 0
#define LV_LOG_TRACE_EVENT 0
#define LV_LOG_TRACE_OBJ_CREATE 0
#define LV_LOG_TRACE_LAYOUT 0
#define LV_LOG_TRACE_ANIM 0
#endif /*LV_USE_LOG*/

@ -1 +1 @@
Subproject commit 9024b72b4853e1e7ac29a42e54b7a10d3c4f3b20
Subproject commit 755d363ecc4eb7a85583db477446343969aa5efa

View File

@ -3,10 +3,16 @@ SRCS
"app_lvgl.c"
"app_main.c"
"app_mqtt.c"
"app_ui_weather.c"
"app_ui.c"
"app_wifi.c"
"assets/fonts/noto_sans_24.c"
"assets/fonts/noto_sans_96.c"
"assets/fonts/noto_sans_150.c"
"assets/fonts/noto_sans_240.c"
"assets/fonts/weather_icon_24.c"
"assets/fonts/weather_icon_96.c"
"assets/fonts/weather_icon_150.c"
"assets/fonts/weather_icon_240.c"
INCLUDE_DIRS
"include"

View File

@ -8,19 +8,21 @@
#include "freertos/task.h"
/* IDF drivers */
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "esp_log.h"
/* EPD driver */
#include "epd_driver.h"
#include "epd_highlevel.h"
#include "epd_board_specific.h"
/* LVGL */
#include "lvgl.h"
/* Private */
#include "app_bkp_ram.h"
#include "app_lvgl.h"
#define LVGL_TASK_HEAP 4096
@ -28,6 +30,8 @@
#define APP_LOG_TAG "APP_LVGL"
#define EPD_MAX_REFR_COUNT 8
static EpdiyHighlevelState s_epd_hl;
static int s_temp = 26;
@ -36,11 +40,14 @@ static lv_color_t *s_screen_buf;
static lv_disp_drv_t s_disp_drv;
static SemaphoreHandle_t s_lvgl_semphr;
static SemaphoreHandle_t s_lvgl_flush_cmd_semphr; /* This semaphore is used to start the drawing */
static SemaphoreHandle_t s_lvgl_flush_ready_semphr; /* This semaphore is used to notify the drawing has completed */
static void app_lvgl_task(void *pvParameters);
static void app_lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
static void app_lvgl_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa);
static void app_lvgl_log_cb(const char *buf);
int app_lvgl_init(void) {
/* Initialize EPDIY */
@ -54,12 +61,14 @@ int app_lvgl_init(void) {
/* Initialize LVGL */
lv_init();
lv_log_register_print_cb(app_lvgl_log_cb);
lv_disp_draw_buf_init(&s_disp_buf, s_screen_buf, NULL, 960 * 540);
lv_disp_drv_init(&s_disp_drv);
s_disp_drv.user_data = &s_epd_hl;
s_disp_drv.draw_buf = &s_disp_buf;
s_disp_drv.hor_res = 960;
s_disp_drv.ver_res = 540;
s_disp_drv.full_refresh = true;
s_disp_drv.flush_cb = app_lvgl_flush_cb;
s_disp_drv.set_px_cb = app_lvgl_set_px_cb;
lv_disp_drv_register(&s_disp_drv);
@ -70,9 +79,21 @@ int app_lvgl_init(void) {
return -1;
}
s_lvgl_flush_ready_semphr = xSemaphoreCreateBinary();
if (s_lvgl_flush_ready_semphr == NULL) {
ESP_LOGE(APP_LOG_TAG, "Failed to create flush semaphore.");
return -2;
}
s_lvgl_flush_cmd_semphr = xSemaphoreCreateBinary();
if(s_lvgl_flush_cmd_semphr == NULL) {
ESP_LOGE(APP_LOG_TAG, "Failed to create flush command semaphore.");
return -3;
}
if (xTaskCreate(app_lvgl_task, "LV_TASK", LVGL_TASK_HEAP, NULL, 2, NULL) != pdPASS) {
ESP_LOGE(APP_LOG_TAG, "Failed to create LVGL task, available heap: %d.", esp_get_free_heap_size());
return -2;
return -4;
}
ESP_LOGI(APP_LOG_TAG, "LVGL initialized.");
@ -80,8 +101,17 @@ int app_lvgl_init(void) {
return 0;
}
int app_lvgl_deinit(void) {
epd_powerdown_lilygo_t5_47();
epd_deinit();
/* LVGL is not required to be de-init. */
return 0;
}
int app_lvgl_lock(uint32_t ms) {
if(xSemaphoreTake(s_lvgl_semphr, pdMS_TO_TICKS(ms)) != pdPASS) {
if (xSemaphoreTake(s_lvgl_semphr, pdMS_TO_TICKS(ms)) != pdPASS) {
return -1;
}
@ -89,7 +119,23 @@ int app_lvgl_lock(uint32_t ms) {
}
int app_lvgl_unlock(void) {
if(xSemaphoreGive(s_lvgl_semphr) != pdPASS) {
if (xSemaphoreGive(s_lvgl_semphr) != pdPASS) {
return -1;
}
return 0;
}
int app_lvgl_start_flush(void) {
if (xSemaphoreGive(s_lvgl_flush_cmd_semphr) != pdPASS) {
return -1;
}
return 0;
}
int app_lvgl_wait_flush(uint32_t timeout_ms) {
if (xSemaphoreTake(s_lvgl_flush_ready_semphr, pdMS_TO_TICKS(timeout_ms)) != pdPASS) {
return -1;
}
@ -97,8 +143,14 @@ int app_lvgl_unlock(void) {
}
static void app_lvgl_task(void *pvParameters) {
if(xSemaphoreTake(s_lvgl_flush_cmd_semphr, portMAX_DELAY) != pdPASS) {
vTaskSuspend(NULL);
}
ESP_LOGI(APP_LOG_TAG, "Received flush GO signal, start flushing...");
for (;;) {
if(app_lvgl_lock(50) == 0) {
if (app_lvgl_lock(50) == 0) {
lv_timer_handler();
ESP_LOGD(APP_LOG_TAG, "LVGL timer handler executed.");
@ -122,9 +174,12 @@ static void app_lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv
ESP_LOGI(APP_LOG_TAG, "Flush called.");
epd_poweron();
epd_hl_update_area(hl, MODE_GC16, s_temp, rect);
epd_hl_update_area(hl, MODE_GL16, s_temp, rect);
epd_poweroff();
// Done flushing..
xSemaphoreGive(s_lvgl_flush_ready_semphr);
lv_disp_flush_ready(disp_drv);
}
@ -142,4 +197,8 @@ static void app_lvgl_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t
buf[px_offset] &= 0x0F;
buf[px_offset] |= pix_y << 4;
}
}
static void app_lvgl_log_cb(const char *buf) {
ESP_LOGI(APP_LOG_TAG, "LVGL: %s", buf);
}

View File

@ -7,21 +7,37 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_system.h"
/* ESP libraries */
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_system.h"
/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
/* MISC */
#include "cbor.h"
#include "nvs_flash.h"
/* APP */
#include "app_bkp_ram.h"
#include "app_lvgl.h"
#include "app_mqtt.h"
#include "app_ui.h"
#include "app_wifi.h"
#define APP_WAKE_INTERVAL_MS (30 * 60 * 1000)
#define APP_LOG_TAG "MAIN"
RTC_NOINIT_ATTR app_bkp_ram_t g_app_bkp;
int app_ui_weather_init(void);
static CborError app_get_condition_string(CborValue *it, char *key, char **value, uint32_t *size);
static int app_windlevel_to_font(char *input, char **output);
static int app_condition_to_font(char *input, char **output);
static int app_update_ui(void);
void app_main(void) {
esp_err_t ret = nvs_flash_init();
@ -35,19 +51,331 @@ void app_main(void) {
ESP_ERROR_CHECK(ret);
if(app_wifi_init() != 0) {
return;
if (app_wifi_init() != 0) {
goto next_round;
}
if(app_mqtt_init() != 0) {
return;
if (app_mqtt_init() != 0) {
goto next_round;
}
if(app_lvgl_init() != 0) {
return;
if (app_wifi_wait_ready(5000) != 0) {
ESP_LOGW(APP_LOG_TAG, "Failed to connect to WiFi in time, bail out.");
goto next_round;
}
if(app_ui_weather_init() != 0) {
return;
if (app_lvgl_init() != 0) {
goto next_round;
}
if (app_ui_init() != 0) {
goto next_round;
}
if (app_update_ui() != 0) {
goto next_round;
}
app_lvgl_start_flush();
if(app_lvgl_wait_flush(5000) < 0) {
ESP_LOGW(APP_LOG_TAG, "Did not get flush ready flag in time, bail out.");
goto next_round;
}
next_round:
ESP_LOGI(APP_LOG_TAG, "Enter deep power down mode, interval: %d", APP_WAKE_INTERVAL_MS);
app_lvgl_deinit();
esp_deep_sleep(APP_WAKE_INTERVAL_MS * 1000);
}
static int app_update_ui(void) {
app_mqtt_subscribe("iot/weather/ESP32_Weather/response");
CborEncoder root_encoder;
uint8_t *tx_buf = malloc(100);
if (tx_buf == NULL) {
ESP_LOGE(APP_LOG_TAG, "Failed to allocate tx buffer");
return -1;
}
cbor_encoder_init(&root_encoder, tx_buf, 100, 0);
CborEncoder map_encoder;
cbor_encoder_create_map(&root_encoder, &map_encoder, 2);
cbor_encode_text_stringz(&map_encoder, "type");
cbor_encode_text_stringz(&map_encoder, "condition");
cbor_encode_text_stringz(&map_encoder, "city_id");
cbor_encode_uint(&map_encoder, 10);
cbor_encoder_close_container(&root_encoder, &map_encoder);
uint32_t data_size = cbor_encoder_get_buffer_size(&root_encoder, tx_buf);
ESP_LOGI(APP_LOG_TAG, "Encoded CBOR message %d bytes.", data_size);
app_mqtt_publish("iot/weather/ESP32_Weather/request", tx_buf, data_size);
free(tx_buf);
char *topic;
uint8_t *payload;
uint32_t payload_len;
if (app_mqtt_poll(&topic, &payload, &payload_len, 10000) != 0) {
ESP_LOGE(APP_LOG_TAG, "failed to receive payload in time, bail out");
return -2;
}
ESP_LOGI(APP_LOG_TAG, "Received %d bytes from topic %s", payload_len, topic);
CborParser root_parser;
CborValue it;
CborError cbor_ret;
cbor_ret = cbor_parser_init(payload, payload_len, 0, &root_parser, &it);
if (cbor_ret != CborNoError) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 1");
return -3;
}
char *str;
uint32_t size;
if (app_get_condition_string(&it, "sunRise", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_SUNRISE, str);
free(str);
}
if (app_get_condition_string(&it, "sunSet", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_SUNSET, str);
free(str);
}
if (app_get_condition_string(&it, "uvi", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_UVI, str);
free(str);
}
if (app_get_condition_string(&it, "updatetime", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_UPDATED, str);
free(str);
}
if (app_get_condition_string(&it, "temp", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_TEMP, str);
free(str);
}
if (app_get_condition_string(&it, "humidity", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_HUMID, str);
free(str);
}
if (app_get_condition_string(&it, "realFeel", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_TEMP_REAL, str);
free(str);
}
if (app_get_condition_string(&it, "windSpeed", &str, &size) == CborNoError) {
app_ui_update(APP_UI_CMD_WIND_SPEED, str);
free(str);
}
if (app_get_condition_string(&it, "windLevel", &str, &size) == CborNoError) {
app_windlevel_to_font(str, &str);
app_ui_update(APP_UI_CMD_WIND_LEVEL, str);
free(str);
}
if (app_get_condition_string(&it, "conditionId", &str, &size) == CborNoError) {
app_condition_to_font(str, &str);
app_ui_update(APP_UI_CMD_COND, str);
free(str);
}
free(topic);
free(payload);
return 0;
}
static int app_condition_to_font(char *input, char **output) {
*output = malloc(4);
if (*output == NULL) return -1;
char *unicode = "\U0000F00D";
int wx_id = strtol(input, NULL, 10);
if (wx_id <= 5) {
/* Do nothing */
} else if (wx_id <= 7) {
unicode = "\U0000F072";
} else if (wx_id <= 11) {
unicode = "\U0000F002";
} else if (wx_id <= 12) {
unicode = "\U0000F072";
} else if (wx_id <= 14) {
unicode = "\U0000F013";
} else if (wx_id <= 23) {
unicode = "\U0000F009";
} else if (wx_id <= 25) {
unicode = "\U0000F00A";
} else if (wx_id <= 28) {
unicode = "\U0000F003";
} else if (wx_id <= 33) {
unicode = "\U0000F082";
} else if (wx_id <= 35) {
unicode = "\U0000F0B6";
} else if (wx_id <= 36) {
unicode = "\U0000F041";
} else if (wx_id <= 41) {
unicode = "\U0000F00E";
} else if (wx_id <= 43) {
unicode = "\U0000F005";
} else if (wx_id <= 45) {
unicode = "\U0000F068";
} else if (wx_id <= 48) {
unicode = "\U0000F004";
} else if (wx_id <= 50) {
unicode = "\U0000F006";
} else if (wx_id <= 57) {
unicode = "\U0000F008";
} else if (wx_id <= 63) {
unicode = "\U0000F00A";
} else if (wx_id <= 65) {
unicode = "\U0000F006";
} else if (wx_id <= 70) {
unicode = "\U0000F008";
} else if (wx_id <= 77) {
unicode = "\U0000F00A";
} else if (wx_id <= 78) {
unicode = "\U0000F008";
} else if (wx_id <= 79) {
unicode = "\U0000F0B6";
} else if (wx_id <= 82) {
unicode = "\U0000F002";
} else if (wx_id <= 84) {
unicode = "\U0000F003";
} else if (wx_id <= 85) {
unicode = "\U0000F002";
} else if (wx_id <= 86) {
unicode = "\U0000F009";
} else if (wx_id <= 90) {
unicode = "\U0000F00E";
} else if (wx_id <= 93) {
unicode = "\U0000F008";
} else if (wx_id <= 94) {
unicode = "\U0000F00A";
}
snprintf(*output, 4, "%s", unicode);
free(input);
return 0;
}
static int app_windlevel_to_font(char *input, char **output) {
*output = malloc(4);
if (*output == NULL) return -1;
char *unicode = "\U0000F0B7";
if (input[1] != '\0') {
switch (input[0]) {
case '0':
unicode = "\U0000F0C1";
break;
case '1':
unicode = "\U0000F0C2";
break;
case '2':
unicode = "\U0000F0C3";
break;
default:
break;
}
} else {
switch (input[0]) {
case '0':
unicode = "\U0000F0B7";
break;
case '1':
unicode = "\U0000F0B8";
break;
case '2':
unicode = "\U0000F0B9";
break;
case '3':
unicode = "\U0000F0BA";
break;
case '4':
unicode = "\U0000F0BB";
break;
case '5':
unicode = "\U0000F0BC";
break;
case '6':
unicode = "\U0000F0BD";
break;
case '7':
unicode = "\U0000F0BE";
break;
case '8':
unicode = "\U0000F0BF";
break;
case '9':
unicode = "\U0000F0C0";
break;
}
}
snprintf(*output, 4, "%s", unicode);
free(input);
return 0;
}
static CborError app_get_condition_string(CborValue *it, char *key, char **value, uint32_t *size) {
CborError ret;
CborValue data;
ret = cbor_value_map_find_value(it, "data", &data);
if (ret != CborNoError) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 2");
return ret;
}
if (cbor_value_get_type(&data) != CborMapType) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 3");
return ret;
}
CborValue condition;
ret = cbor_value_map_find_value(&data, "condition", &condition);
if (ret != CborNoError) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 4");
return ret;
}
if (cbor_value_get_type(&condition) != CborMapType) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 5");
return ret;
}
CborValue result;
ret = cbor_value_map_find_value(&condition, key, &result);
if (ret != CborNoError) {
ESP_LOGE(APP_LOG_TAG, "CBOR Parse 6");
return ret;
}
if (cbor_value_is_text_string(&result)) {
cbor_value_dup_text_string(&result, value, (size_t *)&size, &result);
}
return ret;
}

View File

@ -19,9 +19,17 @@
#define APP_LOG_TAG "APP_MQTT"
typedef enum {
APP_MQTT_CMD_PUBLISH,
APP_MQTT_CMD_SUBSCRIBE,
APP_MQTT_CMD_UNSUBSCRIBE,
} app_mqtt_queue_cmd_t;
typedef struct {
char *topic;
char *payload;
app_mqtt_queue_cmd_t cmd;
char *topic;
uint8_t *payload;
uint32_t payload_len;
} app_mqtt_queue_item_t;
extern const char mqtt_client_cert_start[] asm("_binary_client_crt_start");
@ -32,44 +40,108 @@ extern const char mqtt_client_key_end[] asm("_binary_client_key_end");
static void app_mqtt_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
static void app_mqtt_task(void *pvParameters);
static QueueHandle_t s_app_mqtt_publish_queue;
static QueueHandle_t s_app_mqtt_command_queue;
static QueueHandle_t s_app_mqtt_response_queue;
int app_mqtt_init(void) {
s_app_mqtt_publish_queue = xQueueCreate(4, sizeof(app_mqtt_queue_item_t));
if(s_app_mqtt_publish_queue == NULL) {
s_app_mqtt_command_queue = xQueueCreate(4, sizeof(app_mqtt_queue_item_t));
if (s_app_mqtt_command_queue == NULL) {
return -1;
}
s_app_mqtt_response_queue = xQueueCreate(4, sizeof(app_mqtt_queue_item_t));
if (s_app_mqtt_response_queue == NULL) {
return -2;
}
if (xTaskCreate(app_mqtt_task, "MQ_TASK", 2048, NULL, 2U, NULL) != pdPASS) {
return -3;
}
return 0;
}
int app_mqtt_publish(char *topic, uint8_t *payload, uint32_t payload_len) {
app_mqtt_queue_item_t item;
item.cmd = APP_MQTT_CMD_PUBLISH;
item.topic = malloc(strlen(topic) + 1);
if (item.topic == NULL) return -1;
item.payload = malloc(payload_len);
if (item.payload == NULL) {
free(item.topic);
return -2;
}
item.payload_len = payload_len;
strcpy(item.topic, topic);
memcpy(item.payload, payload, payload_len);
if (xQueueSend(s_app_mqtt_command_queue, &item, portMAX_DELAY) != pdPASS) {
free(item.topic);
free(item.payload);
return -3;
}
return 0;
}
int app_mqtt_subscribe(char *topic) {
app_mqtt_queue_item_t item;
item.cmd = APP_MQTT_CMD_SUBSCRIBE;
item.topic = malloc(strlen(topic) + 1);
if (item.topic == NULL) {
return -1;
}
strcpy(item.topic, topic);
if (xQueueSend(s_app_mqtt_command_queue, &item, portMAX_DELAY) != pdPASS) {
free(item.topic);
return -2;
}
return 0;
}
int app_mqtt_publish(char *topic, char *payload) {
int app_mqtt_unsubscribe(char *topic) {
app_mqtt_queue_item_t item;
item.topic = malloc(strlen(topic) + 1);
if(item.topic == NULL) return -1;
item.cmd = APP_MQTT_CMD_UNSUBSCRIBE;
item.payload = malloc(strlen(payload) + 1);
if(item.payload == NULL) {
free(item.topic);
return -2;
item.topic = malloc(strlen(topic) + 1);
if (item.topic == NULL) {
return -1;
}
strcpy(item.topic, topic);
strcpy(item.payload, payload);
if(xQueueSend(s_app_mqtt_publish_queue, &item, portMAX_DELAY) != pdPASS) {
if (xQueueSend(s_app_mqtt_command_queue, &item, portMAX_DELAY) != pdPASS) {
free(item.topic);
free(item.payload);
return -3;
return -2;
}
return 0;
}
int app_mqtt_poll(char **topic, uint8_t **payload, uint32_t *payload_len, uint32_t timeout) {
app_mqtt_queue_item_t item;
if (xQueueReceive(s_app_mqtt_response_queue, &item, pdMS_TO_TICKS(timeout)) != pdPASS) {
return -1;
}
*topic = item.topic;
*payload = item.payload;
*payload_len = item.payload_len;
return 0;
}
static void app_mqtt_task(void *pvParameters) {
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = CONFIG_APP_MQTT_BROKER_ADDR,
@ -90,16 +162,70 @@ static void app_mqtt_task(void *pvParameters) {
app_mqtt_queue_item_t item;
for (;;) {
if(xQueueReceive(s_app_mqtt_publish_queue, &item, portMAX_DELAY) == pdPASS) {
esp_mqtt_client_publish(client, item.topic, item.payload, strlen(item.payload), 0, 0);
if (xQueueReceive(s_app_mqtt_command_queue, &item, portMAX_DELAY) == pdPASS) {
switch (item.cmd) {
case APP_MQTT_CMD_PUBLISH: {
esp_mqtt_client_publish(client, item.topic, (char *)item.payload, item.payload_len, 0, 0);
/* This is alloc'ed by us. */
free(item.topic);
free(item.payload);
/* This is alloc'ed by us. */
free(item.topic);
free(item.payload);
} break;
case APP_MQTT_CMD_SUBSCRIBE: {
esp_mqtt_client_subscribe(client, item.topic, 0);
free(item.topic);
} break;
case APP_MQTT_CMD_UNSUBSCRIBE: {
esp_mqtt_client_unsubscribe(client, item.topic);
free(item.topic);
}
default:
break;
}
}
}
}
static void app_mqtt_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
/**/
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_DATA:
ESP_LOGI(APP_LOG_TAG, "Received from: %.*s", event->topic_len, event->topic);
app_mqtt_queue_item_t item;
item.topic = malloc(event->topic_len + 1);
if (item.topic == NULL) {
ESP_LOGE(APP_LOG_TAG, "Failed to allocate topic");
return;
}
item.payload = malloc(event->data_len);
if (item.payload == NULL) {
ESP_LOGE(APP_LOG_TAG, "Failed to allocate data");
free(item.topic);
return;
}
memcpy(item.topic, event->topic, event->topic_len);
memcpy(item.payload, event->data, event->data_len);
item.topic[event->topic_len] = '\0';
item.payload_len = event->data_len;
if (xQueueSend(s_app_mqtt_response_queue, &item, pdMS_TO_TICKS(500)) != pdTRUE) {
ESP_LOGE(APP_LOG_TAG, "Response queue is full...");
free(item.topic);
free(item.payload);
}
break;
default:
break;
}
}

366
main/app_ui.c Normal file
View File

@ -0,0 +1,366 @@
#include <stdio.h>
#include "sdkconfig.h"
/**/
#include "esp_log.h"
#include "esp_system.h"
/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
/* LVGL */
#include "lvgl.h"
/* Private */
#include "app_lvgl.h"
#include "app_ui.h"
#define UI_TASK_HEAP 4096
#define APP_LOG_TAG "UI_WX"
typedef struct {
app_ui_cmd_t cmd;
char *data;
} app_ui_queue_item_t;
LV_FONT_DECLARE(noto_sans_24)
LV_FONT_DECLARE(noto_sans_96)
LV_FONT_DECLARE(noto_sans_150)
LV_FONT_DECLARE(noto_sans_240)
LV_FONT_DECLARE(weather_icon_24)
LV_FONT_DECLARE(weather_icon_96)
LV_FONT_DECLARE(weather_icon_150)
LV_FONT_DECLARE(weather_icon_240)
static lv_style_t s_epd_style;
static lv_theme_t s_epd_theme;
static QueueHandle_t s_app_ui_update_queue;
static void app_ui_theme_apply(lv_theme_t *th, lv_obj_t *obj);
static void app_ui_task(void *pvParameters);
int app_ui_init(void) {
s_app_ui_update_queue = xQueueCreate(4, sizeof(app_ui_queue_item_t));
if (s_app_ui_update_queue == NULL) {
return -1;
}
if (xTaskCreate(app_ui_task, "UI_WX", UI_TASK_HEAP, NULL, 3, NULL) != pdPASS) {
ESP_LOGE(APP_LOG_TAG, "UI WX task failed to create.");
return -2;
}
ESP_LOGI(APP_LOG_TAG, "UI WX initialized.");
return 0;
}
int app_ui_update(app_ui_cmd_t cmd, char *data) {
app_ui_queue_item_t item;
item.cmd = cmd;
item.data = malloc(strlen(data));
if (item.data == NULL) {
return -1;
}
strcpy(item.data, data);
if (xQueueSend(s_app_ui_update_queue, &item, portMAX_DELAY) != pdPASS) {
return -2;
}
return 0;
}
static void app_ui_task(void *pvParameters) {
lv_obj_t *screen_cond = NULL;
/* Top bar */
lv_obj_t *container_topbar = NULL;
lv_obj_t *label_sunrise = NULL;
lv_obj_t *label_sunrise_time = NULL;
lv_obj_t *label_sunset = NULL;
lv_obj_t *label_sunset_time = NULL;
lv_obj_t *label_uvi = NULL;
lv_obj_t *label_uvi_value = NULL;
lv_obj_t *label_updated = NULL;
lv_obj_t *label_updated_time = NULL;
/* Seperation line */
lv_obj_t *line_topbar = NULL;
/* Middle display */
lv_obj_t *container_main = NULL;
lv_obj_t *label_wxicon = NULL;
lv_obj_t *label_temperature = NULL;
lv_obj_t *label_temperature_hi = NULL;
lv_obj_t *label_temperature_lo = NULL;
lv_obj_t *label_temperature_real = NULL;
lv_obj_t *label_temperature_real_desc = NULL;
lv_obj_t *label_temperature_unit = NULL;
lv_obj_t *label_temperature_hi_unit = NULL;
lv_obj_t *label_temperature_lo_unit = NULL;
lv_obj_t *label_temperature_real_unit = NULL;
lv_obj_t *label_humidity = NULL;
lv_obj_t *label_humidity_unit = NULL;
lv_obj_t *label_wind_level = NULL;
lv_obj_t *label_wind_speed = NULL;
lv_obj_t *label_wind_direction = NULL;
lv_point_t points_line_topbar[] = {{0, 37}, {959, 37}};
if (app_lvgl_lock(portMAX_DELAY) == 0) {
lv_style_init(&s_epd_style);
lv_style_set_pad_all(&s_epd_style, 0);
lv_style_set_radius(&s_epd_style, 0);
lv_style_set_border_width(&s_epd_style, 0);
lv_theme_set_apply_cb(&s_epd_theme, app_ui_theme_apply);
lv_disp_set_theme(NULL, &s_epd_theme);
screen_cond = lv_obj_create(NULL);
container_topbar = lv_obj_create(screen_cond);
label_sunrise = lv_label_create(container_topbar);
label_sunrise_time = lv_label_create(container_topbar);
label_sunset = lv_label_create(container_topbar);
label_sunset_time = lv_label_create(container_topbar);
label_uvi = lv_label_create(container_topbar);
label_uvi_value = lv_label_create(container_topbar);
label_updated = lv_label_create(container_topbar);
label_updated_time = lv_label_create(container_topbar);
line_topbar = lv_line_create(screen_cond);
container_main = lv_obj_create(screen_cond);
label_wxicon = lv_label_create(container_main);
label_temperature = lv_label_create(container_main);
label_temperature_hi = lv_label_create(container_main);
label_temperature_lo = lv_label_create(container_main);
label_temperature_real = lv_label_create(container_main);
label_temperature_real_desc = lv_label_create(container_main);
label_temperature_unit = lv_label_create(container_main);
label_temperature_hi_unit = lv_label_create(container_main);
label_temperature_lo_unit = lv_label_create(container_main);
label_temperature_real_unit = lv_label_create(container_main);
label_humidity = lv_label_create(container_main);
label_humidity_unit = lv_label_create(container_main);
label_wind_level = lv_label_create(container_main);
label_wind_direction = lv_label_create(container_main);
label_wind_speed = lv_label_create(container_main);
/* Top bar parts */
lv_obj_set_style_text_font(label_sunrise, &weather_icon_24, 0U);
lv_obj_set_style_text_font(label_sunset, &weather_icon_24, 0U);
lv_obj_set_style_text_font(label_uvi, &weather_icon_24, 0U);
lv_obj_set_style_text_font(label_updated, &weather_icon_24, 0U);
lv_obj_set_style_text_font(label_sunrise_time, &noto_sans_24, 0U);
lv_obj_set_style_text_font(label_sunset_time, &noto_sans_24, 0U);
lv_obj_set_style_text_font(label_uvi_value, &noto_sans_24, 0U);
lv_obj_set_style_text_font(label_updated_time, &noto_sans_24, 0U);
lv_label_set_text(label_sunrise, "\U0000F051");
lv_label_set_text(label_sunset, "\U0000F052");
lv_label_set_text(label_uvi, "\U0000F00D");
lv_label_set_text(label_updated, "\U0000F04C");
lv_label_set_text(label_sunrise_time, "----/--/-- --:--:--");
lv_label_set_text(label_sunset_time, "----/--/-- --:--:--");
lv_label_set_text(label_uvi_value, "-");
lv_label_set_text(label_updated_time, "----/--/-- --:--:--");
lv_obj_set_size(container_topbar, 960, 36);
lv_obj_set_width(label_sunrise, 32);
lv_obj_set_width(label_sunset, 32);
lv_obj_set_width(label_uvi, 32);
lv_obj_set_width(label_updated, 32);
lv_obj_set_style_text_align(label_sunrise, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_sunset, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_uvi, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_updated, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_sunrise_time, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_align(label_sunset_time, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_align(label_uvi_value, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_align(label_updated_time, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_align(container_topbar, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_align(label_sunrise, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_align(label_sunset, LV_ALIGN_LEFT_MID, 300, 0);
lv_obj_align(label_uvi, LV_ALIGN_LEFT_MID, 600, 0);
lv_obj_align(label_updated, LV_ALIGN_LEFT_MID, 680, 0);
lv_obj_align(label_sunrise_time, LV_ALIGN_LEFT_MID, 40, 3);
lv_obj_align(label_sunset_time, LV_ALIGN_LEFT_MID, 340, 3);
lv_obj_align(label_uvi_value, LV_ALIGN_LEFT_MID, 640, 3);
lv_obj_align(label_updated_time, LV_ALIGN_LEFT_MID, 720, 3);
lv_line_set_points(line_topbar, points_line_topbar, 2);
lv_obj_set_style_line_width(line_topbar, 1, 0);
/* Main parts */
lv_obj_set_style_text_font(label_wxicon, &weather_icon_150, 0);
lv_obj_set_style_text_font(label_temperature_unit, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_humidity_unit, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_temperature_hi_unit, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_temperature_lo_unit, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_temperature_real_unit, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_wind_direction, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_wind_level, &weather_icon_96, 0);
lv_obj_set_style_text_font(label_temperature, &noto_sans_150, 0);
lv_obj_set_style_text_font(label_temperature_hi, &noto_sans_96, 0);
lv_obj_set_style_text_font(label_temperature_lo, &noto_sans_96, 0);
lv_obj_set_style_text_font(label_temperature_real, &noto_sans_96, 0);
lv_obj_set_style_text_font(label_temperature_real_desc, &noto_sans_24, 0);
lv_obj_set_style_text_font(label_humidity, &noto_sans_96, 0);
lv_obj_set_style_text_font(label_wind_speed, &noto_sans_96, 0);
lv_label_set_text(label_wxicon, "\U0000F07B");
lv_label_set_text(label_temperature_unit, "\U0000F03C");
lv_label_set_text(label_temperature_hi_unit, "\U0000F058");
lv_label_set_text(label_temperature_lo_unit, "\U0000F044");
lv_label_set_text(label_temperature_real_unit, "\U0000F03C");
lv_label_set_text(label_humidity_unit, "\U0000F07A");
lv_label_set_text(label_wind_direction, "\U0000F0B1");
lv_label_set_text(label_wind_level, "\U0000F0BA");
lv_label_set_text(label_temperature, "--");
lv_label_set_text(label_temperature_hi, "--");
lv_label_set_text(label_temperature_lo, "--");
lv_label_set_text(label_temperature_real, "--");
lv_label_set_text(label_temperature_real_desc, "Real\nFeel");
lv_label_set_text(label_humidity, "---");
lv_label_set_text(label_wind_speed, "----");
lv_obj_set_size(container_main, 960, 370);
lv_obj_set_size(label_temperature_unit, 96, 96);
lv_obj_set_size(label_temperature_hi_unit, 96, 96);
lv_obj_set_size(label_temperature_lo_unit, 96, 96);
lv_obj_set_size(label_temperature_real_unit, 96, 96);
lv_obj_set_size(label_humidity_unit, 96, 150);
lv_obj_set_size(label_wind_direction, 96, 96);
lv_obj_set_size(label_wind_level, 96, 96);
lv_obj_set_width(label_wxicon, 200);
lv_obj_set_width(label_temperature, 200);
lv_obj_set_width(label_temperature_hi, 150);
lv_obj_set_width(label_temperature_lo, 150);
lv_obj_set_width(label_temperature_real, 150);
lv_obj_set_width(label_temperature_real_desc, 50);
lv_obj_set_width(label_humidity, 200);
lv_obj_set_width(label_wind_speed, 200);
lv_obj_set_style_text_align(label_wxicon, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_temperature, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_text_align(label_temperature_hi, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_text_align(label_temperature_lo, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_text_align(label_temperature_real, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_text_align(label_temperature_real_desc, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_align(label_temperature_unit, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_temperature_hi_unit, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_temperature_lo_unit, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_temperature_real_unit, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_humidity, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_text_align(label_humidity_unit, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_wind_direction, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_wind_level, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_text_align(label_wind_speed, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_align(container_main, LV_ALIGN_TOP_LEFT, 0, 38);
lv_obj_align(label_wxicon, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_align(label_temperature_unit, LV_ALIGN_TOP_LEFT, 400, -20);
lv_obj_align(label_temperature_hi_unit, LV_ALIGN_TOP_LEFT, 200, 115);
lv_obj_align(label_temperature_lo_unit, LV_ALIGN_TOP_LEFT, 200, 235);
lv_obj_align(label_temperature, LV_ALIGN_TOP_LEFT, 200, 0);
lv_obj_align(label_temperature_hi, LV_ALIGN_TOP_LEFT, 250, 150);
lv_obj_align(label_temperature_lo, LV_ALIGN_TOP_LEFT, 250, 250);
lv_obj_align(label_temperature_real, LV_ALIGN_TOP_LEFT, 650, 130);
lv_obj_align(label_temperature_real_desc, LV_ALIGN_TOP_LEFT, 585, 165);
lv_obj_align(label_humidity, LV_ALIGN_TOP_LEFT, 600, 10);
lv_obj_align(label_humidity_unit, LV_ALIGN_TOP_LEFT, 800, -20);
lv_obj_align(label_temperature_real_unit, LV_ALIGN_TOP_LEFT, 800, 120);
lv_obj_align(label_wind_direction, LV_ALIGN_TOP_LEFT, 500, 230);
lv_obj_align(label_wind_speed, LV_ALIGN_TOP_LEFT, 600, 250);
lv_obj_align(label_wind_level, LV_ALIGN_TOP_LEFT, 815, 240);
lv_scr_load(screen_cond);
app_lvgl_unlock();
}
app_ui_queue_item_t item;
for (;;) {
/* Receive update requests. */
if (xQueueReceive(s_app_ui_update_queue, &item, portMAX_DELAY) == pdPASS) {
if (app_lvgl_lock(portMAX_DELAY) == 0) {
lv_obj_t *label = NULL;
switch (item.cmd) {
case APP_UI_CMD_SUNRISE:
label = label_sunrise_time;
break;
case APP_UI_CMD_SUNSET:
label = label_sunset_time;
break;
case APP_UI_CMD_UVI:
label = label_uvi_value;
break;
case APP_UI_CMD_COND:
label = label_wxicon;
break;
case APP_UI_CMD_TEMP:
label = label_temperature;
break;
case APP_UI_CMD_TEMP_HI:
label = label_temperature_hi;
break;
case APP_UI_CMD_TEMP_LO:
label = label_temperature_lo;
break;
case APP_UI_CMD_TEMP_REAL:
label = label_temperature_real;
break;
case APP_UI_CMD_HUMID:
label = label_humidity;
break;
case APP_UI_CMD_UPDATED:
label = label_updated_time;
break;
case APP_UI_CMD_WIND_SPEED:
label = label_wind_speed;
break;
case APP_UI_CMD_WIND_LEVEL:
label = label_wind_level;
break;
default:
break;
}
lv_label_set_text(label, item.data);
app_lvgl_unlock();
}
free(item.data);
}
}
}
static void app_ui_theme_apply(lv_theme_t *th, lv_obj_t *obj) {
lv_obj_add_style(obj, &s_epd_style, 0);
}

View File

@ -1,55 +0,0 @@
#include <stdio.h>
#include "sdkconfig.h"
/**/
#include "esp_log.h"
#include "esp_system.h"
/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* LVGL */
#include "lvgl.h"
/* Private */
#include "app_lvgl.h"
#define UI_WEATHER_TASK_HEAP 2048
#define APP_LOG_TAG "UI_WX"
LV_FONT_DECLARE(noto_sans_96)
LV_FONT_DECLARE(weather_icon_96)
static void app_ui_weather_task(void *pvParameters);
int app_ui_weather_init(void) {
if (xTaskCreate(app_ui_weather_task, "UI_WX", UI_WEATHER_TASK_HEAP, NULL, 3, NULL) != pdPASS) {
return -1;
}
ESP_LOGI(APP_LOG_TAG, "UI WX initialized.");
return 0;
}
static void app_ui_weather_task(void *pvParameters) {
lv_obj_t *main_label = NULL;
if (app_lvgl_lock(portMAX_DELAY) == 0) {
main_label = lv_label_create(lv_scr_act());
lv_label_set_text(main_label, "\U0000F052");
lv_obj_set_width(main_label, 960);
lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(main_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_font(main_label, &weather_icon_96, 0);
ESP_LOGI(APP_LOG_TAG, "UI label created");
app_lvgl_unlock();
}
for (;;) {
vTaskSuspend(NULL);
}
}

0
main/app_weather.c Normal file
View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,10 @@
#include <stdint.h>
int app_lvgl_init(void);
int app_lvgl_deinit(void);
int app_lvgl_lock(uint32_t ms);
int app_lvgl_unlock(void);
int app_lvgl_start_flush(void);
int app_lvgl_wait_flush(uint32_t timeout_ms);
#endif

View File

@ -2,6 +2,9 @@
#define APP_MQTT_H
int app_mqtt_init(void);
int app_mqtt_publish(char *topic, char *payload);
int app_mqtt_publish(char *topic, uint8_t *payload, uint32_t payload_len);
int app_mqtt_subscribe(char *topic);
int app_mqtt_unsubscribe(char *topic);
int app_mqtt_poll(char **topic, uint8_t **payload, uint32_t *payload_len, uint32_t timeout);
#endif

24
main/include/app_ui.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef APP_UI_H
#define APP_UI_H
typedef enum {
APP_UI_CMD_SUNRISE,
APP_UI_CMD_SUNSET,
APP_UI_CMD_UVI,
APP_UI_CMD_UPDATED,
APP_UI_CMD_TEMP,
APP_UI_CMD_COND,
APP_UI_CMD_TEMP_HI,
APP_UI_CMD_TEMP_LO,
APP_UI_CMD_HUMID,
APP_UI_CMD_TEMP_REAL,
APP_UI_CMD_WIND_DIR,
APP_UI_CMD_WIND_SPEED,
APP_UI_CMD_WIND_LEVEL,
} app_ui_cmd_t;
int app_ui_init(void);
int app_ui_update(app_ui_cmd_t cmd, char *data);
#endif