Compare commits

...

6 Commits

Author SHA1 Message Date
imi415 1af5bf82f2
Fixed bugs in weekday, implemented LTC2941 driver. 2022-02-08 21:45:58 +08:00
imi415 66c4b9b064
More style rework. 2022-01-03 13:48:09 +08:00
imi415 228883c700
Optimized interface event routines. 2022-01-03 10:52:35 +08:00
imi415 38b517ca1b
Enabled wifi temporarily 2022-01-02 00:41:18 +08:00
imi415 f531b89c4d
Added clock. 2022-01-01 17:11:20 +08:00
imi415 85ce092f39
Added DHT refresh logic. 2022-01-01 15:57:04 +08:00
35 changed files with 939 additions and 78695 deletions

View File

@ -4,13 +4,16 @@ idf_component_register(SRCS
"assets/fonts/bebas_neue/bebas_neue_120.c"
"assets/fonts/material_webfont/material_webfont_32.c"
"main.c"
"helper/helper_wifi.c"
"impl/impl_btn.c"
"impl/impl_dht.c"
"impl/impl_epd.c"
"impl/impl_lvgl.c"
"impl/impl_sdfs.c"
"interface/if_standby.c"
"interface/if_wifi.c"
"lib/epd-spi/src/epd_common.c"
"lib/epd-spi/src/epd_panel_gdew042t2.c"
"lib/htu21d/htu21d.c"
"lib/lvgl/src/core/lv_disp.c"
"lib/lvgl/src/core/lv_event.c"
"lib/lvgl/src/core/lv_group.c"
@ -163,11 +166,18 @@ idf_component_register(SRCS
"lib/lvgl/src/widgets/lv_switch.c"
"lib/lvgl/src/widgets/lv_table.c"
"lib/lvgl/src/widgets/lv_textarea.c"
"service/service_battery.c"
"service/service_clock.c"
"service/service_dht.c"
"service/service_wifi.c"
INCLUDE_DIRS
"lib"
"lib/epd-spi/include"
"lib/htu21d"
"lib/lvgl"
"impl"
"include"
"interface"
"service"
)

20
main/Kconfig.projbuild Normal file
View File

@ -0,0 +1,20 @@
menu "Application Specific Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_WIFI_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
endmenu

View File

@ -0,0 +1,18 @@
#ifndef FONT_HELPER_H
#define FONT_HELPER_H
#define MDI_WIFI_STRENGTH_OFF "\U000F092D"
#define MDI_WIFI_STRENGTH_4 "\U000F0928"
#define MDI_WIFI_STRENGTH_3 "\U000F0925"
#define MDI_WIFI_STRENGTH_2 "\U000F0922"
#define MDI_WIFI_STRENGTH_1 "\U000F091F"
#define MDI_WIFI_STRENGTH_1_ALERT "\U000F0920"
#define MDI_BATTERY "\U000F0079"
#define MDI_BATTERY_10 "\U000F007A"
#define MDI_WEATHER_SUNNY "\U000F0599"
#define MDI_WATER "\U000F058C"
#define MDI_WATER_CHECK "\U000F1504"
#endif

View File

@ -0,0 +1 @@
0xF0079, 0xF007A, 0xF0504, 0xF058C, 0xF0599, 0xF05AD, 0xF0928, 0xF092D, 0xF1504

View File

@ -62,6 +62,29 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x66, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x18, 0x0,
0x0, 0x18, 0x0, 0x0, 0x0, 0x0,
/* U+F05AD "󰖭" */
0xe0, 0x0, 0xfe, 0x0, 0x3d, 0xe0, 0xf, 0x1e,
0x3, 0xc1, 0xe0, 0xf0, 0x1e, 0x3c, 0x1, 0xef,
0x0, 0x1f, 0xc0, 0x1, 0xf0, 0x0, 0x3e, 0x0,
0xf, 0xe0, 0x3, 0xde, 0x0, 0xf1, 0xe0, 0x3c,
0x1e, 0xf, 0x1, 0xe3, 0xc0, 0x1e, 0xf0, 0x1,
0xfc, 0x0, 0x1c,
/* U+F0928 "󰤨" */
0x0, 0x1f, 0xf8, 0x0, 0x1, 0xff, 0xff, 0x80,
0x7, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xf8,
0x3f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xfe,
0x7f, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfc,
0x1f, 0xff, 0xff, 0xf8, 0xf, 0xff, 0xff, 0xf0,
0x7, 0xff, 0xff, 0xe0, 0x7, 0xff, 0xff, 0xe0,
0x3, 0xff, 0xff, 0xc0, 0x1, 0xff, 0xff, 0x80,
0x0, 0xff, 0xff, 0x0, 0x0, 0x7f, 0xfe, 0x0,
0x0, 0x7f, 0xfe, 0x0, 0x0, 0x3f, 0xfc, 0x0,
0x0, 0x1f, 0xf8, 0x0, 0x0, 0xf, 0xf0, 0x0,
0x0, 0x7, 0xe0, 0x0, 0x0, 0x7, 0xe0, 0x0,
0x0, 0x3, 0xc0, 0x0, 0x0, 0x1, 0x80, 0x0,
0x0, 0x0, 0x0, 0x0,
/* U+F092D "󰤭" */
0x8, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0,
0xe, 0x1f, 0xf8, 0x0, 0x7, 0x1f, 0xff, 0x80,
@ -90,7 +113,9 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 52, .adv_w = 512, .box_w = 24, .box_h = 23, .ofs_x = 4, .ofs_y = 1},
{.bitmap_index = 121, .adv_w = 512, .box_w = 16, .box_h = 23, .ofs_x = 8, .ofs_y = 1},
{.bitmap_index = 167, .adv_w = 512, .box_w = 24, .box_h = 26, .ofs_x = 4, .ofs_y = -1},
{.bitmap_index = 245, .adv_w = 512, .box_w = 32, .box_h = 27, .ofs_x = 0, .ofs_y = -1}
{.bitmap_index = 245, .adv_w = 512, .box_w = 19, .box_h = 18, .ofs_x = 6, .ofs_y = 3},
{.bitmap_index = 288, .adv_w = 512, .box_w = 32, .box_h = 25, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 388, .adv_w = 512, .box_w = 32, .box_h = 27, .ofs_x = 0, .ofs_y = -1}
};
/*---------------------
@ -98,7 +123,7 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
*--------------------*/
static const uint16_t unicode_list_0[] = {
0x0, 0x48a, 0x512, 0x51f, 0x8b3
0x0, 0x48a, 0x512, 0x51f, 0x533, 0x8ae, 0x8b3
};
/*Collect the unicode lists and glyph_id offsets*/
@ -106,7 +131,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 983162, .range_length = 2228, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 5, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 7, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
#include "esp_log.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
esp_err_t helper_wifi_init(void) {
// Check stored credentials from NVS,
// if no credentials stored or button pressed, force initiate easy connect
return ESP_OK;
}
void helper_wifi_task(void *pvParameters) {
//
}

