diff --git a/.gitignore b/.gitignore index 9a00b9b..4591540 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ build sdkconfig.old desktop.ini .PVS-Studio -sdkconfig \ No newline at end of file +sdkconfig +dependencies.lock +managed_components +rules.md diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9f4cfe4..0000000 --- a/.gitmodules +++ /dev/null @@ -1,27 +0,0 @@ -[submodule "components/zh_inclinometer"] - path = components/zh_inclinometer - url = https://github.com/aZholtikov/zh_inclinometer -[submodule "components/zh_encoder"] - path = components/zh_encoder - url = https://github.com/aZholtikov/zh_encoder -[submodule "components/zh_160x_i2c"] - path = components/zh_160x_i2c - url = https://github.com/aZholtikov/zh_160x_i2c -[submodule "components/zh_pcf8574"] - path = components/zh_pcf8574 - url = https://github.com/aZholtikov/zh_pcf8574 -[submodule "components/zh_vector"] - path = components/zh_vector - url = https://github.com/aZholtikov/zh_vector -[submodule "components/zh_ota_server"] - path = components/zh_ota_server - url = https://github.com/aZholtikov/zh_ota_server -[submodule "components/zh_espnow"] - path = components/zh_espnow - url = https://github.com/aZholtikov/zh_espnow -[submodule "components/zh_tachometer"] - path = components/zh_tachometer - url = https://github.com/aZholtikov/zh_tachometer -[submodule "components/zh_syslog"] - path = components/zh_syslog - url = https://github.com/aZholtikov/zh_syslog diff --git a/README.md b/README.md index 6c81274..aad1322 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # Universal Optical 2-Channel Angle Gauge / Tachometer -## Tested on - -1. [ESP32 ESP-IDF v5.5.3](https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32/index.html) - ## SAST Tools [PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. @@ -12,7 +8,7 @@ Run the following command to firmware build and flash module: -```text +```bash cd your_projects_folder git clone --recurse-submodules https://github.com/aZholtikov/ate0005 cd ate0005 diff --git a/components/zh_160x_i2c b/components/zh_160x_i2c deleted file mode 160000 index 19fedfc..0000000 --- a/components/zh_160x_i2c +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19fedfc816854bc1437f9e216d4f11d1bf933f2c diff --git a/components/zh_encoder b/components/zh_encoder deleted file mode 160000 index 911d402..0000000 --- a/components/zh_encoder +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 911d402e47dc585db53b2ddde73ce53c7b6f954e diff --git a/components/zh_espnow b/components/zh_espnow deleted file mode 160000 index af76d31..0000000 --- a/components/zh_espnow +++ /dev/null @@ -1 +0,0 @@ -Subproject commit af76d3172f05b5b9ad73006e18abe333898097a6 diff --git a/components/zh_inclinometer b/components/zh_inclinometer deleted file mode 160000 index e05bc42..0000000 --- a/components/zh_inclinometer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e05bc424cf5ac6839d32302fb561c8b36c91fd7b diff --git a/components/zh_ota_server b/components/zh_ota_server deleted file mode 160000 index 9cf8f35..0000000 --- a/components/zh_ota_server +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9cf8f359abbea13ef7eda2263361f9b9913c04d6 diff --git a/components/zh_pcf8574 b/components/zh_pcf8574 deleted file mode 160000 index 60ee3f5..0000000 --- a/components/zh_pcf8574 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 60ee3f5b4baf28b38c282b5411f56616e466186e diff --git a/components/zh_syslog b/components/zh_syslog deleted file mode 160000 index 0c4f929..0000000 --- a/components/zh_syslog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0c4f9297ead26cb2d7ad52bc9f4523e91b1ed42b diff --git a/components/zh_tachometer b/components/zh_tachometer deleted file mode 160000 index 935d3b4..0000000 --- a/components/zh_tachometer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 935d3b4b97a3816d2112ce9a5ed362e660c3e5d8 diff --git a/components/zh_vector b/components/zh_vector deleted file mode 160000 index 43d818a..0000000 --- a/components/zh_vector +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43d818a8d542923ef2aa0dc6083f5972ac8fbb3e diff --git a/main/ate0005.c b/main/ate0005.c index 91e346d..7901ffd 100644 --- a/main/ate0005.c +++ b/main/ate0005.c @@ -1,22 +1,39 @@ #include "ate0005.h" +/** @brief Global structure holding minimum free stack sizes for processing tasks. */ 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}; +/** @brief HTTP server handle for OTA and web interface. */ static httpd_handle_t _http_server_handle = NULL; -static zh_pcf8574_handle_t _lcd_handle = {0}; -static zh_encoder_handle_t _mode_encoder_handle = {0}; -static zh_inclinometer_handle_t _a_inclinometer_handle = {0}; -static zh_inclinometer_handle_t _b_inclinometer_handle = {0}; -static zh_tachometer_handle_t _a_tachometer_handle = {0}; -static zh_tachometer_handle_t _b_tachometer_handle = {0}; +/** @brief PCF8574 handle for LCD I2C expander. */ +static zh_pcf8574_handle_t *_lcd_handle = NULL; +/** @brief LCD 16x2 display handle wrapper. */ +static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL; +/** @brief Mode encoder handle for menu navigation. */ +static zh_encoder_handle_t *_mode_encoder_handle = NULL; +/** @brief Inclinometer handle for channel A. */ +static zh_inclinometer_handle_t *_a_inclinometer_handle = NULL; +/** @brief Inclinometer handle for channel B. */ +static zh_inclinometer_handle_t *_b_inclinometer_handle = NULL; +/** @brief Tachometer handle for channel A. */ +static zh_tachometer_handle_t *_a_tachometer_handle = NULL; +/** @brief Tachometer handle for channel B. */ +static zh_tachometer_handle_t *_b_tachometer_handle = NULL; +/** @brief ESP timer handle for inclinometer reset timeout. */ static esp_timer_handle_t _inclinometer_reset_timer_handle = {0}; +/** @brief Mutex for protecting LCD access from multiple tasks. */ static SemaphoreHandle_t _lcd_mutex = NULL; +/** @brief Task handle for the A inclinometer processing task. */ static TaskHandle_t _a_inclinometer_task_handle = NULL; +/** @brief Task handle for the B inclinometer processing task. */ static TaskHandle_t _b_inclinometer_task_handle = NULL; +/** @brief Task handle for the A tachometer processing task. */ static TaskHandle_t _a_tachometer_task_handle = NULL; +/** @brief Task handle for the B tachometer processing task. */ static TaskHandle_t _b_tachometer_task_handle = NULL; +/** @brief Task handle for the statistics logging task. */ static TaskHandle_t _zh_stats_task_handle = NULL; static void _zh_wifi_init(void); @@ -41,6 +58,20 @@ 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); +static const char *_main_menu[] = {MAIN_MENU_ANGLE_ANGLE_NAME, MAIN_MENU_TACHO_TACHO_NAME, MAIN_MENU_ANGLE_TACHO_NAME, MAIN_MENU_REMOTE_NAME}; +/** @brief Flag indicating whether the menu selection mode is active. */ +volatile static bool _is_menu_mode = false; +/** @brief Flag indicating whether a long button press (reset mode) is detected. */ +volatile static bool _is_reset_mode = false; +/** @brief Flag indicating that a reset event has occurred and must be cleared. */ +volatile static bool _is_reset_occurred = false; +/** @brief Index of the currently selected menu mode. */ +volatile static uint8_t _current_menu_value = 0; +/** @brief Counter for WiFi STA reconnection retry attempts. */ +volatile static uint8_t _wifi_reconnect_retry_num = 0; +/** @brief Flag indicating whether WiFi STA is connected. */ +volatile static bool _is_wifi_connected = false; + 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; @@ -49,37 +80,51 @@ volatile static uint8_t _current_menu_value = 0; volatile static uint8_t _wifi_reconnect_retry_num = 0; volatile static bool _is_wifi_connected = false; +/** + * @brief Application entry point. + * + * Initializes all system components: LCD, encoder, timers, inclinometers, + * WiFi, web server, and ESP-NOW. Checks OTA state and marks firmware + * as valid if a pending verification is detected. + */ void app_main(void) { - _lcd_mutex = xSemaphoreCreateMutex(); - 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(); + _lcd_mutex = xSemaphoreCreateMutex(); /*!< Create mutex for LCD access protection. */ + nvs_flash_init(); /*!< Initialize NVS flash storage. */ + esp_event_loop_create_default(); /*!< Create default event loop. */ + _zh_pcf8574_init(); /*!< Initialize I2C bus and PCF8574 expander for LCD. */ + _zh_system_load(); /*!< Display boot animation on LCD. */ + _zh_encoder_init(); /*!< Initialize mode encoder for menu navigation. */ + _zh_timer_init(); /*!< Initialize inclinometer reset timer. */ + _zh_a_inclinometer_init(); /*!< Initialize inclinometer for channel A and its task. */ + _zh_b_inclinometer_init(); /*!< Initialize inclinometer for channel B and its task. */ + _zh_wifi_init(); /*!< Initialize WiFi in AP+STA mode. */ + _zh_webserver_init(); /*!< Start HTTP server with OTA endpoint. */ + _zh_espnow_init(); /*!< Initialize ESP-NOW communication. */ + const esp_partition_t *running = esp_ota_get_running_partition(); /*!< Get current OTA 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) + esp_ota_get_state_partition(running, &ota_state); /*!< Read OTA image state. */ + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) /*!< Check if firmware update is pending verification. */ { - vTaskDelay(60000 / portTICK_PERIOD_MS); - esp_ota_mark_app_valid_cancel_rollback(); + vTaskDelay(60000 / portTICK_PERIOD_MS); /*!< Wait 60 seconds before confirming. */ + esp_ota_mark_app_valid_cancel_rollback(); /*!< Mark firmware as valid and cancel rollback. */ } } +/** + * @brief Initialize WiFi in AP+STA mode. + * + * Configures and starts WiFi with both AP and STA interfaces. + * AP provides local access for device configuration. + * STA connects to the configured network for remote logging. + */ static void _zh_wifi_init(void) { - esp_netif_init(); - esp_netif_create_default_wifi_ap(); - esp_netif_create_default_wifi_sta(); + esp_netif_init(); /*!< Initialize networking stack. */ + esp_netif_create_default_wifi_ap(); /*!< Create default network interface for AP. */ + esp_netif_create_default_wifi_sta(); /*!< Create default network interface for STA. */ wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); - esp_wifi_init(&wifi_init_config); + esp_wifi_init(&wifi_init_config); /*!< Initialize WiFi driver with default config. */ wifi_config_t wifi_ap_config = { .ap.ssid = WIFI_AP_SSID, .ap.password = WIFI_AP_PASS, @@ -91,144 +136,215 @@ static void _zh_wifi_init(void) .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(); + esp_wifi_set_mode(WIFI_MODE_APSTA); /*!< Set WiFi mode to AP+STA. */ + esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config); /*!< Apply AP configuration. */ + esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config); /*!< Apply STA configuration. */ + esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &_zh_wifi_event_handler, NULL, NULL); /*!< Register WiFi event handler. */ + esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &_zh_wifi_event_handler, NULL, NULL); /*!< Register IP event handler. */ + esp_wifi_start(); /*!< Start WiFi. */ } +/** + * @brief Initialize the HTTP server with OTA support. + * + * Starts the ESP HTTP server and registers the OTA server + * endpoint for over-the-air firmware updates. + */ 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, "/"); + httpd_start(&_http_server_handle, &config); /*!< Start HTTP server. */ + zh_ota_server_init(_http_server_handle, "/"); /*!< Register OTA endpoint. */ } +/** + * @brief Initialize ESP-NOW communication. + * + * Currently a placeholder. ESP-NOW initialization is not yet implemented. + */ static void _zh_espnow_init(void) { // To Do. } +/** + * @brief Initialize the mode encoder for menu navigation. + * + * Configures the rotary encoder with GPIO pins, task parameters, + * and event handler. Sets encoder range to cover all menu modes. + */ static void _zh_encoder_init(void) { - esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &_zh_encoder_event_handler, NULL, NULL); + esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &_zh_encoder_event_handler, NULL, NULL); /*!< Register encoder event handler. */ 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.s_gpio_debounce_time = 1000; - 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); + config.task_priority = ENCODER_TASK_PRIORITY; /*!< Set encoder task priority. */ + config.stack_size = ENCODER_TASK_STACK_SIZE; /*!< Set encoder task stack size. */ + config.queue_size = 5; /*!< Set event queue size. */ + config.a_gpio_number = MODE_ENCODER_A_GPIO; /*!< Set A-phase GPIO pin. */ + config.b_gpio_number = MODE_ENCODER_B_GPIO; /*!< Set B-phase GPIO pin. */ + config.s_gpio_number = MODE_ENCODER_S_GPIO; /*!< Set switch GPIO pin. */ + config.s_gpio_debounce_time = 1000; /*!< Set switch debounce time in microseconds. */ + config.encoder_min_value = 0; /*!< Set minimum encoder value (first menu item). */ + config.encoder_max_value = (sizeof(_main_menu) / sizeof(_main_menu[0])) - 1; /*!< Set maximum encoder value (last menu item). */ + config.encoder_step = 1; /*!< Set encoder step size. */ + config.encoder_number = MODE_ENCODER_NUMBER; /*!< Set encoder instance number. */ + zh_encoder_init(&config, &_mode_encoder_handle); /*!< Initialize encoder driver. */ + zh_encoder_set(&_mode_encoder_handle, 0); /*!< Reset encoder to initial position. */ } +/** + * @brief Initialize inclinometer for channel A and create its processing task. + * + * Configures optical device GPIO pins and encoder pulses (3600). + * Creates a FreeRTOS task for processing inclinometer data. + */ 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); + config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */ + config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */ + config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */ + zh_inclinometer_init(&config, &_a_inclinometer_handle); /*!< Initialize inclinometer driver. */ 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); + &_a_inclinometer_task_handle, tskNO_AFFINITY); /*!< Create processing task. */ } +/** + * @brief Initialize inclinometer for channel B and create its processing task. + * + * Configures optical device GPIO pins and encoder pulses (3600). + * Creates a FreeRTOS task for processing inclinometer data. + */ 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); + config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */ + config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */ + config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */ + zh_inclinometer_init(&config, &_b_inclinometer_handle); /*!< Initialize inclinometer driver. */ 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); + &_b_inclinometer_task_handle, tskNO_AFFINITY); /*!< Create processing task. */ } +/** + * @brief Initialize tachometer for channel A and create its processing task. + * + * Configures optical device GPIO pins and encoder pulses (3600). + * Creates a FreeRTOS task for processing tachometer data. + */ 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); + config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */ + config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */ + config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */ + zh_tachometer_init(&config, &_a_tachometer_handle); /*!< Initialize tachometer driver. */ 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); + &_a_tachometer_task_handle, tskNO_AFFINITY); /*!< Create processing task. */ } +/** + * @brief Initialize tachometer for channel B and create its processing task. + * + * Configures optical device GPIO pins and encoder pulses (3600). + * Creates a FreeRTOS task for processing tachometer data. + */ 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); + config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */ + config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */ + config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */ + zh_tachometer_init(&config, &_b_tachometer_handle); /*!< Initialize tachometer driver. */ 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); + &_b_tachometer_task_handle, tskNO_AFFINITY); /*!< Create processing task. */ } +/** + * @brief Initialize I2C bus and PCF8574 expander for LCD display. + * + * Configures I2C with internal pull-ups and creates the master bus. + * Initializes the PCF8574 expander and 16x2 LCD wrapper. + */ 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, + .scl_io_num = I2C_SCL_GPIO, /*!< I2C clock GPIO. */ + .sda_io_num = I2C_SDA_GPIO, /*!< I2C data GPIO. */ + .glitch_ignore_cnt = 7, /*!< Glitch filter count. */ + .flags.enable_internal_pullup = true, /*!< Enable internal pull-up resistors. */ }; - i2c_new_master_bus(&i2c_bus_config, &_i2c_bus_handle); + i2c_new_master_bus(&i2c_bus_config, &_i2c_bus_handle); /*!< Create I2C master bus. */ 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, ZH_LCD_16X2); + config.task_priority = EXPANDER_TASK_PRIORITY; /*!< Set expander task priority. */ + config.stack_size = EXPANDER_TASK_STACK_SIZE; /*!< Set expander task stack size. */ + config.i2c_handle = _i2c_bus_handle; /*!< Assign I2C bus handle. */ + config.i2c_address = LCD_I2C_ADDRESS; /*!< Set I2C address of PCF8574. */ + zh_pcf8574_init(&config, &_lcd_handle); /*!< Initialize PCF8574 driver. */ + zh_160x_init(&_lcd_handle, &_lcd_160x_handle, ZH_LCD_16X2); /*!< Initialize 16x2 LCD wrapper. */ } +/** + * @brief Display system boot animation on LCD. + * + * Shows company name, project name, and version. + * Then displays a progress bar during system loading. + * Finally, shows the initial operating mode. + */ static void _zh_system_load(void) { - zh_160x_set_cursor(&_lcd_handle, 0, 0); - zh_160x_print_char(&_lcd_handle, "LLC AEROTECH"); - zh_160x_set_cursor(&_lcd_handle, 1, 0); - const esp_app_desc_t *esp_app_desc = esp_app_get_description(); - zh_160x_print_char(&_lcd_handle, esp_app_desc->project_name); - zh_160x_set_cursor(&_lcd_handle, 1, 8); - zh_160x_print_char(&_lcd_handle, "V"); - zh_160x_print_char(&_lcd_handle, esp_app_desc->version); - vTaskDelay(1000 / portTICK_PERIOD_MS); - zh_160x_lcd_clear(&_lcd_handle); - zh_160x_set_cursor(&_lcd_handle, 0, 0); - zh_160x_print_char(&_lcd_handle, "SYSTEM LOADING:"); + zh_160x_set_cursor(&_lcd_160x_handle, 0, 0); /*!< Set cursor to first row, first column. */ + zh_160x_print_char(&_lcd_160x_handle, "LLC AEROTECH"); /*!< Display company name. */ + zh_160x_set_cursor(&_lcd_160x_handle, 1, 0); /*!< Set cursor to second row, first column. */ + const esp_app_desc_t *esp_app_desc = esp_app_get_description(); /*!< Get application description. */ + zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->project_name); /*!< Display project name. */ + zh_160x_set_cursor(&_lcd_160x_handle, 1, 8); /*!< Set cursor to version position. */ + zh_160x_print_char(&_lcd_160x_handle, "V"); /*!< Display version prefix. */ + zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->version); /*!< Display firmware version. */ + vTaskDelay(1000 / portTICK_PERIOD_MS); /*!< Wait 1 second. */ + zh_160x_lcd_clear(&_lcd_160x_handle); /*!< Clear LCD. */ + 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_handle, 1, i); + zh_160x_print_progress_bar(&_lcd_160x_handle, 1, i); /*!< Draw progress bar at given percentage. */ vTaskDelay(100 / portTICK_PERIOD_MS); } vTaskDelay(500 / portTICK_PERIOD_MS); - zh_160x_lcd_clear(&_lcd_handle); + zh_160x_lcd_clear(&_lcd_160x_handle); vTaskDelay(500 / portTICK_PERIOD_MS); - zh_160x_set_cursor(&_lcd_handle, 0, 0); - zh_160x_print_char(&_lcd_handle, "MODE:"); - zh_160x_print_char(&_lcd_handle, _main_menu[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[0]); } +/** + * @brief Initialize the inclinometer reset timer. + * + * Creates a one-shot timer that triggers after a long button press. + * Used to reset inclinometer angles when the encoder button is held. + */ static void _zh_timer_init(void) { const esp_timer_create_args_t timer_args = { - .callback = &_zh_inclinometer_reset_timer_on_alarm_cb, + .callback = &_zh_inclinometer_reset_timer_on_alarm_cb, /*!< Timer callback function. */ }; - esp_timer_create(&timer_args, &_inclinometer_reset_timer_handle); + esp_timer_create(&timer_args, &_inclinometer_reset_timer_handle); /*!< Create one-shot timer. */ } +/** + * @brief Handle encoder events (button press/release and rotation). + * + * Processes button events for menu navigation and long-press reset. + * Processes rotation events for menu mode selection. + * + * @param arg Unused context argument. + * @param event_base Event base (ZH_ENCODER). + * @param event_id Event identifier (ZH_BUTTON_EVENT or ZH_ENCODER_EVENT). + * @param event_data Pointer to event data structure. + */ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { switch (event_id) @@ -237,7 +353,7 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in zh_encoder_button_event_on_isr_t *button_event = event_data; if (button_event->button_status == ENCODER_BUTTON_PRESSED) { - esp_timer_start_once(_inclinometer_reset_timer_handle, 2000000); + esp_timer_start_once(_inclinometer_reset_timer_handle, 2000000); /*!< Start reset timer (2 seconds). */ _is_reset_mode = true; } else @@ -249,7 +365,7 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in } if (_is_reset_mode == true) { - esp_timer_stop(_inclinometer_reset_timer_handle); + esp_timer_stop(_inclinometer_reset_timer_handle); /*!< Stop reset timer on button release. */ _is_reset_mode = false; } _is_menu_mode = !_is_menu_mode; @@ -281,13 +397,13 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in default: break; } - zh_160x_clear_row(&_lcd_handle, 1); - zh_160x_set_cursor(&_lcd_handle, 0, 0); - zh_160x_on_cursor(&_lcd_handle, true); + 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_handle); + zh_160x_off_cursor(&_lcd_160x_handle); switch (_current_menu_value) { case MODE_ANGLE_ANGLE: @@ -303,8 +419,8 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in _zh_b_tachometer_init(); break; case MODE_REMOTE: - zh_160x_set_cursor(&_lcd_handle, 1, 0); - zh_160x_print_char(&_lcd_handle, "NOT CONNECTED"); + zh_160x_set_cursor(&_lcd_160x_handle, 1, 0); + zh_160x_print_char(&_lcd_160x_handle, "NOT CONNECTED"); break; default: break; @@ -316,12 +432,12 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in zh_encoder_event_on_isr_t *encoder_event = event_data; if (_is_menu_mode == true) { - _current_menu_value = (uint8_t)encoder_event->encoder_position; // -V2004 - zh_160x_clear_row(&_lcd_handle, 0); - zh_160x_set_cursor(&_lcd_handle, 0, 0); - zh_160x_print_char(&_lcd_handle, "MODE:"); - zh_160x_print_char(&_lcd_handle, _main_menu[(uint8_t)encoder_event->encoder_position]); // -V2004 - zh_160x_set_cursor(&_lcd_handle, 0, 0); + _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 { @@ -333,11 +449,33 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in } } +/** + * @brief Handle ESP-NOW events. + * + * Currently a placeholder. ESP-NOW event handling is not yet implemented. + * + * @param arg Unused context argument. + * @param event_base Event base. + * @param event_id Event identifier. + * @param event_data Pointer to event data structure. + */ static void _zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { // To Do. } +/** + * @brief Handle WiFi events (connection, disconnection, IP acquisition). + * + * Manages STA connection lifecycle: initiates connection on start, + * attempts reconnection on disconnect (with retry limit), and starts + * syslog + stats task upon successful IP acquisition. + * + * @param arg Unused context argument. + * @param event_base Event base (WIFI_EVENT or IP_EVENT). + * @param event_id Event identifier. + * @param event_data Pointer to event data structure. + */ static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { switch (event_id) @@ -371,6 +509,14 @@ static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32 } } +/** + * @brief Timer callback for inclinometer reset on long button press. + * + * Called when the encoder button is held for 2 seconds. + * Resets inclinometer angles for the active mode. + * + * @param arg Unused context argument. + */ static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg) { _is_reset_mode = false; @@ -392,13 +538,21 @@ static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg) } } +/** + * @brief FreeRTOS task for processing inclinometer A data. + * + * Reads angle from inclinometer A and updates the LCD display + * on row 1, columns 2-5. Tracks minimum free stack size for monitoring. + * + * @param pvParameter Unused parameter. + */ static void _zh_a_inclinometer_processing_task(void *pvParameter) { float prev_angle = 0, current_angle = 0; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 0); - zh_160x_print_char(&_lcd_handle, "A:"); - zh_160x_print_float(&_lcd_handle, current_angle, 1); + 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 (;;) { @@ -407,10 +561,10 @@ static void _zh_a_inclinometer_processing_task(void *pvParameter) { prev_angle = current_angle; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 5); - zh_160x_print_char(&_lcd_handle, " "); - zh_160x_set_cursor(&_lcd_handle, 1, 2); - zh_160x_print_float(&_lcd_handle, current_angle, 1); + 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); @@ -419,13 +573,21 @@ static void _zh_a_inclinometer_processing_task(void *pvParameter) vTaskDelete(NULL); } +/** + * @brief FreeRTOS task for processing inclinometer B data. + * + * Reads angle from inclinometer B and updates the LCD display + * on row 1, columns 10-13. Tracks minimum free stack size for monitoring. + * + * @param pvParameter Unused parameter. + */ static void _zh_b_inclinometer_processing_task(void *pvParameter) { float prev_angle = 0, current_angle = 0; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 8); - zh_160x_print_char(&_lcd_handle, "B:"); - zh_160x_print_float(&_lcd_handle, current_angle, 1); + 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 (;;) { @@ -434,10 +596,10 @@ static void _zh_b_inclinometer_processing_task(void *pvParameter) { prev_angle = current_angle; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 13); - zh_160x_print_char(&_lcd_handle, " "); - zh_160x_set_cursor(&_lcd_handle, 1, 10); - zh_160x_print_float(&_lcd_handle, current_angle, 1); + 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); @@ -446,13 +608,21 @@ static void _zh_b_inclinometer_processing_task(void *pvParameter) vTaskDelete(NULL); } +/** + * @brief FreeRTOS task for processing tachometer A data. + * + * Reads RPM from tachometer A and updates the LCD display + * on row 1, columns 2-5. Tracks minimum free stack size for monitoring. + * + * @param pvParameter Unused parameter. + */ static void _zh_a_tachometer_processing_task(void *pvParameter) { uint16_t prev_rpm = 0, current_rpm = 0; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 0); - zh_160x_print_char(&_lcd_handle, "A:"); - zh_160x_print_int(&_lcd_handle, current_rpm); + 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 (;;) { @@ -461,10 +631,10 @@ static void _zh_a_tachometer_processing_task(void *pvParameter) { prev_rpm = current_rpm; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 3); - zh_160x_print_char(&_lcd_handle, " "); - zh_160x_set_cursor(&_lcd_handle, 1, 2); - zh_160x_print_int(&_lcd_handle, current_rpm); + 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); @@ -473,13 +643,21 @@ static void _zh_a_tachometer_processing_task(void *pvParameter) vTaskDelete(NULL); } +/** + * @brief FreeRTOS task for processing tachometer B data. + * + * Reads RPM from tachometer B and updates the LCD display + * on row 1, columns 10-13. Tracks minimum free stack size for monitoring. + * + * @param pvParameter Unused parameter. + */ static void _zh_b_tachometer_processing_task(void *pvParameter) { uint16_t prev_rpm = 0, current_rpm = 0; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 8); - zh_160x_print_char(&_lcd_handle, "B:"); - zh_160x_print_int(&_lcd_handle, current_rpm); + 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 (;;) { @@ -488,10 +666,10 @@ static void _zh_b_tachometer_processing_task(void *pvParameter) { prev_rpm = current_rpm; xSemaphoreTake(_lcd_mutex, portMAX_DELAY); - zh_160x_set_cursor(&_lcd_handle, 1, 11); - zh_160x_print_char(&_lcd_handle, " "); - zh_160x_set_cursor(&_lcd_handle, 1, 10); - zh_160x_print_int(&_lcd_handle, current_rpm); + 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); @@ -500,6 +678,16 @@ static void _zh_b_tachometer_processing_task(void *pvParameter) vTaskDelete(NULL); } +/** + * @brief FreeRTOS task for periodic system statistics logging. + * + * Periodically sends syslog messages with minimum free stack sizes + * for all processing tasks, and error counters for encoder and + * PCF8574 drivers. Logs at INFO level if values are within limits, + * WARNING otherwise. + * + * @param pvParameter Unused parameter. + */ static void _zh_stats_processing_task(void *pvParameter) { const esp_app_desc_t *esp_app_desc = esp_app_get_description(); @@ -508,43 +696,43 @@ static void _zh_stats_processing_task(void *pvParameter) 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(); - sprintf(buffer, "A inclinometer task minimum free stack size: %ld.\n", _task_stats.a_inclinometer_task_min_stack_size); + snprintf(buffer, sizeof(buffer), "A inclinometer task minimum free stack size: %ld.\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); - sprintf(buffer, "B inclinometer task minimum free stack size: %ld.\n", _task_stats.b_inclinometer_task_min_stack_size); + snprintf(buffer, sizeof(buffer), "B inclinometer task minimum free stack size: %ld.\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); - sprintf(buffer, "A tachometer task minimum free stack size: %ld.\n", _task_stats.b_tachometer_task_min_stack_size); + snprintf(buffer, sizeof(buffer), "A tachometer task minimum free stack size: %ld.\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: %ld.\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); - sprintf(buffer, "B tachometer task minimum free stack size: %ld.\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); - sprintf(buffer, "Stats task minimum free stack size: %ld.\n", (uint32_t)uxTaskGetStackHighWaterMark(NULL)); + snprintf(buffer, sizeof(buffer), "Stats task minimum free stack size: %ld.\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); - sprintf(buffer, "Encoder task minimum free stack size: %ld.\n", encoder_stats->min_stack_size); + snprintf(buffer, sizeof(buffer), "Encoder task minimum free stack size: %ld.\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); - sprintf(buffer, "Expander task minimum free stack size: %ld.\n", pcf8574_stats->min_stack_size); + snprintf(buffer, sizeof(buffer), "Expander task minimum free stack size: %ld.\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); - sprintf(buffer, "Number of event post error: %ld.\n", encoder_stats->event_post_error); + snprintf(buffer, sizeof(buffer), "Number of event post error: %ld.\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); - sprintf(buffer, "Number of queue overflow error: %ld.\n", encoder_stats->queue_overflow_error); + snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %ld.\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); - sprintf(buffer, "Number of i2c driver error: %ld.\n", pcf8574_stats->i2c_driver_error); + snprintf(buffer, sizeof(buffer), "Number of i2c driver error: %ld.\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); - sprintf(buffer, "Number of event post error: %ld.\n", pcf8574_stats->event_post_error); + snprintf(buffer, sizeof(buffer), "Number of event post error: %ld.\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); - sprintf(buffer, "Number of vector error: %ld.\n", pcf8574_stats->vector_error); + snprintf(buffer, sizeof(buffer), "Number of vector error: %ld.\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); - sprintf(buffer, "Number of queue overflow error: %ld.\n", pcf8574_stats->queue_overflow_error); + snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %ld.\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); } diff --git a/main/ate0005.h b/main/ate0005.h index a0b2d50..3949b5c 100644 --- a/main/ate0005.h +++ b/main/ate0005.h @@ -1,5 +1,18 @@ /** * @file ate0005.h + * + * @brief Header file for ATE0005 — Universal Optical 2-Channel Angle Gauge / Tachometer + * + * This header defines task priorities, stack sizes, mode constants, + * WiFi configuration, GPIO pin mappings, and data structures for the + * ATE0005 device running on ESP32 platform with FreeRTOS. + * + * Key features: + * - Task configuration for inclinometer, tachometer, encoder, and stats + * - WiFi AP/STA mode configuration + * - I2C and GPIO pin definitions for peripherals + * - Mode selection constants for different operating modes + * - LCD display and PCF8574 expander macros */ #pragma once @@ -15,80 +28,86 @@ #include "zh_espnow.h" #include "zh_syslog.h" -#define STATS_TASK_PRIORITY tskIDLE_PRIORITY -#define STATS_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 -#define A_INCLINOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 -#define A_INCLINOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 -#define B_INCLINOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 -#define B_INCLINOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 -#define A_TACHOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 -#define A_TACHOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 -#define B_TACHOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 -#define B_TACHOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 -#define ENCODER_TASK_PRIORITY tskIDLE_PRIORITY + 4 -#define ENCODER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE -#define EXPANDER_TASK_PRIORITY tskIDLE_PRIORITY + 4 -#define EXPANDER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#define STATS_TASK_PRIORITY tskIDLE_PRIORITY /*!< Priority of the statistics logging task. */ +#define STATS_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 1280 /*!< Stack size for the statistics logging task. */ +#define A_INCLINOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 /*!< Priority of the A inclinometer processing task. */ +#define A_INCLINOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 /*!< Stack size for the A inclinometer processing task. */ +#define B_INCLINOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 /*!< Priority of the B inclinometer processing task. */ +#define B_INCLINOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 /*!< Stack size for the B inclinometer processing task. */ +#define A_TACHOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 /*!< Priority of the A tachometer processing task. */ +#define A_TACHOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 /*!< Stack size for the A tachometer processing task. */ +#define B_TACHOMETER_TASK_PRIORITY tskIDLE_PRIORITY + 5 /*!< Priority of the B tachometer processing task. */ +#define B_TACHOMETER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE + 512 /*!< Stack size for the B tachometer processing task. */ +#define ENCODER_TASK_PRIORITY tskIDLE_PRIORITY + 4 /*!< Priority of the mode encoder task. */ +#define ENCODER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE /*!< Stack size for the mode encoder task. */ +#define EXPANDER_TASK_PRIORITY tskIDLE_PRIORITY + 4 /*!< Priority of the PCF8574 expander task. */ +#define EXPANDER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE /*!< Stack size for the PCF8574 expander task. */ -#define TRIAC_ON true -#define TRIAC_OFF false +#define TRIAC_ON true /*!< Triac enabled state. */ +#define TRIAC_OFF false /*!< Triac disabled state. */ -#define ENCODER_BUTTON_RELEASED true -#define ENCODER_BUTTON_PRESSED false +#define ENCODER_BUTTON_RELEASED true /*!< Encoder button released state. */ +#define ENCODER_BUTTON_PRESSED false /*!< Encoder button pressed state. */ -#define LED_OFF ZH_PCF8574_GPIO_LOW -#define LED_ON ZH_PCF8574_GPIO_HIGH +#define LED_OFF ZH_PCF8574_GPIO_LOW /*!< LED turned off (low level). */ +#define LED_ON ZH_PCF8574_GPIO_HIGH /*!< LED turned on (high level). */ -#define RELAY_OFF ZH_PCF8574_GPIO_LOW -#define RELAY_ON ZH_PCF8574_GPIO_HIGH +#define RELAY_OFF ZH_PCF8574_GPIO_LOW /*!< Relay disconnected (low level). */ +#define RELAY_ON ZH_PCF8574_GPIO_HIGH /*!< Relay connected (high level). */ -#define MODE_ANGLE_ANGLE 0 -#define MAIN_MENU_ANGLE_ANGLE_NAME "ANGLE/ANGLE" -#define MODE_TACHO_TACHO 1 -#define MAIN_MENU_TACHO_TACHO_NAME "TACHO/TACHO" -#define MODE_ANGLE_TACHO 2 -#define MAIN_MENU_ANGLE_TACHO_NAME "ANGLE/TACHO" -#define MODE_REMOTE 3 -#define MAIN_MENU_REMOTE_NAME "REMOTE" +#define MODE_ANGLE_ANGLE 0 /*!< Operating mode: both inclinometers active. */ +#define MAIN_MENU_ANGLE_ANGLE_NAME "ANGLE/ANGLE" /*!< Menu label for angle/angle mode. */ +#define MODE_TACHO_TACHO 1 /*!< Operating mode: both tachometers active. */ +#define MAIN_MENU_TACHO_TACHO_NAME "TACHO/TACHO" /*!< Menu label for tachometer/tachometer mode. */ +#define MODE_ANGLE_TACHO 2 /*!< Operating mode: A inclinometer and B tachometer active. */ +#define MAIN_MENU_ANGLE_TACHO_NAME "ANGLE/TACHO" /*!< Menu label for angle/tachometer mode. */ +#define MODE_REMOTE 3 /*!< Operating mode: remote control via ESP-NOW. */ +#define MAIN_MENU_REMOTE_NAME "REMOTE" /*!< Menu label for remote mode. */ -#define WIFI_AP_SSID "ATE0005" -#define WIFI_AP_PASS "repairlab" -#define WIFI_AP_CHANNEL 1 -#define MAX_AP_CONNECTION 4 +#define WIFI_AP_SSID "ATE0005" /*!< WiFi AP SSID. */ +#define WIFI_AP_PASS "repairlab" /*!< WiFi AP password. */ +#define WIFI_AP_CHANNEL 1 /*!< WiFi AP channel. */ +#define MAX_AP_CONNECTION 4 /*!< Maximum number of stations connected to AP. */ -#define WIFI_STA_SSID "rick_professor" -#define WIFI_STA_PASS "repairlab" -#define WIFI_STA_MAXIMUM_RETRY 10 +#define WIFI_STA_SSID "rick_professor" /*!< WiFi STA SSID. */ +#define WIFI_STA_PASS "repairlab" /*!< WiFi STA password. */ +#define WIFI_STA_MAXIMUM_RETRY 10 /*!< Maximum number of reconnection attempts for STA mode. */ -#define SYSLOG_IP "77.51.201.8" +#define SYSLOG_IP "77.51.201.8" /*!< IP address of the remote syslog server. */ -#define I2C_SCL_GPIO GPIO_NUM_22 -#define I2C_SDA_GPIO GPIO_NUM_21 +#define I2C_SCL_GPIO GPIO_NUM_22 /*!< I2C clock line GPIO. */ +#define I2C_SDA_GPIO GPIO_NUM_21 /*!< I2C data line GPIO. */ -#define LCD_I2C_ADDRESS 0x20 /*!< U2. */ +#define LCD_I2C_ADDRESS 0x20 /*!< I2C address of the LCD PCF8574 expander (U2). */ -#define MODE_ENCODER_NUMBER 0x01 /*!< Connector EN1. */ -#define MODE_ENCODER_A_GPIO GPIO_NUM_27 -#define MODE_ENCODER_B_GPIO GPIO_NUM_26 -#define MODE_ENCODER_S_GPIO GPIO_NUM_25 +#define MODE_ENCODER_NUMBER 0x01 /*!< Encoder instance number (Connector EN1). */ +#define MODE_ENCODER_A_GPIO GPIO_NUM_27 /*!< Mode encoder A-phase GPIO. */ +#define MODE_ENCODER_B_GPIO GPIO_NUM_26 /*!< Mode encoder B-phase GPIO. */ +#define MODE_ENCODER_S_GPIO GPIO_NUM_25 /*!< Mode encoder switch GPIO. */ -#define A_OPTICAL_DEVICE_A_GPIO GPIO_NUM_14 /*!< Connector EN2. */ -#define A_OPTICAL_DEVICE_B_GPIO GPIO_NUM_13 +#define A_OPTICAL_DEVICE_A_GPIO GPIO_NUM_14 /*!< Optical device A-phase GPIO for channel A (Connector EN2). */ +#define A_OPTICAL_DEVICE_B_GPIO GPIO_NUM_13 /*!< Optical device B-phase GPIO for channel A. */ -#define B_OPTICAL_DEVICE_A_GPIO GPIO_NUM_18 /*!< Connector EN3. */ -#define B_OPTICAL_DEVICE_B_GPIO GPIO_NUM_19 +#define B_OPTICAL_DEVICE_A_GPIO GPIO_NUM_18 /*!< Optical device A-phase GPIO for channel B (Connector EN3). */ +#define B_OPTICAL_DEVICE_B_GPIO GPIO_NUM_19 /*!< Optical device B-phase GPIO for channel B. */ #ifdef __cplusplus extern "C" { #endif + /** + * @brief Structure holding minimum free stack sizes for processing tasks. + * + * Tracks the minimum free stack size observed at runtime for each + * processing task, used for syslog monitoring and stack overflow detection. + */ typedef struct { - uint32_t a_inclinometer_task_min_stack_size; - uint32_t b_inclinometer_task_min_stack_size; - uint32_t a_tachometer_task_min_stack_size; - uint32_t b_tachometer_task_min_stack_size; + 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; #ifdef __cplusplus diff --git a/main/idf_component.yml b/main/idf_component.yml new file mode 100644 index 0000000..d31cd1b --- /dev/null +++ b/main/idf_component.yml @@ -0,0 +1,22 @@ +description: Honeywell Ram Air Modulation Linear Electromechanical Actuator CMM 21-20-36 P/N 541674-X Linear Actuator Interface Test Box +url: https://github.com/aZholtikov/ate0002 +version: "1.2.0" +license: "Apache-2.0" +maintainers: + - "Alexey Zholtikov " +dependencies: + idf: ">=5.0" + zh_160x_i2c: + git: "https://github.com/aZholtikov/zh_160x_i2c.git" + zh_syslog: + git: "https://github.com/aZholtikov/zh_syslog.git" + zh_ota_server: + git: "https://github.com/aZholtikov/zh_ota_server.git" + zh_encoder: + git: "https://github.com/aZholtikov/zh_encoder.git" + zh_inclinometer: + git: "https://github.com/aZholtikov/zh_inclinometer" + zh_tachometer: + git: "https://github.com/aZholtikov/zh_tachometer" + zh_espnow: + git: "https://github.com/aZholtikov/zh_espnow" \ No newline at end of file