Compare commits
2
Commits
main
..
1ad6a80fce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad6a80fce | ||
|
|
e987d4cafb |
No files matched your search
+4
-1
@@ -4,4 +4,7 @@ build
|
||||
sdkconfig.old
|
||||
desktop.ini
|
||||
.PVS-Studio
|
||||
sdkconfig
|
||||
sdkconfig
|
||||
dependencies.lock
|
||||
managed_components
|
||||
rules.md
|
||||
-27
@@ -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
|
||||
@@ -1,18 +1,21 @@
|
||||
# Universal Optical 2-Channel Angle Gauge / Tachometer
|
||||
|
||||
## Tested on
|
||||
## Features
|
||||
|
||||
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.
|
||||
1. Two independent channels supporting optical encoders with 3600 pulses per revolution.
|
||||
2. Four operating modes: both inclinometers, both tachometers, mixed angle/tachometer, and remote via ESP-NOW.
|
||||
3. 16x2 LCD display (PCF8574 I2C expander) with boot animation, progress bar, and real-time value updates.
|
||||
4. Rotary encoder menu with push-button for mode switching and angle reset (long press, 2 seconds).
|
||||
5. WiFi AP+STA mode with local access and STA connection for remote logging.
|
||||
6. Over-the-Air (OTA) firmware updates via built-in HTTP server.
|
||||
7. Remote syslog with periodic logging of task stack usage and driver error counters.
|
||||
8. FreeRTOS-based multitasking with separate tasks for each channel, encoder, LCD expander, and statistics monitoring.
|
||||
|
||||
## Build and flash
|
||||
|
||||
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
|
||||
|
||||
Submodule components/zh_160x_i2c deleted from 19fedfc816.
Submodule components/zh_encoder deleted from 911d402e47.
Submodule components/zh_espnow deleted from af76d3172f.
Submodule components/zh_inclinometer deleted from e05bc424cf.
Submodule components/zh_ota_server deleted from 9cf8f359ab.
Submodule components/zh_pcf8574 deleted from 60ee3f5b4b.
Submodule components/zh_syslog deleted from 0c4f9297ea.
Submodule components/zh_tachometer deleted from 935d3b4b97.
Submodule components/zh_vector deleted from 43d818a8d5.
+344
-156
@@ -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);
|
||||
}
|
||||
|
||||
+72
-53
@@ -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
|
||||
|
||||
@@ -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 <alexey.zholtikov@yandex.ru>"
|
||||
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"
|
||||
Reference in new issue
Block a user