32
main/impl/impl_dht.c Normal file
View File

@ -0,0 +1,32 @@
#include "esp_system.h"
#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "impl_dht.h"
#define IMPL_DHT_I2C_NUM 0
#define IMPL_DHT_I2C_TIMEOUT 1000
htu21d_ret_t impl_dht_i2c_read(void *handle, uint8_t* data, uint8_t len) {
if(i2c_master_read_from_device(IMPL_DHT_I2C_NUM, 0x40, data, len, pdMS_TO_TICKS(IMPL_DHT_I2C_TIMEOUT)) != ESP_OK) {
return HTU21D_ERROR;
}
return HTU21D_OK;
}
htu21d_ret_t impl_dht_i2c_write(void *handle, uint8_t* data, uint8_t len) {
if(i2c_master_write_to_device(IMPL_DHT_I2C_NUM, 0x40, data, len, pdMS_TO_TICKS(IMPL_DHT_I2C_TIMEOUT)) != ESP_OK) {
return HTU21D_ERROR;
}
return HTU21D_OK;
}
htu21d_ret_t impl_dht_delay(void *handle, uint32_t msec) {
vTaskDelay(pdMS_TO_TICKS(msec));
return HTU21D_OK;
}

10
main/impl/impl_dht.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef IMPL_DHT_H
#define IMPL_DHT_H
#include "htu21d.h"
htu21d_ret_t impl_dht_i2c_read(void *handle, uint8_t* data, uint8_t len);
htu21d_ret_t impl_dht_i2c_write(void *handle, uint8_t* data, uint8_t len);
htu21d_ret_t impl_dht_delay(void *handle, uint32_t msec);
#endif

View File

@ -8,7 +8,7 @@
#define EPD_GPIO_RES GPIO_NUM_10
#define EPD_GPIO_DC GPIO_NUM_12
#define EPD_SPI_HOST SPI2_HOST
#define EPD_SPI_HOST SPI3_HOST
#define EPD_SPI_SPEED (16 * 1000 * 1000) /* 16MHz */
#define EPD_SPI_MAX_XFER_SIZE (1024)

View File

