Files
ate0005/main/ate0005.c
T
2026-11-04 08:34:20 +03:00

598 lines
25 KiB
C

#include "ate0005.h"
typedef struct
{
uint32_t a_inclinometer_task_min_stack_size; /*!< Minimum free stack size of the A inclinometer task. */
uint32_t b_inclinometer_task_min_stack_size; /*!< Minimum free stack size of the B inclinometer task. */
uint32_t a_tachometer_task_min_stack_size; /*!< Minimum free stack size of the A tachometer task. */
uint32_t b_tachometer_task_min_stack_size; /*!< Minimum free stack size of the B tachometer task. */
} task_stats_t;
static httpd_handle_t _http_server_handle = NULL;
static zh_pcf8574_handle_t *_lcd_handle = NULL;
static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL;
static zh_encoder_handle_t *_mode_encoder_handle = NULL;
static zh_inclinometer_handle_t *_a_inclinometer_handle = NULL;
static zh_inclinometer_handle_t *_b_inclinometer_handle = NULL;
static zh_tachometer_handle_t *_a_tachometer_handle = NULL;
static zh_tachometer_handle_t *_b_tachometer_handle = NULL;
static esp_timer_handle_t _inclinometer_reset_timer_handle = {0};
static SemaphoreHandle_t _lcd_mutex = NULL;
static TaskHandle_t _a_inclinometer_task_handle = NULL;
static TaskHandle_t _b_inclinometer_task_handle = NULL;
static TaskHandle_t _a_tachometer_task_handle = NULL;
static TaskHandle_t _b_tachometer_task_handle = NULL;
static TaskHandle_t _zh_stats_task_handle = NULL;
static void _zh_wifi_init(void);
static void _zh_webserver_init(void);
static void _zh_espnow_init(void);
static void _zh_encoder_init(void);
static void _zh_a_inclinometer_init(void);
static void _zh_b_inclinometer_init(void);
static void _zh_a_tachometer_init(void);
static void _zh_b_tachometer_init(void);
static void _zh_pcf8574_init(void);
static void _zh_system_load(void);
static void _zh_timer_init(void);
static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
static void _zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg);
static void _zh_a_inclinometer_processing_task(void *pvParameter);
static void _zh_b_inclinometer_processing_task(void *pvParameter);
static void _zh_a_tachometer_processing_task(void *pvParameter);
static void _zh_b_tachometer_processing_task(void *pvParameter);
static void _zh_stats_processing_task(void *pvParameter);
volatile static task_stats_t _task_stats = {A_INCLINOMETER_TASK_STACK_SIZE, B_INCLINOMETER_TASK_STACK_SIZE, A_TACHOMETER_TASK_STACK_SIZE, B_TACHOMETER_TASK_STACK_SIZE};
static const char *_main_menu[] = {MAIN_MENU_ANGLE_ANGLE_NAME, MAIN_MENU_TACHO_TACHO_NAME, MAIN_MENU_ANGLE_TACHO_NAME, MAIN_MENU_REMOTE_NAME};
volatile static bool _is_menu_mode = false;
volatile static bool _is_reset_mode = false;
volatile static bool _is_reset_occurred = false;
volatile static uint8_t _current_menu_value = 0;
volatile static uint8_t _wifi_reconnect_retry_num = 0;
volatile static bool _is_wifi_connected = false;
void app_main(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
esp_event_loop_create_default();
_zh_pcf8574_init();
_zh_system_load();
_zh_encoder_init();
_zh_timer_init();
_zh_a_inclinometer_init();
_zh_b_inclinometer_init();
_zh_wifi_init();
_zh_webserver_init();
_zh_espnow_init();
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state = {0};
esp_ota_get_state_partition(running, &ota_state);
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
vTaskDelay(60000 / portTICK_PERIOD_MS);
esp_ota_mark_app_valid_cancel_rollback();
}
}
static void _zh_wifi_init(void)
{
esp_netif_init();
esp_netif_create_default_wifi_ap();
esp_netif_create_default_wifi_sta();
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&wifi_init_config);
wifi_config_t wifi_ap_config = {
.ap.ssid = WIFI_AP_SSID,
.ap.password = WIFI_AP_PASS,
.ap.channel = WIFI_AP_CHANNEL,
.ap.max_connection = MAX_AP_CONNECTION,
.ap.authmode = WIFI_AUTH_WPA2_PSK,
};
wifi_config_t wifi_sta_config = {
.sta.ssid = WIFI_STA_SSID,
.sta.password = WIFI_STA_PASS,
};
esp_wifi_set_mode(WIFI_MODE_APSTA);
esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config);
esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &_zh_wifi_event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &_zh_wifi_event_handler, NULL, NULL);
esp_wifi_start();
}
static void _zh_webserver_init(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_start(&_http_server_handle, &config);
zh_ota_server_init(_http_server_handle, "/");
}
static void _zh_espnow_init(void)
{
// To Do.
}
static void _zh_encoder_init(void)
{
esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &_zh_encoder_event_handler, NULL, NULL);
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.task_priority = ENCODER_TASK_PRIORITY;
config.stack_size = ENCODER_TASK_STACK_SIZE;
config.queue_size = 5;
config.a_gpio_number = MODE_ENCODER_A_GPIO;
config.b_gpio_number = MODE_ENCODER_B_GPIO;
config.s_gpio_number = MODE_ENCODER_S_GPIO;
config.encoder_min_value = 0;
config.encoder_max_value = (sizeof(_main_menu) / sizeof(_main_menu[0])) - 1;
config.encoder_step = 1;
config.encoder_number = MODE_ENCODER_NUMBER;
zh_encoder_init(&config, &_mode_encoder_handle);
zh_encoder_set(&_mode_encoder_handle, 0);
}
static void _zh_a_inclinometer_init(void)
{
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO;
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO;
config.encoder_pulses = 3600;
zh_inclinometer_init(&config, &_a_inclinometer_handle);
xTaskCreatePinnedToCore(&_zh_a_inclinometer_processing_task, "a_inclinometer_processing_task", A_INCLINOMETER_TASK_STACK_SIZE, NULL, A_INCLINOMETER_TASK_PRIORITY,
&_a_inclinometer_task_handle, tskNO_AFFINITY);
}
static void _zh_b_inclinometer_init(void)
{
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO;
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO;
config.encoder_pulses = 3600;
zh_inclinometer_init(&config, &_b_inclinometer_handle);
xTaskCreatePinnedToCore(&_zh_b_inclinometer_processing_task, "b_inclinometer_processing_task", B_INCLINOMETER_TASK_STACK_SIZE, NULL, B_INCLINOMETER_TASK_PRIORITY,
&_b_inclinometer_task_handle, tskNO_AFFINITY);
}
static void _zh_a_tachometer_init(void)
{
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO;
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO;
config.encoder_pulses = 3600;
zh_tachometer_init(&config, &_a_tachometer_handle);
xTaskCreatePinnedToCore(&_zh_a_tachometer_processing_task, "a_tachometer_processing_task", A_TACHOMETER_TASK_STACK_SIZE, NULL, A_TACHOMETER_TASK_PRIORITY,
&_a_tachometer_task_handle, tskNO_AFFINITY);
}
static void _zh_b_tachometer_init(void)
{
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO;
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO;
config.encoder_pulses = 3600;
zh_tachometer_init(&config, &_b_tachometer_handle);
xTaskCreatePinnedToCore(&_zh_b_tachometer_processing_task, "b_tachometer_processing_task", B_TACHOMETER_TASK_STACK_SIZE, NULL, B_TACHOMETER_TASK_PRIORITY,
&_b_tachometer_task_handle, tskNO_AFFINITY);
}
static void _zh_pcf8574_init(void)
{
i2c_master_bus_handle_t _i2c_bus_handle = NULL;
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.scl_io_num = I2C_SCL_GPIO,
.sda_io_num = I2C_SDA_GPIO,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_new_master_bus(&i2c_bus_config, &_i2c_bus_handle);
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
config.task_priority = EXPANDER_TASK_PRIORITY;
config.stack_size = EXPANDER_TASK_STACK_SIZE;
config.i2c_handle = _i2c_bus_handle;
config.i2c_address = LCD_I2C_ADDRESS;
zh_pcf8574_init(&config, &_lcd_handle);
zh_160x_init(&_lcd_handle, &_lcd_160x_handle, ZH_LCD_16X2);
_lcd_mutex = xSemaphoreCreateMutex();
}
static void _zh_system_load(void)
{
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
zh_160x_print_char(&_lcd_160x_handle, "LLC AEROTECH");
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
const esp_app_desc_t *esp_app_desc = esp_app_get_description();
zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->project_name);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
zh_160x_print_char(&_lcd_160x_handle, "V");
zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->version);
vTaskDelay(1000 / portTICK_PERIOD_MS);
zh_160x_lcd_clear(&_lcd_160x_handle);
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
zh_160x_print_char(&_lcd_160x_handle, "SYSTEM LOADING:");
for (uint8_t i = 0; i <= 100; i += 10)
{
zh_160x_print_progress_bar(&_lcd_160x_handle, 1, i);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelay(500 / portTICK_PERIOD_MS);
zh_160x_lcd_clear(&_lcd_160x_handle);
vTaskDelay(500 / portTICK_PERIOD_MS);
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
zh_160x_print_char(&_lcd_160x_handle, "MODE:");
zh_160x_print_char(&_lcd_160x_handle, _main_menu[0]);
}
static void _zh_timer_init(void)
{
const esp_timer_create_args_t timer_args = {
.callback = &_zh_inclinometer_reset_timer_on_alarm_cb,
};
esp_timer_create(&timer_args, &_inclinometer_reset_timer_handle);
}
static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
(void)arg;
switch (event_id)
{
case ZH_BUTTON_EVENT:
zh_encoder_button_event_on_isr_t *button_event = (zh_encoder_button_event_on_isr_t *)event_data;
if (button_event->button_status == ZH_ENCODER_BUTTON_STATUS_PRESSED)
{
esp_timer_start_once(_inclinometer_reset_timer_handle, 2000000);
_is_reset_mode = true;
}
else
{
if (_is_reset_occurred == true)
{
_is_reset_occurred = false;
break;
}
if (_is_reset_mode == true)
{
esp_timer_stop(_inclinometer_reset_timer_handle);
_is_reset_mode = false;
}
_is_menu_mode = !_is_menu_mode;
if (_is_menu_mode == true)
{
switch (_current_menu_value)
{
case MODE_ANGLE_ANGLE:
if (_a_inclinometer_task_handle != NULL)
{
vTaskDelete(_a_inclinometer_task_handle);
}
if (_b_inclinometer_task_handle != NULL)
{
vTaskDelete(_b_inclinometer_task_handle);
}
zh_inclinometer_deinit(&_a_inclinometer_handle);
zh_inclinometer_deinit(&_b_inclinometer_handle);
break;
case MODE_TACHO_TACHO:
if (_a_tachometer_task_handle != NULL)
{
vTaskDelete(_a_tachometer_task_handle);
}
if (_b_tachometer_task_handle != NULL)
{
vTaskDelete(_b_tachometer_task_handle);
}
zh_tachometer_deinit(&_a_tachometer_handle);
zh_tachometer_deinit(&_b_tachometer_handle);
break;
case MODE_ANGLE_TACHO:
if (_a_inclinometer_task_handle != NULL)
{
vTaskDelete(_a_inclinometer_task_handle);
}
if (_b_tachometer_task_handle != NULL)
{
vTaskDelete(_b_tachometer_task_handle);
}
zh_inclinometer_deinit(&_a_inclinometer_handle);
zh_tachometer_deinit(&_b_tachometer_handle);
break;
case MODE_REMOTE:
// To Do.
break;
default:
break;
}
zh_160x_clear_row(&_lcd_160x_handle, 1);
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
zh_160x_on_cursor(&_lcd_160x_handle, true);
}
else
{
zh_160x_off_cursor(&_lcd_160x_handle);
switch (_current_menu_value)
{
case MODE_ANGLE_ANGLE:
_zh_a_inclinometer_init();
_zh_b_inclinometer_init();
break;
case MODE_TACHO_TACHO:
_zh_a_tachometer_init();
_zh_b_tachometer_init();
break;
case MODE_ANGLE_TACHO:
_zh_a_inclinometer_init();
_zh_b_tachometer_init();
break;
case MODE_REMOTE:
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
zh_160x_print_char(&_lcd_160x_handle, "NOT CONNECTED");
break;
default:
break;
}
}
}
break;
case ZH_ENCODER_EVENT:
zh_encoder_event_on_isr_t *encoder_event = (zh_encoder_event_on_isr_t *)event_data;
if (_is_menu_mode == true)
{
_current_menu_value = (uint8_t)encoder_event->encoder_position;
zh_160x_clear_row(&_lcd_160x_handle, 0);
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
zh_160x_print_char(&_lcd_160x_handle, "MODE:");
zh_160x_print_char(&_lcd_160x_handle, _main_menu[(uint8_t)encoder_event->encoder_position]);
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
}
else
{
zh_encoder_set(&_mode_encoder_handle, (float)_current_menu_value);
}
break;
default:
break;
}
}
static void _zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
// To Do.
}
static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
(void)arg;
(void)event_data;
switch (event_id)
{
case WIFI_EVENT_STA_START:
esp_wifi_connect();
break;
case WIFI_EVENT_STA_DISCONNECTED:
if (_is_wifi_connected == true)
{
if (_zh_stats_task_handle != NULL)
{
vTaskDelete(_zh_stats_task_handle);
}
zh_syslog_deinit();
_is_wifi_connected = false;
}
if (_wifi_reconnect_retry_num < WIFI_STA_MAXIMUM_RETRY)
{
esp_wifi_connect();
++_wifi_reconnect_retry_num;
}
break;
case IP_EVENT_STA_GOT_IP:
_wifi_reconnect_retry_num = 0;
_is_wifi_connected = true;
zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
strcpy(config.syslog_ip, SYSLOG_IP);
zh_syslog_init(&config);
if (_zh_stats_task_handle == NULL)
{
xTaskCreatePinnedToCore(&_zh_stats_processing_task, "stats_processing_task", STATS_TASK_STACK_SIZE, NULL, STATS_TASK_PRIORITY, &_zh_stats_task_handle, tskNO_AFFINITY);
}
break;
default:
break;
}
}
static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg)
{
(void)arg;
_is_reset_mode = false;
_is_reset_occurred = true;
if (_is_menu_mode == false)
{
switch (_current_menu_value)
{
case MODE_ANGLE_ANGLE:
zh_inclinometer_reset(&_a_inclinometer_handle);
zh_inclinometer_reset(&_b_inclinometer_handle);
break;
case MODE_ANGLE_TACHO:
zh_inclinometer_reset(&_a_inclinometer_handle);
break;
default:
break;
}
}
}
static void _zh_a_inclinometer_processing_task(void *pvParameter)
{
(void)pvParameter;
float prev_angle = 0.0;
float current_angle = 0.0;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
zh_160x_print_char(&_lcd_160x_handle, "A:");
zh_160x_print_float(&_lcd_160x_handle, current_angle, 1);
xSemaphoreGive(_lcd_mutex);
for (;;)
{
zh_inclinometer_get(&_a_inclinometer_handle, &current_angle);
if (prev_angle != current_angle)
{
prev_angle = current_angle;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 5);
zh_160x_print_char(&_lcd_160x_handle, " ");
zh_160x_set_cursor(&_lcd_160x_handle, 1, 2);
zh_160x_print_float(&_lcd_160x_handle, current_angle, 1);
xSemaphoreGive(_lcd_mutex);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
_task_stats.a_inclinometer_task_min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL);
}
vTaskDelete(NULL);
}
static void _zh_b_inclinometer_processing_task(void *pvParameter)
{
(void)pvParameter;
float prev_angle = 0.0;
float current_angle = 0.0;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
zh_160x_print_char(&_lcd_160x_handle, "B:");
zh_160x_print_float(&_lcd_160x_handle, current_angle, 1);
xSemaphoreGive(_lcd_mutex);
for (;;)
{
zh_inclinometer_get(&_b_inclinometer_handle, &current_angle);
if (prev_angle != current_angle)
{
prev_angle = current_angle;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 13);
zh_160x_print_char(&_lcd_160x_handle, " ");
zh_160x_set_cursor(&_lcd_160x_handle, 1, 10);
zh_160x_print_float(&_lcd_160x_handle, current_angle, 1);
xSemaphoreGive(_lcd_mutex);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
_task_stats.b_inclinometer_task_min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL);
}
vTaskDelete(NULL);
}
static void _zh_a_tachometer_processing_task(void *pvParameter)
{
(void)pvParameter;
uint16_t prev_rpm = 0;
uint16_t current_rpm = 0;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
zh_160x_print_char(&_lcd_160x_handle, "A:");
zh_160x_print_int(&_lcd_160x_handle, current_rpm);
xSemaphoreGive(_lcd_mutex);
for (;;)
{
zh_tachometer_get(&_a_tachometer_handle, &current_rpm);
if (prev_rpm != current_rpm)
{
prev_rpm = current_rpm;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 3);
zh_160x_print_char(&_lcd_160x_handle, " ");
zh_160x_set_cursor(&_lcd_160x_handle, 1, 2);
zh_160x_print_int(&_lcd_160x_handle, current_rpm);
xSemaphoreGive(_lcd_mutex);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
_task_stats.a_tachometer_task_min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL);
}
vTaskDelete(NULL);
}
static void _zh_b_tachometer_processing_task(void *pvParameter)
{
(void)pvParameter;
uint16_t prev_rpm = 0;
uint16_t current_rpm = 0;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
zh_160x_print_char(&_lcd_160x_handle, "B:");
zh_160x_print_int(&_lcd_160x_handle, current_rpm);
xSemaphoreGive(_lcd_mutex);
for (;;)
{
zh_tachometer_get(&_b_tachometer_handle, &current_rpm);
if (prev_rpm != current_rpm)
{
prev_rpm = current_rpm;
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
zh_160x_set_cursor(&_lcd_160x_handle, 1, 11);
zh_160x_print_char(&_lcd_160x_handle, " ");
zh_160x_set_cursor(&_lcd_160x_handle, 1, 10);
zh_160x_print_int(&_lcd_160x_handle, current_rpm);
xSemaphoreGive(_lcd_mutex);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
_task_stats.b_tachometer_task_min_stack_size = (uint32_t)uxTaskGetStackHighWaterMark(NULL);
}
vTaskDelete(NULL);
}
static void _zh_stats_processing_task(void *pvParameter)
{
(void)pvParameter;
const esp_app_desc_t *esp_app_desc = esp_app_get_description();
for (;;)
{
char buffer[1024] = {0};
const zh_encoder_stats_t *encoder_stats = zh_encoder_get_stats();
const zh_pcf8574_stats_t *pcf8574_stats = zh_pcf8574_get_stats();
snprintf(buffer, sizeof(buffer), "A inclinometer task minimum free stack size: %lu.\n", _task_stats.a_inclinometer_task_min_stack_size);
zh_syslog_send(ZH_KERN, _task_stats.a_inclinometer_task_min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_inclinometer", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "B inclinometer task minimum free stack size: %lu.\n", _task_stats.b_inclinometer_task_min_stack_size);
zh_syslog_send(ZH_KERN, _task_stats.b_inclinometer_task_min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_inclinometer", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "A tachometer task minimum free stack size: %lu.\n", _task_stats.a_tachometer_task_min_stack_size);
zh_syslog_send(ZH_KERN, _task_stats.a_tachometer_task_min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_tachometer", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "B tachometer task minimum free stack size: %lu.\n", _task_stats.b_tachometer_task_min_stack_size);
zh_syslog_send(ZH_KERN, _task_stats.b_tachometer_task_min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_tachometer", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Stats task minimum free stack size: %lu.\n", (uint32_t)uxTaskGetStackHighWaterMark(NULL));
zh_syslog_send(ZH_KERN, (uint32_t)uxTaskGetStackHighWaterMark(NULL) > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_syslog", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Encoder task minimum free stack size: %lu.\n", encoder_stats->min_stack_size);
zh_syslog_send(ZH_KERN, encoder_stats->min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_encoder", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Expander task minimum free stack size: %lu.\n", pcf8574_stats->min_stack_size);
zh_syslog_send(ZH_KERN, pcf8574_stats->min_stack_size > 100 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_pcf8574", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of event post error: %lu.\n", encoder_stats->event_post_error);
zh_syslog_send(ZH_KERN, encoder_stats->event_post_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_encoder", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %lu.\n", encoder_stats->queue_overflow_error);
zh_syslog_send(ZH_KERN, encoder_stats->queue_overflow_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_encoder", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of i2c driver error: %lu.\n", pcf8574_stats->i2c_driver_error);
zh_syslog_send(ZH_KERN, pcf8574_stats->i2c_driver_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_pcf8574", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of event post error: %lu.\n", pcf8574_stats->event_post_error);
zh_syslog_send(ZH_KERN, pcf8574_stats->event_post_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_pcf8574", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of vector error: %lu.\n", pcf8574_stats->vector_error);
zh_syslog_send(ZH_KERN, pcf8574_stats->vector_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_pcf8574", buffer);
vTaskDelay(500 / portTICK_PERIOD_MS);
snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %lu.\n", pcf8574_stats->queue_overflow_error);
zh_syslog_send(ZH_KERN, pcf8574_stats->queue_overflow_error == 0 ? ZH_INFO : ZH_WARNING, esp_app_desc->project_name, "zh_pcf8574", buffer);
vTaskDelay(60000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}