@ -3,6 +3,7 @@
#include "esp_log.h"
#include "impl_btn.h"
#include "impl_epd.h"
#include "task_config.h"
#define LOG_TAG "IMPL_LVGL"
@ -10,7 +11,7 @@
#define EPD_DISPLAY_FRAME_SIZE (EPD_DISPLAY_PIXEL_COUNT * 2 / 8)
#define EPD_DISPLAY_GS 0
#define EPD_DISPLAY_MAX_PARTIAL 12
#define EPD_DISPLAY_MAX_PARTIAL 64 /* !! Must be even !! */
static impl_epd_handle_t s_epd_impl;
@ -38,7 +39,7 @@ static uint8_t s_epd_partial_counter = 0;
static uint32_t s_last_key = 0;
static lv_disp_draw_buf_t s_disp_buf;
static lv_color_t s_disp_store[EPD_DISPLAY_FRAME_SIZE];
static lv_color_t *s_disp_store;
static lv_disp_drv_t s_disp_drv;
static lv_indev_drv_t s_indev_drv;
@ -144,9 +145,9 @@ static void impl_lvgl_button_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
}
else {
data->key = s_last_key;
data->state = LV_INDEV_STATE_RELEASED;
s_last_key = 0U;
data->key = s_last_key;
data->state = LV_INDEV_STATE_RELEASED;
s_last_key = 0U;
}
}
@ -194,6 +195,9 @@ esp_err_t impl_lvgl_init(void) {
lv_log_register_print_cb(impl_lvgl_log_cb);
s_disp_store = malloc(EPD_DISPLAY_FRAME_SIZE);
if (s_disp_store == NULL) return ESP_FAIL;
lv_disp_draw_buf_init(&s_disp_buf, s_disp_store, NULL, EPD_DISPLAY_PIXEL_COUNT);
lv_disp_drv_init(&s_disp_drv);
@ -214,11 +218,11 @@ esp_err_t impl_lvgl_init(void) {
impl_btn_init();
lv_indev_drv_init(&s_indev_drv);
s_indev_drv.type = LV_INDEV_TYPE_KEYPAD;
s_indev_drv.type = LV_INDEV_TYPE_KEYPAD;
s_indev_drv.read_cb = impl_lvgl_button_read;
lv_indev_t *indev = lv_indev_drv_register(&s_indev_drv);
if(indev == NULL) {
lv_indev_t *indev = lv_indev_drv_register(&s_indev_drv);
if (indev == NULL) {
ESP_LOGE(LOG_TAG, "LVGL indev register failed.");
return ESP_FAIL;
}
@ -230,7 +234,7 @@ esp_err_t impl_lvgl_init(void) {
lv_theme_t *default_theme = lv_theme_mono_init(disp, false, &lv_font_unscii_16);
lv_disp_set_theme(disp, default_theme);
s_lv_semphr_handle = xSemaphoreCreateBinary();
s_lv_semphr_handle = xSemaphoreCreateMutex();
if (s_lv_semphr_handle == NULL) {
ESP_LOGE(LOG_TAG, "LVGL semaphore creation failed.");
return ESP_FAIL;
@ -238,11 +242,13 @@ esp_err_t impl_lvgl_init(void) {
xSemaphoreGive(s_lv_semphr_handle);
if (xTaskCreate(impl_lvgl_tick_task, "LV_TICK", 4096, NULL, 5, &s_lv_tick_handle) != pdPASS) {
if (xTaskCreate(impl_lvgl_tick_task, "IMPL_LVT", TASK_IMPL_LVT_STACK_SIZE, NULL, TASK_IMPL_LVT_PRIORITY,
&s_lv_tick_handle) != pdPASS) {
ESP_LOGE(LOG_TAG, "LVGL tick task creation failed.");
return ESP_FAIL;
}
if (xTaskCreate(impl_lvgl_timer_task, "LV_TMR", 4096, NULL, 4, &s_lv_task_handle) != pdPASS) {
if (xTaskCreate(impl_lvgl_timer_task, "IMPL_LVE", TASK_IMPL_LVE_STACK_SIZE, NULL, TASK_IMPL_LVE_PRIORITY,
&s_lv_task_handle) != pdPASS) {
ESP_LOGE(LOG_TAG, "LVGL timer handler task creation failed.");
return ESP_FAIL;
}

18
main/impl/impl_sdfs.c Normal file
View File

@ -0,0 +1,18 @@
#include "impl_sdfs.h"
#include "lvgl.h"
lv_fs_res_t impl_sdfs_open(lv_fs_drv_t *drv, void *file_p, const char *path, lv_fs_mode_t mode) {
//
return LV_FS_RES_OK;
}
lv_fs_res_t impl_sdfs_close(lv_fs_drv_t *drv, void *file_p) {
//
return LV_FS_RES_OK;
}
esp_err_t impl_sdfs_init(void) {
//
return ESP_OK;
}

6
main/impl/impl_sdfs.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef IMPL_SDFS_H
#define IMPL_SDFS_H
#include "esp_system.h"
#endif

View File

@ -0,0 +1,19 @@
#ifndef TASK_CONFIG_H
#define TASK_CONFIG_H
#define TASK_IMPL_LVE_STACK_SIZE 4096
#define TASK_IMPL_LVE_PRIORITY 4
#define TASK_IMPL_LVT_STACK_SIZE 4096
#define TASK_IMPL_LVT_PRIORITY 5
#define TASK_SVC_CLK_STACK_SIZE 2048
#define TASK_SVC_CLK_PRIORITY 6
#define TASK_SVC_DHT_STACK_SIZE 2048
#define TASK_SVC_DHT_PRIORITY 6
#define TASK_SVC_WIFI_STACK_SIZE 2048
#define TASK_SVC_WIFI_PRIORITY 6
#endif

View File

View File

View File

@ -3,16 +3,42 @@
#include <sys/time.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "if_wifi.h"
#include "impl_lvgl.h"
#define LOG_TAG "IF_STANDBY"
#define LOG_TAG "IF_STANDBY"
static TaskHandle_t s_if_standby_handle;
#define IF_EVENT_WIFI_BUTTON 0
#define IF_EVENT_BAT_BUTTON 1
typedef struct {
if_standby_component_t component;
char payload[IF_STANDBY_PAYLOAD_LENGTH];
} if_standby_request_t;
static TaskHandle_t s_if_standby_handle;
static QueueHandle_t s_standby_screen_queue;
lv_obj_t *g_standby_screen;
void if_standby_event_handler(lv_event_t *ev) {
int button_event = (int)ev->user_data;
switch (button_event) {
case IF_EVENT_WIFI_BUTTON:
if_wifi_init(g_standby_screen);
break;
case IF_EVENT_BAT_BUTTON:
break;
default:
break;
}
}
/**
* @brief Standby screen task, handles GUI element updates through a queue.
*
* @param pvParameters
*/
void if_standby_task(void *pvParameters) {
impl_lvgl_lock();
@ -38,6 +64,9 @@ void if_standby_task(void *pvParameters) {
lv_obj_t *date_obj = lv_obj_create(top_container);
lv_obj_t *battery_obj = lv_btn_create(top_container);
lv_obj_add_event_cb(network_status_obj, if_standby_event_handler, LV_EVENT_CLICKED, (void *)IF_EVENT_WIFI_BUTTON);
lv_obj_add_event_cb(battery_obj, if_standby_event_handler, LV_EVENT_CLICKED, (void *)IF_EVENT_BAT_BUTTON);
lv_obj_set_size(network_status_obj, 50, 50);
lv_obj_set_size(battery_obj, 50, 50);
@ -105,9 +134,9 @@ void if_standby_task(void *pvParameters) {
lv_label_set_text(date_label, "1970/01/01");
lv_label_set_text(time_label, "23:59");
lv_label_set_text(temp_icon_label, "\U000F0599");
lv_label_set_text(temp_label, "30.00C");
lv_label_set_text(temp_label, "30.00");
lv_label_set_text(humid_icon_label, "\U000F058C");
lv_label_set_text(humid_label, "100.00%");
lv_label_set_text(humid_label, "100.00");
lv_obj_set_align(network_status_label, LV_ALIGN_CENTER);
lv_obj_set_align(battery_label, LV_ALIGN_CENTER);
@ -123,9 +152,49 @@ void if_standby_task(void *pvParameters) {
ESP_LOGI(LOG_TAG, "Standby screen created.");
if_standby_request_t update_request;
for (;;) {
// Wait for notifications.
vTaskSuspend(NULL);
// Read from queue for UI element updates.
xQueueReceive(s_standby_screen_queue, &update_request, portMAX_DELAY);
impl_lvgl_lock();
switch (update_request.component) {
case IF_STANDBY_COMPONENT_DATE:
lv_label_set_text(date_label, update_request.payload);
lv_obj_set_align(date_label, LV_ALIGN_CENTER);
break;
case IF_STANDBY_COMPONENT_HUMID:
lv_label_set_text(humid_label, update_request.payload);
lv_obj_align(humid_label, LV_ALIGN_CENTER, 0, 16);
break;
case IF_STANDBY_COMPONENT_HUMID_ICON:
lv_label_set_text(humid_icon_label, update_request.payload);
lv_obj_align(humid_icon_label, LV_ALIGN_CENTER, 0, -16);
break;
case IF_STANDBY_COMPONENT_TEMP:
lv_label_set_text(temp_label, update_request.payload);
lv_obj_align(temp_label, LV_ALIGN_CENTER, 0, 16);
break;
case IF_STANDBY_COMPONENT_TEMP_ICON:
lv_label_set_text(temp_icon_label, update_request.payload);
lv_obj_align(temp_icon_label, LV_ALIGN_CENTER, 0, -16);
break;
case IF_STANDBY_COMPONENT_TIME:
lv_label_set_text(time_label, update_request.payload);
lv_obj_set_align(time_label, LV_ALIGN_CENTER);
break;
case IF_STANDBY_COMPONENT_WEEKDAY:
lv_label_set_text(weekday_label, update_request.payload);
lv_obj_set_align(weekday_label, LV_ALIGN_CENTER);
break;
case IF_STANDBY_COMPONENT_WIFI:
lv_label_set_text(network_status_label, update_request.payload);
lv_obj_set_align(network_status_label, LV_ALIGN_CENTER);
break;
default:
break;
}
impl_lvgl_unlock();
}
}
@ -135,9 +204,40 @@ esp_err_t if_standby_init(void) {
lv_disp_load_scr(g_standby_screen);
impl_lvgl_unlock();
if (xTaskCreate(if_standby_task, "STBY_TASK", 4096, NULL, 6, &s_if_standby_handle) != pdPASS) {
s_standby_screen_queue = xQueueCreate(8, sizeof(if_standby_request_t));
if (s_standby_screen_queue == NULL) {
return ESP_FAIL;
}
if (xTaskCreate(if_standby_task, "IF_STBY", 4096, NULL, 6, &s_if_standby_handle) != pdPASS) {
return ESP_FAIL;
}
return ESP_OK;
}
}
/**
* @brief Update component content on standby screen.
*
* @param component if_standby_component_t
* @param fmt printf-style format
* @param ...
* @return esp_err_t
*/
esp_err_t if_standby_component_update(if_standby_component_t component, const char *fmt, ...) {
if_standby_request_t req;
va_list ap;
req.component = component;
va_start(ap, fmt);
vsnprintf(req.payload, IF_STANDBY_PAYLOAD_LENGTH, fmt, ap);
va_end(ap);
// Queue update command.
if (xQueueSendToBack(s_standby_screen_queue, &req, 0) != pdPASS) {
return ESP_FAIL;
}
return ESP_OK;
}

View File

@ -2,6 +2,28 @@
#define IF_STANDBY_H
#include "esp_system.h"
esp_err_t if_standby_init(void);
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "lvgl.h"
#endif
#define IF_STANDBY_PAYLOAD_LENGTH 16
typedef enum {
IF_STANDBY_COMPONENT_BAT,
IF_STANDBY_COMPONENT_DATE,
IF_STANDBY_COMPONENT_HUMID,
IF_STANDBY_COMPONENT_HUMID_ICON,
IF_STANDBY_COMPONENT_TIME,
IF_STANDBY_COMPONENT_TEMP,
IF_STANDBY_COMPONENT_TEMP_ICON,
IF_STANDBY_COMPONENT_WEEKDAY,
IF_STANDBY_COMPONENT_WIFI,
} if_standby_component_t;
extern lv_obj_t *g_standby_screen;
esp_err_t if_standby_init(void);
esp_err_t if_standby_component_update(if_standby_component_t component, const char *fmt, ...);
#endif

88
main/interface/if_wifi.c Normal file
View File

@ -0,0 +1,88 @@
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "impl_lvgl.h"
#define IF_EVENT_QUEUE_LEN 2
#define IF_EVENT_QUEUE_SIZE() (sizeof(int))
#define IF_EVENT_QUEUE_TIMEOUT 100
#define IF_EVENT_EXIT_BUTTON 0
static TaskHandle_t s_if_wifi_task_handle;
static QueueHandle_t s_if_wifi_event_queue;
static void if_wifi_event_handler(lv_event_t *ev) {
int button_event = (int)ev->user_data;
xQueueSend(s_if_wifi_event_queue, &button_event, pdMS_TO_TICKS(100));
}
void if_wifi_task(void *pvParameters) {
lv_obj_t *parent = pvParameters;
impl_lvgl_lock();
lv_obj_t *wifi_main = lv_obj_create(parent);
lv_obj_set_size(wifi_main, 360, 280);
lv_obj_set_align(wifi_main, LV_ALIGN_CENTER);
lv_obj_t *exit_obj = lv_btn_create(wifi_main);
lv_obj_set_size(exit_obj, 36, 36);
lv_obj_align(exit_obj, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_add_event_cb(exit_obj, if_wifi_event_handler, LV_EVENT_CLICKED, (void *)IF_EVENT_EXIT_BUTTON);
lv_obj_t *exit_label = lv_label_create(exit_obj);
lv_obj_set_style_text_font(exit_label, &material_webfont_32, 0);
lv_label_set_text(exit_label, "\U000F05AD");
lv_obj_set_align(exit_label, LV_ALIGN_CENTER);
impl_lvgl_unlock();
int button_event = 0;
uint8_t terminate_flag = 0;
for (;;) {
if (xQueueReceive(s_if_wifi_event_queue, &button_event, portMAX_DELAY) == pdPASS) {
switch (button_event) {
case IF_EVENT_EXIT_BUTTON:
terminate_flag = 1;
break;
default:
break;
}
}
if (terminate_flag) {
break;
}
}
// When we are done, delete UI elements and task
lv_obj_del_async(wifi_main);
vQueueDelete(s_if_wifi_event_queue);
s_if_wifi_event_queue = NULL;
vTaskDelete(NULL);
}
/**
* @brief Initialize WiFi status/configuration component
*
* @param parent_obj
* @return esp_err_t
*/
esp_err_t if_wifi_init(lv_obj_t *parent_obj) {
s_if_wifi_event_queue = xQueueCreate(IF_EVENT_QUEUE_LEN, IF_EVENT_QUEUE_SIZE());
if (xTaskCreate(if_wifi_task, "STBY_TASK", 4096, parent_obj, 6, &s_if_wifi_task_handle) != pdPASS) {
return ESP_FAIL;
}
return ESP_OK;
}

6
main/interface/if_wifi.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef IF_WIFI_H
#define IF_WIFI_H
esp_err_t if_wifi_init(lv_obj_t *parent_obj);
#endif

44
main/lib/htu21d/htu21d.c Normal file
View File

@ -0,0 +1,44 @@
#include "htu21d.h"
#define HTU21D_REG_MEASURE_TEMP_HOLD 0xE3
#define HTU21D_REG_MEASURE_TEMP_NOHOLD 0xF3
#define HTU21D_REG_MEASURE_HUMID_HOLD 0xE5
#define HTU21D_REG_MEASURE_HUMID_NOHOLD 0xF5
#define HTU21D_REG_READ_USER_REG 0xE7
#define HTU21D_REG_WRITE_USER_REG 0xE6
#define HTU21D_REG_SOFT_RESET 0xFE
#define HTU21D_ERROR_CHECK(x) \
if (x != HTU21D_OK) return HTU21D_ERROR
htu21d_ret_t htu21d_init(htu21d_t *htu) {
uint8_t tx_buf = HTU21D_REG_SOFT_RESET;
HTU21D_ERROR_CHECK(htu->cb.write_cb(htu->user_data, &tx_buf, 0x01));
return HTU21D_OK;
}
htu21d_ret_t htu21d_measure(htu21d_t *htu, htu21d_result_t *result) {
uint8_t tx_buf = HTU21D_REG_MEASURE_TEMP_NOHOLD;
uint8_t rx_data[3];
HTU21D_ERROR_CHECK(htu->cb.write_cb(htu->user_data, &tx_buf, 0x01));
HTU21D_ERROR_CHECK(htu->cb.delay_cb(htu->user_data, 50));
HTU21D_ERROR_CHECK(htu->cb.read_cb(htu->user_data, rx_data, 0x03));
uint16_t raw_data = (rx_data[0] << 8U) | (rx_data[1] & 0xFC);
result->temperature = (float)raw_data / 65536.0 * 175.72 - 46.85;
tx_buf = HTU21D_REG_MEASURE_HUMID_NOHOLD;
HTU21D_ERROR_CHECK(htu->cb.write_cb(htu->user_data, &tx_buf, 0x01));
HTU21D_ERROR_CHECK(htu->cb.delay_cb(htu->user_data, 50));
HTU21D_ERROR_CHECK(htu->cb.read_cb(htu->user_data, rx_data, 0x03));
raw_data = (rx_data[0] << 8U) | (rx_data[1] & 0xFC);
result->humidity = (float)raw_data / 65536.0 * 125.0 - 6.0;
return HTU21D_OK;
}

30
main/lib/htu21d/htu21d.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef HTU21D_H
#define HTU21D_H
#include <stdint.h>
typedef enum {
HTU21D_OK,
HTU21D_ERROR
} htu21d_ret_t;
typedef struct {
htu21d_ret_t (*write_cb)(void * handle, uint8_t* data, uint8_t len);
htu21d_ret_t (*read_cb)(void *handle, uint8_t* data, uint8_t len);
htu21d_ret_t (*delay_cb)(void *handle, uint32_t msec);
} htu21d_cb_t;
typedef struct {
float temperature;
float humidity;
} htu21d_result_t;
typedef struct {
htu21d_cb_t cb;
void *user_data;
} htu21d_t;
htu21d_ret_t htu21d_init(htu21d_t *htu);
htu21d_ret_t htu21d_measure(htu21d_t *htu, htu21d_result_t *result);
#endif

View File

@ -0,0 +1,42 @@
#include "ltc2941.h"
#define LTC2941_REG_STATUS 0x00
#define LTC2941_REG_CONTROL 0x01
#define LTC2941_REG_CHARGE_MSB 0x02
#define LTC2941_REG_CHARGE_LSB 0x03
ltc2941_ret_t ltc2941_init(ltc2941_t *bat) {
uint8_t control_reg =
((bat->config.alert_mode & 0x03U) << 0x01U) |
((bat->config.prescaler & 0x07U) << 0x03U) |
((bat->config.alert_level & 0x03U) << 0x06U);
bat->cb.write_register_cb(bat->user_data, LTC2941_REG_CONTROL, control_reg);
return LTC2941_OK;
}
ltc2941_ret_t ltc2941_read_status(ltc2941_t *bat, uint8_t *status) {
return bat->cb.read_register_cb(bat->user_data, LTC2941_REG_STATUS, status);
}
ltc2941_ret_t ltc2941_read_charge(ltc2941_t *bat, uint16_t *charge) {
uint8_t reg = 0x00;
bat->cb.read_register_cb(bat->user_data, LTC2941_REG_CHARGE_MSB, &reg);
*charge = reg << 0x08U;
bat->cb.read_register_cb(bat->user_data, LTC2941_REG_CHARGE_LSB, &reg);
*charge |= reg;
return LTC2941_OK;
}
ltc2941_ret_t ltc2941_write_charge(ltc2941_t *bat, uint16_t charge) {
uint8_t reg = (charge >> 0x08U); // MSB
bat->cb.write_register_cb(bat->user_data, LTC2941_REG_CHARGE_MSB, reg);
reg = (charge & 0xFFU); // LSB
bat->cb.write_register_cb(bat->user_data, LTC2941_REG_CHARGE_LSB, reg);
return LTC2941_OK;
}

View File

@ -0,0 +1,64 @@
#ifndef __LTC2941_BATTERY_H
#define __LTC2941_BATTERY_H
#include <stdint.h>
typedef enum {
LTC2941_OK,
LTC2941_ERROR
} ltc2941_ret_t;
typedef enum {
LTC2941_ALERT_3_0V = 0x03,
LTC2941_ALERT_2_9V = 0x02,
LTC2941_ALERT_2_8V = 0x01,
LTC2941_ALERT_OFF = 0x00
} ltc2941_battery_alert_t;
typedef enum {
LTC2941_PRE_1 = 0x00,
LTC2941_PRE_2 = 0x01,
LTC2941_PRE_4 = 0x02,
LTC2941_PRE_8 = 0x03,
LTC2941_PRE_16 = 0x04,
LTC2941_PRE_32 = 0x05,
LTC2941_PRE_64 = 0x06,
LTC2941_PRE_128 = 0x07
} ltc2941_prescale_t;
typedef enum {
LTC2941_ALERT_DISABLED = 0x00,
LTC2941_ALERT_CC = 0x01,
LTC2941_ALERT_AL = 0x02
} ltc2941_alert_mode_t;
typedef struct {
ltc2941_ret_t (*write_register_cb)(void * handle, uint8_t reg, uint8_t value);
ltc2941_ret_t (*read_register_cb)(void *handle, uint8_t reg, uint8_t *value);
} ltc2941_cb_t;
typedef struct {
ltc2941_prescale_t prescaler;
ltc2941_alert_mode_t alert_mode;
ltc2941_battery_alert_t alert_level;
} ltc2941_config_t;
typedef struct {
ltc2941_cb_t cb;
ltc2941_config_t config;
void *user_data;
} ltc2941_t;
#define LTC2941_STATUS_CHIPID (1 << 7U)
#define LTC2941_STATUS_OVERFLOW (1 << 5U)
#define LTC2941_STATUS_ALRT_HIGH (1 << 3U)
#define LTC2941_STATUS_ALRT_LOW (1 << 2U)
#define LTC2941_STATUS_ALRT_VBAT (1 << 1U)
#define LTC2941_STATUS_ALRT_UVLO 1U
ltc2941_ret_t ltc2941_init(ltc2941_t *bat);
ltc2941_ret_t ltc2941_read_status(ltc2941_t *bat, uint8_t *status);
ltc2941_ret_t ltc2941_read_charge(ltc2941_t *bat, uint16_t *charge);
ltc2941_ret_t ltc2941_write_charge(ltc2941_t *bat, uint16_t charge);
#endif

View File

@ -78,10 +78,10 @@
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
#define LV_DISP_DEF_REFR_PERIOD 100 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
#define LV_INDEV_DEF_READ_PERIOD 50 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
@ -181,7 +181,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
*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_INFO
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
@ -343,7 +343,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
LV_FONT_DECLARE(material_webfont_32)
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_unscii_16
#define LV_FONT_DEFAULT &bebas_neue_32
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.

View File

@ -1,6 +1,8 @@
#include <stdio.h>
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_sntp.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "nvs_flash.h"
@ -9,8 +11,16 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//
#include "impl_lvgl.h"
#include "if_standby.h"
#include "impl_lvgl.h"
#include "service_clock.h"
#include "service_dht.h"
#include "service_wifi.h"
#define APP_I2C_MASTER_NUM 0
#define APP_I2C_SDA_NUM GPIO_NUM_39
#define APP_I2C_SCL_NUM GPIO_NUM_40
#define APP_I2C_HZ 400000
static esp_err_t app_init_nvs(void) {
esp_err_t ret = nvs_flash_init();
@ -22,18 +32,48 @@ static esp_err_t app_init_nvs(void) {
return ret;
}
static esp_err_t app_init_gpio(void) { return gpio_install_isr_service(0); }
static esp_err_t app_init_gpio(void) {
//
return gpio_install_isr_service(0);
}
esp_err_t impl_lvgl_init(void);
static esp_err_t app_init_i2c(void) {
i2c_config_t cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = APP_I2C_SDA_NUM,
.scl_io_num = APP_I2C_SCL_NUM,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = APP_I2C_HZ,
};
i2c_param_config(APP_I2C_MASTER_NUM, &cfg);
return i2c_driver_install(APP_I2C_MASTER_NUM, cfg.mode, 0, 0, 0);
}
static esp_err_t app_init_sntp(void) {
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();
return ESP_OK;
}
void app_main(void) {
ESP_ERROR_CHECK(app_init_nvs());
ESP_ERROR_CHECK(app_init_gpio());
ESP_ERROR_CHECK(app_init_i2c());
ESP_ERROR_CHECK(app_init_sntp());
ESP_ERROR_CHECK(impl_lvgl_init());
ESP_ERROR_CHECK(if_standby_init());
ESP_ERROR_CHECK(service_dht_init());
ESP_ERROR_CHECK(service_clock_init());
ESP_ERROR_CHECK(service_wifi_init());
/* Dead loop */
for (;;) {
vTaskSuspend(NULL);

View File

View File

View File

@ -0,0 +1,58 @@
#include "service_clock.h"
#include <time.h>
#include "esp_log.h"
#include "if_standby.h"
#include "task_config.h"
#define LOG_TAG "SERVICE_CLOCK"
static TaskHandle_t s_clock_task_handle;
static const char *s_wday_array[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
void clock_task(void *pvParameters) {
struct tm timeinfo;
time_t now;
uint8_t prev_min = 0xFF;
uint8_t prev_mday = 0xFF;
uint8_t prev_wday = 0xFF;
setenv("TZ", "CST-8", 1);
tzset();
for (;;) {
time(&now);
localtime_r(&now, &timeinfo);
if ((timeinfo.tm_min != prev_min) || (prev_min == 0xFF)) {
if_standby_component_update(IF_STANDBY_COMPONENT_TIME, "%02u:%02u", timeinfo.tm_hour, timeinfo.tm_min);
prev_min = timeinfo.tm_min;
}
if ((timeinfo.tm_mday != prev_mday) || (prev_mday == 0xFF)) {
if_standby_component_update(IF_STANDBY_COMPONENT_DATE, "%04u/%02d/%02d", timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1, timeinfo.tm_mday);
prev_mday = timeinfo.tm_mday;
}
if ((timeinfo.tm_wday != prev_wday) || (prev_wday == 0xFF)) {
if_standby_component_update(IF_STANDBY_COMPONENT_WEEKDAY, s_wday_array[timeinfo.tm_wday]);
prev_wday = timeinfo.tm_wday;
}
// TODO: Set an alarm at 0sec every 1 minute.
vTaskDelay(pdMS_TO_TICKS(200));
}
}
esp_err_t service_clock_init(void) {
if (xTaskCreate(clock_task, "SVC_CLK", TASK_SVC_CLK_STACK_SIZE, NULL, TASK_SVC_CLK_PRIORITY,
&s_clock_task_handle) != pdTRUE) {
return ESP_FAIL;
}
return ESP_OK;
}

View File

@ -0,0 +1,8 @@
#ifndef SERVICE_CLOCK_H
#define SERVICE_CLOCK_H
#include "esp_system.h"
esp_err_t service_clock_init(void);
#endif

View File

@ -0,0 +1,46 @@
#include "service_dht.h"
#include "esp_log.h"
#include "if_standby.h"
#include "impl_dht.h"
#include "task_config.h"
#define LOG_TAG "SERVICE_DHT"
static htu21d_t s_htu = {
.cb =
{
.read_cb = impl_dht_i2c_read,
.write_cb = impl_dht_i2c_write,
.delay_cb = impl_dht_delay,
},
.user_data = NULL,
};
static TaskHandle_t s_dht_task_handle;
void dht_task(void *pvParameters) {
htu21d_result_t result;
for (;;) {
while (htu21d_measure(&s_htu, &result) != HTU21D_OK) {
vTaskDelay(pdMS_TO_TICKS(100));
}
if_standby_component_update(IF_STANDBY_COMPONENT_TEMP, "%.2f", result.temperature);
if_standby_component_update(IF_STANDBY_COMPONENT_HUMID, "%.2f", result.humidity);
vTaskDelay(pdMS_TO_TICKS(30000));
}
}
esp_err_t service_dht_init(void) {
htu21d_init(&s_htu);
if (xTaskCreate(dht_task, "SVC_DHT", TASK_SVC_DHT_STACK_SIZE, NULL, TASK_SVC_DHT_PRIORITY, &s_dht_task_handle) !=
pdTRUE) {
return ESP_FAIL;
}
return ESP_OK;
}

View File

@ -0,0 +1,8 @@
#ifndef SERVICE_DHT_H
#define SERVICE_DHT_H
#include "esp_system.h"
esp_err_t service_dht_init(void);
#endif

156
main/service/service_wifi.c Normal file
View File

@ -0,0 +1,156 @@
#include "esp_event.h"
#include "esp_log.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "if_standby.h"
#include "if_wifi.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "task_config.h"
#define LOG_TAG "SVC_WIFI"
#define WIFI_SERVICE_SSID CONFIG_ESP_WIFI_SSID
#define WIFI_SERVICE_PASS CONFIG_ESP_WIFI_PASSWORD
#define WIFI_SERVICE_MAXIMUM_RETRY CONFIG_ESP_WIFI_MAXIMUM_RETRY
static TaskHandle_t s_service_wifi_task_handle;
static QueueHandle_t s_service_wifi_event_queue;
static uint8_t s_retry_count = 0;
typedef enum {
SERVICE_WIFI_EV_STATE_CHANGE,
} service_wifi_event_t;
typedef enum {
SERVICE_WIFI_STATE_DISABLED,
SERVICE_WIFI_STATE_DPP,
SERVICE_WIFI_STATE_CONNECTING,
SERVICE_WIFI_STATE_CONNECTED,
SERVICE_WIFI_STATE_IP_READY,
} service_wifi_state_t;
typedef struct {
service_wifi_event_t ev;
void *payload;
} service_wifi_request_t;
static esp_err_t service_wifi_update_state(service_wifi_state_t new_state) {
service_wifi_request_t req = {
.ev = SERVICE_WIFI_EV_STATE_CHANGE,
.payload = (void *)new_state,
};
if(xQueueSend(s_service_wifi_event_queue, &req, 0) != pdPASS) {
return ESP_FAIL;
}
return ESP_OK;
}
static void service_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_count < WIFI_SERVICE_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_count++;
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
s_retry_count = 0;
service_wifi_update_state(SERVICE_WIFI_STATE_IP_READY);
}
}
void service_wifi_task(void *pvParameters) {
char wifi_ssid[32];
char wifi_pass[32];
size_t wifi_length = 32;
uint8_t wifi_config_valid = 0;
nvs_handle_t n_handle;
if (nvs_open("storage", NVS_READWRITE, &n_handle) != ESP_OK) {
ESP_LOGE(LOG_TAG, "Failed to create NVS handle.");
vTaskSuspend(NULL);
}
// TODO: check WiFi storage length.
if (nvs_get_str(n_handle, "u_wifi_ssid", wifi_ssid, &wifi_length) == ESP_OK) {
if (nvs_get_str(n_handle, "u_wifi_pass", wifi_pass, &wifi_length) == ESP_OK) {
wifi_config_valid = 1;
}
}
if (!wifi_config_valid) {
if_standby_component_update(IF_STANDBY_COMPONENT_WIFI, "\U000F092D");
}
wifi_config_t wifi_config = {
.sta =
{
.ssid = WIFI_SERVICE_SSID,
.password = WIFI_SERVICE_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_WPA3_PSK,
.pmf_cfg =
{
.capable = true,
.required = false,
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
service_wifi_request_t request;
for (;;) {
if (xQueueReceive(s_service_wifi_event_queue, &request, portMAX_DELAY) == pdPASS) {
if (request.ev == SERVICE_WIFI_EV_STATE_CHANGE) {
service_wifi_state_t state = (service_wifi_state_t)request.payload;
switch (state) {
case SERVICE_WIFI_STATE_IP_READY:
if_standby_component_update(IF_STANDBY_COMPONENT_WIFI, "\U000F0928");
break;
default:
break;
}
}
}
}
}
esp_err_t service_wifi_init(void) {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, service_wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, service_wifi_event_handler, NULL));
s_service_wifi_event_queue = xQueueCreate(2, sizeof(service_wifi_request_t));
if (s_service_wifi_event_queue == NULL) {
return ESP_FAIL;
}
if (xTaskCreate(service_wifi_task, "SVC_WIFI", TASK_SVC_WIFI_STACK_SIZE, NULL, TASK_SVC_WIFI_PRIORITY,
&s_service_wifi_task_handle) != pdPASS) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t service_wifi_notify() {
return ESP_OK;
}

View File

@ -0,0 +1,15 @@
#ifndef SERVICE_WIFI_H
#define SERVICE_WIFI_H
#include "esp_system.h"
typedef enum {
SERVICE_WIFI_NOTIFY_STANDBY_REFRESH,
SERVICE_WIFI_NOTIFY_SETTINGS_REFRESH,
} service_wifi_notify_t;
esp_err_t service_wifi_init(void);
esp_err_t service_wifi_notify();
#endif

12
partitions.csv Normal file
View File

@ -0,0 +1,12 @@
# This file format is not compatible with CSV,
# as comments are supported, whitespaces are trimmed.
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 2M,
ota_0, app, ota_0, 0x210000, 2M,
ota_1, app, ota_1, 0x410000, 2M,
nvs_keys, data, nvs_keys, 0x610000, 0x1000,
spiffs, data, spiffs, 0x620000, 8M,
1 # This file format is not compatible with CSV,
2 # as comments are supported, whitespaces are trimmed.
3 #
4 # Name, Type, SubType, Offset, Size, Flags
5 nvs, data, nvs, 0x9000, 0x4000,
6 otadata, data, ota, 0xd000, 0x2000,
7 phy_init, data, phy, 0xf000, 0x1000,
8 factory, app, factory, 0x10000, 2M,
9 ota_0, app, ota_0, 0x210000, 2M,
10 ota_1, app, ota_1, 0x410000, 2M,
11 nvs_keys, data, nvs_keys, 0x610000, 0x1000,
12 spiffs, data, spiffs, 0x620000, 8M,