Compare commits
2
Commits
main
..
1ad6a80fce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad6a80fce | ||
|
|
e987d4cafb |
No files matched your search
+2
-3
@@ -3,9 +3,8 @@ build
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
sdkconfig.old
|
sdkconfig.old
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
.PVS-Studio
|
||||||
sdkconfig
|
sdkconfig
|
||||||
dependencies.lock
|
dependencies.lock
|
||||||
managed_components
|
managed_components
|
||||||
commit-rules.md
|
rules.md
|
||||||
readme-rules.md
|
|
||||||
wiki-rules.md
|
|
||||||
@@ -2,30 +2,23 @@
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
1. Two independent measuring channels for optical encoders with 3600 pulses per revolution.
|
1. Two independent channels supporting optical encoders with 3600 pulses per revolution.
|
||||||
2. Four operating modes selected through the on-device menu: angle/angle, tacho/tacho, angle/tacho and remote.
|
2. Four operating modes: both inclinometers, both tachometers, mixed angle/tachometer, and remote via ESP-NOW.
|
||||||
3. Angle measurement with 0.1 degree resolution and resettable current angle value.
|
3. 16x2 LCD display (PCF8574 I2C expander) with boot animation, progress bar, and real-time value updates.
|
||||||
4. Rotational speed measurement in RPM with 100 ms refresh period.
|
4. Rotary encoder menu with push-button for mode switching and angle reset (long press, 2 seconds).
|
||||||
5. 16x2 LCD display on PCF8574 I2C expander with boot animation, progress bar and live value updates.
|
5. WiFi AP+STA mode with local access and STA connection for remote logging.
|
||||||
6. Rotary encoder menu for mode switching and push-button long press for angle reset.
|
6. Over-the-Air (OTA) firmware updates via built-in HTTP server.
|
||||||
7. WiFi access point and station modes running simultaneously.
|
7. Remote syslog with periodic logging of task stack usage and driver error counters.
|
||||||
8. Over-the-Air firmware updates through the built-in web server with rollback self-check.
|
8. FreeRTOS-based multitasking with separate tasks for each channel, encoder, LCD expander, and statistics monitoring.
|
||||||
9. Remote syslog logging of task stack usage and driver error counters after station connection.
|
|
||||||
10. FreeRTOS multitasking with separate tasks for each channel, encoder, LCD expander and statistics.
|
|
||||||
|
|
||||||
## Attention
|
## Build and flash
|
||||||
|
|
||||||
1. Remote mode via ESP-NOW is not implemented yet.
|
Run the following command to firmware build and flash module:
|
||||||
2. WiFi station credentials are hardcoded in the firmware configuration.
|
|
||||||
3. Long press of the encoder button resets the angle value, short press switches the menu mode.
|
|
||||||
4. The GPIO pin mapping matches the ATE0005 board connectors and must not be changed without a hardware revision.
|
|
||||||
|
|
||||||
## Using
|
|
||||||
|
|
||||||
Clone and build the project:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/aZholtikov/ate0005
|
cd your_projects_folder
|
||||||
|
git clone --recurse-submodules https://github.com/aZholtikov/ate0005
|
||||||
cd ate0005
|
cd ate0005
|
||||||
build
|
idf.py build
|
||||||
|
idf.py flash
|
||||||
```
|
```
|
||||||
+329
-395
@@ -1,261 +1,77 @@
|
|||||||
#include "ate0005.h"
|
#include "ate0005.h"
|
||||||
|
|
||||||
/**
|
/** @brief Global structure holding minimum free stack sizes for processing tasks. */
|
||||||
* @brief Internal structure holding the tasks minimum free stack sizes.
|
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};
|
||||||
*
|
|
||||||
* Each field stores the high water mark of the corresponding processing
|
|
||||||
* task, reported by uxTaskGetStackHighWaterMark(). The statistics task reads
|
|
||||||
* these values to log stack usage and to raise a warning when a value falls
|
|
||||||
* below the threshold.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t a_inclinometer_task_min_stack_size; /*!< Minimum free stack size of the A inclinometer task */
|
|
||||||
uint32_t b_inclinometer_task_min_stack_size; /*!< Minimum free stack size of the B inclinometer task */
|
|
||||||
uint32_t a_tachometer_task_min_stack_size; /*!< Minimum free stack size of the A tachometer task */
|
|
||||||
uint32_t b_tachometer_task_min_stack_size; /*!< Minimum free stack size of the B tachometer task */
|
|
||||||
} _task_stats_t;
|
|
||||||
|
|
||||||
static httpd_handle_t _http_server_handle = NULL; /*!< HTTP server handle */
|
/** @brief HTTP server handle for OTA and web interface. */
|
||||||
static zh_pcf8574_handle_t *_lcd_handle = NULL; /*!< PCF8574 LCD expander handle */
|
static httpd_handle_t _http_server_handle = NULL;
|
||||||
static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL; /*!< 16x2 LCD display handle */
|
|
||||||
static zh_encoder_handle_t *_mode_encoder_handle = NULL; /*!< Mode selector rotary encoder handle */
|
/** @brief PCF8574 handle for LCD I2C expander. */
|
||||||
static zh_inclinometer_handle_t *_a_inclinometer_handle = NULL; /*!< A channel inclinometer handle */
|
static zh_pcf8574_handle_t *_lcd_handle = NULL;
|
||||||
static zh_inclinometer_handle_t *_b_inclinometer_handle = NULL; /*!< B channel inclinometer handle */
|
/** @brief LCD 16x2 display handle wrapper. */
|
||||||
static zh_tachometer_handle_t *_a_tachometer_handle = NULL; /*!< A channel tachometer handle */
|
static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL;
|
||||||
static zh_tachometer_handle_t *_b_tachometer_handle = NULL; /*!< B channel tachometer handle */
|
/** @brief Mode encoder handle for menu navigation. */
|
||||||
static esp_timer_handle_t _inclinometer_reset_timer_handle = {0}; /*!< Inclinometer reset software timer handle */
|
static zh_encoder_handle_t *_mode_encoder_handle = NULL;
|
||||||
static SemaphoreHandle_t _lcd_mutex = NULL; /*!< Mutex to protect the LCD display access */
|
/** @brief Inclinometer handle for channel A. */
|
||||||
static TaskHandle_t _a_inclinometer_task_handle = NULL; /*!< Handle of the A inclinometer processing task */
|
static zh_inclinometer_handle_t *_a_inclinometer_handle = NULL;
|
||||||
static TaskHandle_t _b_inclinometer_task_handle = NULL; /*!< Handle of the B inclinometer processing task */
|
/** @brief Inclinometer handle for channel B. */
|
||||||
static TaskHandle_t _a_tachometer_task_handle = NULL; /*!< Handle of the A tachometer processing task */
|
static zh_inclinometer_handle_t *_b_inclinometer_handle = NULL;
|
||||||
static TaskHandle_t _b_tachometer_task_handle = NULL; /*!< Handle of the B tachometer processing task */
|
/** @brief Tachometer handle for channel A. */
|
||||||
static TaskHandle_t _zh_stats_task_handle = NULL; /*!< Handle of the statistics processing task */
|
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;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize WiFi in AP+STA mode.
|
|
||||||
*
|
|
||||||
* Creates default AP and STA netifs, configures the AP with the SSID,
|
|
||||||
* password, channel and maximum number of connections and the STA with the
|
|
||||||
* SSID and password. Registers the Wi-Fi event handler and starts Wi-Fi.
|
|
||||||
*/
|
|
||||||
static void _zh_wifi_init(void);
|
static void _zh_wifi_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the web server with OTA support.
|
|
||||||
*
|
|
||||||
* Starts the HTTP server with default configuration and registers the OTA
|
|
||||||
* update page handler at the root URI.
|
|
||||||
*/
|
|
||||||
static void _zh_webserver_init(void);
|
static void _zh_webserver_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the ESP-NOW wireless protocol.
|
|
||||||
*
|
|
||||||
* Not implemented yet.
|
|
||||||
*/
|
|
||||||
static void _zh_espnow_init(void);
|
static void _zh_espnow_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the mode selector rotary encoder.
|
|
||||||
*
|
|
||||||
* Registers the encoder event handler and initializes the encoder with the
|
|
||||||
* task configuration, GPIO numbers, queue size and position limits
|
|
||||||
* corresponding to the number of main menu entries.
|
|
||||||
*/
|
|
||||||
static void _zh_encoder_init(void);
|
static void _zh_encoder_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the A channel inclinometer.
|
|
||||||
*
|
|
||||||
* Initializes the inclinometer with the A channel optical device GPIO numbers
|
|
||||||
* and the encoder pulse count, then creates the A inclinometer processing
|
|
||||||
* task.
|
|
||||||
*/
|
|
||||||
static void _zh_a_inclinometer_init(void);
|
static void _zh_a_inclinometer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the B channel inclinometer.
|
|
||||||
*
|
|
||||||
* Initializes the inclinometer with the B channel optical device GPIO numbers
|
|
||||||
* and the encoder pulse count, then creates the B inclinometer processing
|
|
||||||
* task.
|
|
||||||
*/
|
|
||||||
static void _zh_b_inclinometer_init(void);
|
static void _zh_b_inclinometer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the A channel tachometer.
|
|
||||||
*
|
|
||||||
* Initializes the tachometer with the A channel optical device GPIO numbers
|
|
||||||
* and the encoder pulse count, then creates the A tachometer processing
|
|
||||||
* task.
|
|
||||||
*/
|
|
||||||
static void _zh_a_tachometer_init(void);
|
static void _zh_a_tachometer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the B channel tachometer.
|
|
||||||
*
|
|
||||||
* Initializes the tachometer with the B channel optical device GPIO numbers
|
|
||||||
* and the encoder pulse count, then creates the B tachometer processing
|
|
||||||
* task.
|
|
||||||
*/
|
|
||||||
static void _zh_b_tachometer_init(void);
|
static void _zh_b_tachometer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the PCF8574 I2C expander of the LCD display.
|
|
||||||
*
|
|
||||||
* Creates the I2C master bus, initializes the PCF8574 expander with the task
|
|
||||||
* configuration and I2C address, initializes the 16x2 LCD driver on top of
|
|
||||||
* it and creates the LCD access mutex.
|
|
||||||
*/
|
|
||||||
static void _zh_pcf8574_init(void);
|
static void _zh_pcf8574_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show the system loading screen on the LCD display.
|
|
||||||
*
|
|
||||||
* Displays the manufacturer name, project name and firmware version, then an
|
|
||||||
* animated progress bar. After loading, shows the current operating mode
|
|
||||||
* label.
|
|
||||||
*/
|
|
||||||
static void _zh_system_load(void);
|
static void _zh_system_load(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create the inclinometer reset timer.
|
|
||||||
*
|
|
||||||
* Creates a one-shot software timer that resets the inclinometer angle on
|
|
||||||
* long press of the mode encoder button.
|
|
||||||
*/
|
|
||||||
static void _zh_timer_init(void);
|
static void _zh_timer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle the mode encoder events.
|
|
||||||
*
|
|
||||||
* On button press starts the inclinometer reset timer and enters reset mode.
|
|
||||||
* On button release stops the timer and either ignores the event if the reset
|
|
||||||
* already occurred, or toggles the menu mode.
|
|
||||||
*
|
|
||||||
* Entering the menu mode stops the tasks and deinitializes the active devices
|
|
||||||
* of the current operating mode and enables the LCD cursor. Leaving the menu
|
|
||||||
* mode initializes the devices of the selected mode.
|
|
||||||
*
|
|
||||||
* On encoder rotation in the menu mode updates and displays the selected menu
|
|
||||||
* entry, otherwise restores the encoder position to the current menu value.
|
|
||||||
*
|
|
||||||
* @param arg User data registered with the event handler (unused)
|
|
||||||
* @param event_base Event base of the registered event
|
|
||||||
* @param event_id ID of the encoder or button event
|
|
||||||
* @param event_data Pointer to the event payload structure
|
|
||||||
*/
|
|
||||||
static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle the ESP-NOW events.
|
|
||||||
*
|
|
||||||
* Not implemented yet.
|
|
||||||
*
|
|
||||||
* @param arg User data registered with the event handler (unused)
|
|
||||||
* @param event_base Event base of the registered event
|
|
||||||
* @param event_id ID of the ESP-NOW event (unused)
|
|
||||||
* @param event_data Pointer to the event payload (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
static void _zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle the Wi-Fi station events.
|
|
||||||
*
|
|
||||||
* Initiates connection on station start, reconnects with a limited number of
|
|
||||||
* retries on disconnection and stops the syslog statistics task. On IP
|
|
||||||
* assignment resets the retry counter, initializes the syslog client and
|
|
||||||
* creates the statistics processing task.
|
|
||||||
*
|
|
||||||
* @param arg User data registered with the event handler (unused)
|
|
||||||
* @param event_base Event base of the registered event
|
|
||||||
* @param event_id ID of the Wi-Fi or IP event
|
|
||||||
* @param event_data Pointer to the event payload structure (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Inclinometer reset timer callback.
|
|
||||||
*
|
|
||||||
* Called on long press of the mode encoder button. Resets the angle of the
|
|
||||||
* active inclinometers of the current operating mode when the device is not
|
|
||||||
* in the menu mode.
|
|
||||||
*
|
|
||||||
* @param arg User data registered with the timer (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg);
|
static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A channel inclinometer processing task.
|
|
||||||
*
|
|
||||||
* Displays the A channel angle with 1 decimal place on the second LCD row.
|
|
||||||
* The value is refreshed only when it changes. Updates the task minimum free
|
|
||||||
* stack size for the statistics task.
|
|
||||||
*
|
|
||||||
* @param pvParameter Task parameter (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_a_inclinometer_processing_task(void *pvParameter);
|
static void _zh_a_inclinometer_processing_task(void *pvParameter);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief B channel inclinometer processing task.
|
|
||||||
*
|
|
||||||
* Displays the B channel angle with 1 decimal place on the second LCD row.
|
|
||||||
* The value is refreshed only when it changes. Updates the task minimum free
|
|
||||||
* stack size for the statistics task.
|
|
||||||
*
|
|
||||||
* @param pvParameter Task parameter (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_b_inclinometer_processing_task(void *pvParameter);
|
static void _zh_b_inclinometer_processing_task(void *pvParameter);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A channel tachometer processing task.
|
|
||||||
*
|
|
||||||
* Displays the A channel RPM on the second LCD row. The value is refreshed
|
|
||||||
* only when it changes. Updates the task minimum free stack size for the
|
|
||||||
* statistics task.
|
|
||||||
*
|
|
||||||
* @param pvParameter Task parameter (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_a_tachometer_processing_task(void *pvParameter);
|
static void _zh_a_tachometer_processing_task(void *pvParameter);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief B channel tachometer processing task.
|
|
||||||
*
|
|
||||||
* Displays the B channel RPM on the second LCD row. The value is refreshed
|
|
||||||
* only when it changes. Updates the task minimum free stack size for the
|
|
||||||
* statistics task.
|
|
||||||
*
|
|
||||||
* @param pvParameter Task parameter (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_b_tachometer_processing_task(void *pvParameter);
|
static void _zh_b_tachometer_processing_task(void *pvParameter);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Statistics processing task.
|
|
||||||
*
|
|
||||||
* Periodically sends the tasks minimum free stack sizes and the component
|
|
||||||
* error counters to the remote syslog server. Messages are logged with the
|
|
||||||
* warning severity when a stack size falls below the threshold or an error
|
|
||||||
* counter is not zero.
|
|
||||||
*
|
|
||||||
* @param pvParameter Task parameter (unused)
|
|
||||||
*/
|
|
||||||
static void _zh_stats_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 Main application entry point.
|
/** @brief Flag indicating whether the menu selection mode is active. */
|
||||||
*
|
volatile static bool _is_menu_mode = false;
|
||||||
* Initializes NVS flash with erase and re-init on empty or version mismatch,
|
/** @brief Flag indicating whether a long button press (reset mode) is detected. */
|
||||||
* the default event loop, the PCF8574 LCD expander, the system loading
|
volatile static bool _is_reset_mode = false;
|
||||||
* screen, the mode encoder, the inclinometer reset timer, the A and B
|
/** @brief Flag indicating that a reset event has occurred and must be cleared. */
|
||||||
* inclinometers, WiFi in AP+STA mode, the web server and ESP-NOW.
|
volatile static bool _is_reset_occurred = false;
|
||||||
*
|
/** @brief Index of the currently selected menu mode. */
|
||||||
* Then performs the OTA rollback self-check: if the running image is pending
|
volatile static uint8_t _current_menu_value = 0;
|
||||||
* verification, waits 60 seconds and marks it as valid.
|
/** @brief Counter for WiFi STA reconnection retry attempts. */
|
||||||
*/
|
volatile static uint8_t _wifi_reconnect_retry_num = 0;
|
||||||
void app_main(void);
|
/** @brief Flag indicating whether WiFi STA is connected. */
|
||||||
|
volatile static bool _is_wifi_connected = false;
|
||||||
|
|
||||||
volatile static _task_stats_t _task_stats = {A_INCLINOMETER_TASK_STACK_SIZE, B_INCLINOMETER_TASK_STACK_SIZE, A_TACHOMETER_TASK_STACK_SIZE, B_TACHOMETER_TASK_STACK_SIZE};
|
|
||||||
static const char *_main_menu[] = {MAIN_MENU_ANGLE_ANGLE_NAME, MAIN_MENU_TACHO_TACHO_NAME, MAIN_MENU_ANGLE_TACHO_NAME, MAIN_MENU_REMOTE_NAME};
|
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_menu_mode = false;
|
||||||
volatile static bool _is_reset_mode = false;
|
volatile static bool _is_reset_mode = false;
|
||||||
@@ -264,41 +80,51 @@ volatile static uint8_t _current_menu_value = 0;
|
|||||||
volatile static uint8_t _wifi_reconnect_retry_num = 0;
|
volatile static uint8_t _wifi_reconnect_retry_num = 0;
|
||||||
volatile static bool _is_wifi_connected = false;
|
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)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
esp_err_t err = nvs_flash_init();
|
_lcd_mutex = xSemaphoreCreateMutex(); /*!< Create mutex for LCD access protection. */
|
||||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
nvs_flash_init(); /*!< Initialize NVS flash storage. */
|
||||||
{
|
esp_event_loop_create_default(); /*!< Create default event loop. */
|
||||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
_zh_pcf8574_init(); /*!< Initialize I2C bus and PCF8574 expander for LCD. */
|
||||||
ESP_ERROR_CHECK(nvs_flash_init());
|
_zh_system_load(); /*!< Display boot animation on LCD. */
|
||||||
}
|
_zh_encoder_init(); /*!< Initialize mode encoder for menu navigation. */
|
||||||
esp_event_loop_create_default();
|
_zh_timer_init(); /*!< Initialize inclinometer reset timer. */
|
||||||
_zh_pcf8574_init();
|
_zh_a_inclinometer_init(); /*!< Initialize inclinometer for channel A and its task. */
|
||||||
_zh_system_load();
|
_zh_b_inclinometer_init(); /*!< Initialize inclinometer for channel B and its task. */
|
||||||
_zh_encoder_init();
|
_zh_wifi_init(); /*!< Initialize WiFi in AP+STA mode. */
|
||||||
_zh_timer_init();
|
_zh_webserver_init(); /*!< Start HTTP server with OTA endpoint. */
|
||||||
_zh_a_inclinometer_init();
|
_zh_espnow_init(); /*!< Initialize ESP-NOW communication. */
|
||||||
_zh_b_inclinometer_init();
|
const esp_partition_t *running = esp_ota_get_running_partition(); /*!< Get current OTA partition. */
|
||||||
_zh_wifi_init();
|
|
||||||
_zh_webserver_init();
|
|
||||||
_zh_espnow_init();
|
|
||||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
|
||||||
esp_ota_img_states_t ota_state = {0};
|
esp_ota_img_states_t ota_state = {0};
|
||||||
esp_ota_get_state_partition(running, &ota_state);
|
esp_ota_get_state_partition(running, &ota_state); /*!< Read OTA image state. */
|
||||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
|
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) /*!< Check if firmware update is pending verification. */
|
||||||
{
|
{
|
||||||
vTaskDelay(60000 / portTICK_PERIOD_MS);
|
vTaskDelay(60000 / portTICK_PERIOD_MS); /*!< Wait 60 seconds before confirming. */
|
||||||
esp_ota_mark_app_valid_cancel_rollback();
|
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)
|
static void _zh_wifi_init(void)
|
||||||
{
|
{
|
||||||
esp_netif_init();
|
esp_netif_init(); /*!< Initialize networking stack. */
|
||||||
esp_netif_create_default_wifi_ap();
|
esp_netif_create_default_wifi_ap(); /*!< Create default network interface for AP. */
|
||||||
esp_netif_create_default_wifi_sta();
|
esp_netif_create_default_wifi_sta(); /*!< Create default network interface for STA. */
|
||||||
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
|
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 = {
|
wifi_config_t wifi_ap_config = {
|
||||||
.ap.ssid = WIFI_AP_SSID,
|
.ap.ssid = WIFI_AP_SSID,
|
||||||
.ap.password = WIFI_AP_PASS,
|
.ap.password = WIFI_AP_PASS,
|
||||||
@@ -310,125 +136,180 @@ static void _zh_wifi_init(void)
|
|||||||
.sta.ssid = WIFI_STA_SSID,
|
.sta.ssid = WIFI_STA_SSID,
|
||||||
.sta.password = WIFI_STA_PASS,
|
.sta.password = WIFI_STA_PASS,
|
||||||
};
|
};
|
||||||
esp_wifi_set_mode(WIFI_MODE_APSTA);
|
esp_wifi_set_mode(WIFI_MODE_APSTA); /*!< Set WiFi mode to AP+STA. */
|
||||||
esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config);
|
esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config); /*!< Apply AP configuration. */
|
||||||
esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config);
|
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);
|
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);
|
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();
|
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)
|
static void _zh_webserver_init(void)
|
||||||
{
|
{
|
||||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||||
httpd_start(&_http_server_handle, &config);
|
httpd_start(&_http_server_handle, &config); /*!< Start HTTP server. */
|
||||||
zh_ota_server_init(_http_server_handle, "/");
|
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)
|
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)
|
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();
|
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
|
||||||
config.task_priority = ENCODER_TASK_PRIORITY;
|
config.task_priority = ENCODER_TASK_PRIORITY; /*!< Set encoder task priority. */
|
||||||
config.stack_size = ENCODER_TASK_STACK_SIZE;
|
config.stack_size = ENCODER_TASK_STACK_SIZE; /*!< Set encoder task stack size. */
|
||||||
config.queue_size = 5;
|
config.queue_size = 5; /*!< Set event queue size. */
|
||||||
config.a_gpio_number = MODE_ENCODER_A_GPIO;
|
config.a_gpio_number = MODE_ENCODER_A_GPIO; /*!< Set A-phase GPIO pin. */
|
||||||
config.b_gpio_number = MODE_ENCODER_B_GPIO;
|
config.b_gpio_number = MODE_ENCODER_B_GPIO; /*!< Set B-phase GPIO pin. */
|
||||||
config.s_gpio_number = MODE_ENCODER_S_GPIO;
|
config.s_gpio_number = MODE_ENCODER_S_GPIO; /*!< Set switch GPIO pin. */
|
||||||
config.encoder_min_value = 0;
|
config.s_gpio_debounce_time = 1000; /*!< Set switch debounce time in microseconds. */
|
||||||
config.encoder_max_value = (sizeof(_main_menu) / sizeof(_main_menu[0])) - 1;
|
config.encoder_min_value = 0; /*!< Set minimum encoder value (first menu item). */
|
||||||
config.encoder_step = 1;
|
config.encoder_max_value = (sizeof(_main_menu) / sizeof(_main_menu[0])) - 1; /*!< Set maximum encoder value (last menu item). */
|
||||||
config.encoder_number = MODE_ENCODER_NUMBER;
|
config.encoder_step = 1; /*!< Set encoder step size. */
|
||||||
zh_encoder_init(&config, &_mode_encoder_handle);
|
config.encoder_number = MODE_ENCODER_NUMBER; /*!< Set encoder instance number. */
|
||||||
zh_encoder_set(&_mode_encoder_handle, 0);
|
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)
|
static void _zh_a_inclinometer_init(void)
|
||||||
{
|
{
|
||||||
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
|
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
|
||||||
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO;
|
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */
|
||||||
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO;
|
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */
|
||||||
config.encoder_pulses = 3600;
|
config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */
|
||||||
zh_inclinometer_init(&config, &_a_inclinometer_handle);
|
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,
|
xTaskCreatePinnedToCore(&_zh_a_inclinometer_processing_task, "a_inclinometer_processing_task", A_INCLINOMETER_TASK_STACK_SIZE, NULL, A_INCLINOMETER_TASK_PRIORITY,
|
||||||
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)
|
static void _zh_b_inclinometer_init(void)
|
||||||
{
|
{
|
||||||
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
|
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
|
||||||
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO;
|
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */
|
||||||
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO;
|
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */
|
||||||
config.encoder_pulses = 3600;
|
config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */
|
||||||
zh_inclinometer_init(&config, &_b_inclinometer_handle);
|
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,
|
xTaskCreatePinnedToCore(&_zh_b_inclinometer_processing_task, "b_inclinometer_processing_task", B_INCLINOMETER_TASK_STACK_SIZE, NULL, B_INCLINOMETER_TASK_PRIORITY,
|
||||||
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)
|
static void _zh_a_tachometer_init(void)
|
||||||
{
|
{
|
||||||
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
|
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
|
||||||
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO;
|
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */
|
||||||
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO;
|
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */
|
||||||
config.encoder_pulses = 3600;
|
config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */
|
||||||
zh_tachometer_init(&config, &_a_tachometer_handle);
|
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,
|
xTaskCreatePinnedToCore(&_zh_a_tachometer_processing_task, "a_tachometer_processing_task", A_TACHOMETER_TASK_STACK_SIZE, NULL, A_TACHOMETER_TASK_PRIORITY,
|
||||||
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)
|
static void _zh_b_tachometer_init(void)
|
||||||
{
|
{
|
||||||
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
|
zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT();
|
||||||
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO;
|
config.a_gpio_number = B_OPTICAL_DEVICE_A_GPIO; /*!< Set A-phase GPIO pin. */
|
||||||
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO;
|
config.b_gpio_number = B_OPTICAL_DEVICE_B_GPIO; /*!< Set B-phase GPIO pin. */
|
||||||
config.encoder_pulses = 3600;
|
config.encoder_pulses = 3600; /*!< Set encoder pulses per revolution. */
|
||||||
zh_tachometer_init(&config, &_b_tachometer_handle);
|
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,
|
xTaskCreatePinnedToCore(&_zh_b_tachometer_processing_task, "b_tachometer_processing_task", B_TACHOMETER_TASK_STACK_SIZE, NULL, B_TACHOMETER_TASK_PRIORITY,
|
||||||
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)
|
static void _zh_pcf8574_init(void)
|
||||||
{
|
{
|
||||||
i2c_master_bus_handle_t _i2c_bus_handle = NULL;
|
i2c_master_bus_handle_t _i2c_bus_handle = NULL;
|
||||||
i2c_master_bus_config_t i2c_bus_config = {
|
i2c_master_bus_config_t i2c_bus_config = {
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
.scl_io_num = I2C_SCL_GPIO,
|
.scl_io_num = I2C_SCL_GPIO, /*!< I2C clock GPIO. */
|
||||||
.sda_io_num = I2C_SDA_GPIO,
|
.sda_io_num = I2C_SDA_GPIO, /*!< I2C data GPIO. */
|
||||||
.glitch_ignore_cnt = 7,
|
.glitch_ignore_cnt = 7, /*!< Glitch filter count. */
|
||||||
.flags.enable_internal_pullup = true,
|
.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();
|
zh_pcf8574_init_config_t config = ZH_PCF8574_INIT_CONFIG_DEFAULT();
|
||||||
config.task_priority = EXPANDER_TASK_PRIORITY;
|
config.task_priority = EXPANDER_TASK_PRIORITY; /*!< Set expander task priority. */
|
||||||
config.stack_size = EXPANDER_TASK_STACK_SIZE;
|
config.stack_size = EXPANDER_TASK_STACK_SIZE; /*!< Set expander task stack size. */
|
||||||
config.i2c_handle = _i2c_bus_handle;
|
config.i2c_handle = _i2c_bus_handle; /*!< Assign I2C bus handle. */
|
||||||
config.i2c_address = LCD_I2C_ADDRESS;
|
config.i2c_address = LCD_I2C_ADDRESS; /*!< Set I2C address of PCF8574. */
|
||||||
zh_pcf8574_init(&config, &_lcd_handle);
|
zh_pcf8574_init(&config, &_lcd_handle); /*!< Initialize PCF8574 driver. */
|
||||||
zh_160x_init(&_lcd_handle, &_lcd_160x_handle, ZH_LCD_16X2);
|
zh_160x_init(&_lcd_handle, &_lcd_160x_handle, ZH_LCD_16X2); /*!< Initialize 16x2 LCD wrapper. */
|
||||||
_lcd_mutex = xSemaphoreCreateMutex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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)
|
static void _zh_system_load(void)
|
||||||
{
|
{
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
|
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");
|
zh_160x_print_char(&_lcd_160x_handle, "LLC AEROTECH"); /*!< Display company name. */
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
|
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();
|
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);
|
zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->project_name); /*!< Display project name. */
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
|
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8); /*!< Set cursor to version position. */
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "V");
|
zh_160x_print_char(&_lcd_160x_handle, "V"); /*!< Display version prefix. */
|
||||||
zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->version);
|
zh_160x_print_char(&_lcd_160x_handle, esp_app_desc->version); /*!< Display firmware version. */
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
vTaskDelay(1000 / portTICK_PERIOD_MS); /*!< Wait 1 second. */
|
||||||
zh_160x_lcd_clear(&_lcd_160x_handle);
|
zh_160x_lcd_clear(&_lcd_160x_handle); /*!< Clear LCD. */
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
|
zh_160x_set_cursor(&_lcd_160x_handle, 0, 0);
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "SYSTEM LOADING:");
|
zh_160x_print_char(&_lcd_160x_handle, "SYSTEM LOADING:");
|
||||||
for (uint8_t i = 0; i <= 100; i += 10)
|
for (uint8_t i = 0; i <= 100; i += 10)
|
||||||
{
|
{
|
||||||
zh_160x_print_progress_bar(&_lcd_160x_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(100 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
@@ -439,24 +320,40 @@ static void _zh_system_load(void)
|
|||||||
zh_160x_print_char(&_lcd_160x_handle, _main_menu[0]);
|
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)
|
static void _zh_timer_init(void)
|
||||||
{
|
{
|
||||||
const esp_timer_create_args_t timer_args = {
|
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)
|
static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
(void)arg;
|
|
||||||
switch (event_id)
|
switch (event_id)
|
||||||
{
|
{
|
||||||
case ZH_BUTTON_EVENT:
|
case ZH_BUTTON_EVENT:
|
||||||
zh_encoder_button_event_on_isr_t *button_event = (zh_encoder_button_event_on_isr_t *)event_data;
|
zh_encoder_button_event_on_isr_t *button_event = event_data;
|
||||||
if (button_event->button_status == ZH_ENCODER_BUTTON_STATUS_PRESSED)
|
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;
|
_is_reset_mode = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -468,7 +365,7 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in
|
|||||||
}
|
}
|
||||||
if (_is_reset_mode == true)
|
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_reset_mode = false;
|
||||||
}
|
}
|
||||||
_is_menu_mode = !_is_menu_mode;
|
_is_menu_mode = !_is_menu_mode;
|
||||||
@@ -477,42 +374,25 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in
|
|||||||
switch (_current_menu_value)
|
switch (_current_menu_value)
|
||||||
{
|
{
|
||||||
case MODE_ANGLE_ANGLE:
|
case MODE_ANGLE_ANGLE:
|
||||||
if (_a_inclinometer_task_handle != NULL)
|
vTaskDelete(_a_inclinometer_task_handle);
|
||||||
{
|
vTaskDelete(_b_inclinometer_task_handle);
|
||||||
vTaskDelete(_a_inclinometer_task_handle);
|
|
||||||
}
|
|
||||||
if (_b_inclinometer_task_handle != NULL)
|
|
||||||
{
|
|
||||||
vTaskDelete(_b_inclinometer_task_handle);
|
|
||||||
}
|
|
||||||
zh_inclinometer_deinit(&_a_inclinometer_handle);
|
zh_inclinometer_deinit(&_a_inclinometer_handle);
|
||||||
zh_inclinometer_deinit(&_b_inclinometer_handle);
|
zh_inclinometer_deinit(&_b_inclinometer_handle);
|
||||||
break;
|
break;
|
||||||
case MODE_TACHO_TACHO:
|
case MODE_TACHO_TACHO:
|
||||||
if (_a_tachometer_task_handle != NULL)
|
vTaskDelete(_a_tachometer_task_handle);
|
||||||
{
|
vTaskDelete(_b_tachometer_task_handle);
|
||||||
vTaskDelete(_a_tachometer_task_handle);
|
|
||||||
}
|
|
||||||
if (_b_tachometer_task_handle != NULL)
|
|
||||||
{
|
|
||||||
vTaskDelete(_b_tachometer_task_handle);
|
|
||||||
}
|
|
||||||
zh_tachometer_deinit(&_a_tachometer_handle);
|
zh_tachometer_deinit(&_a_tachometer_handle);
|
||||||
zh_tachometer_deinit(&_b_tachometer_handle);
|
zh_tachometer_deinit(&_b_tachometer_handle);
|
||||||
break;
|
break;
|
||||||
case MODE_ANGLE_TACHO:
|
case MODE_ANGLE_TACHO:
|
||||||
if (_a_inclinometer_task_handle != NULL)
|
vTaskDelete(_a_inclinometer_task_handle);
|
||||||
{
|
vTaskDelete(_b_tachometer_task_handle);
|
||||||
vTaskDelete(_a_inclinometer_task_handle);
|
|
||||||
}
|
|
||||||
if (_b_tachometer_task_handle != NULL)
|
|
||||||
{
|
|
||||||
vTaskDelete(_b_tachometer_task_handle);
|
|
||||||
}
|
|
||||||
zh_inclinometer_deinit(&_a_inclinometer_handle);
|
zh_inclinometer_deinit(&_a_inclinometer_handle);
|
||||||
zh_tachometer_deinit(&_b_tachometer_handle);
|
zh_tachometer_deinit(&_b_tachometer_handle);
|
||||||
break;
|
break;
|
||||||
case MODE_REMOTE:
|
case MODE_REMOTE:
|
||||||
|
// To Do.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -549,7 +429,7 @@ static void _zh_encoder_event_handler(void *arg, esp_event_base_t event_base, in
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZH_ENCODER_EVENT:
|
case ZH_ENCODER_EVENT:
|
||||||
zh_encoder_event_on_isr_t *encoder_event = (zh_encoder_event_on_isr_t *)event_data;
|
zh_encoder_event_on_isr_t *encoder_event = event_data;
|
||||||
if (_is_menu_mode == true)
|
if (_is_menu_mode == true)
|
||||||
{
|
{
|
||||||
_current_menu_value = (uint8_t)encoder_event->encoder_position;
|
_current_menu_value = (uint8_t)encoder_event->encoder_position;
|
||||||
@@ -569,14 +449,35 @@ 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)
|
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)
|
static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
(void)arg;
|
|
||||||
(void)event_data;
|
|
||||||
switch (event_id)
|
switch (event_id)
|
||||||
{
|
{
|
||||||
case WIFI_EVENT_STA_START:
|
case WIFI_EVENT_STA_START:
|
||||||
@@ -585,10 +486,7 @@ static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32
|
|||||||
case WIFI_EVENT_STA_DISCONNECTED:
|
case WIFI_EVENT_STA_DISCONNECTED:
|
||||||
if (_is_wifi_connected == true)
|
if (_is_wifi_connected == true)
|
||||||
{
|
{
|
||||||
if (_zh_stats_task_handle != NULL)
|
vTaskDelete(_zh_stats_task_handle);
|
||||||
{
|
|
||||||
vTaskDelete(_zh_stats_task_handle);
|
|
||||||
}
|
|
||||||
zh_syslog_deinit();
|
zh_syslog_deinit();
|
||||||
_is_wifi_connected = false;
|
_is_wifi_connected = false;
|
||||||
}
|
}
|
||||||
@@ -604,20 +502,23 @@ static void _zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32
|
|||||||
zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
|
zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
|
||||||
strcpy(config.syslog_ip, SYSLOG_IP);
|
strcpy(config.syslog_ip, SYSLOG_IP);
|
||||||
zh_syslog_init(&config);
|
zh_syslog_init(&config);
|
||||||
if (_zh_stats_task_handle == NULL)
|
xTaskCreatePinnedToCore(&_zh_stats_processing_task, "stats_processing_task", STATS_TASK_STACK_SIZE, NULL, STATS_TASK_PRIORITY, &_zh_stats_task_handle, tskNO_AFFINITY);
|
||||||
{
|
|
||||||
xTaskCreatePinnedToCore(&_zh_stats_processing_task, "stats_processing_task", STATS_TASK_STACK_SIZE, NULL, STATS_TASK_PRIORITY,
|
|
||||||
&_zh_stats_task_handle, tskNO_AFFINITY);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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)
|
static void _zh_inclinometer_reset_timer_on_alarm_cb(void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
|
||||||
_is_reset_mode = false;
|
_is_reset_mode = false;
|
||||||
_is_reset_occurred = true;
|
_is_reset_occurred = true;
|
||||||
if (_is_menu_mode == false)
|
if (_is_menu_mode == false)
|
||||||
@@ -637,11 +538,17 @@ 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)
|
static void _zh_a_inclinometer_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
(void)pvParameter;
|
float prev_angle = 0, current_angle = 0;
|
||||||
float prev_angle = 0.0;
|
|
||||||
float current_angle = 0.0;
|
|
||||||
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
|
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "A:");
|
zh_160x_print_char(&_lcd_160x_handle, "A:");
|
||||||
@@ -666,11 +573,17 @@ static void _zh_a_inclinometer_processing_task(void *pvParameter)
|
|||||||
vTaskDelete(NULL);
|
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)
|
static void _zh_b_inclinometer_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
(void)pvParameter;
|
float prev_angle = 0, current_angle = 0;
|
||||||
float prev_angle = 0.0;
|
|
||||||
float current_angle = 0.0;
|
|
||||||
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
|
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "B:");
|
zh_160x_print_char(&_lcd_160x_handle, "B:");
|
||||||
@@ -695,11 +608,17 @@ static void _zh_b_inclinometer_processing_task(void *pvParameter)
|
|||||||
vTaskDelete(NULL);
|
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)
|
static void _zh_a_tachometer_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
(void)pvParameter;
|
uint16_t prev_rpm = 0, current_rpm = 0;
|
||||||
uint16_t prev_rpm = 0;
|
|
||||||
uint16_t current_rpm = 0;
|
|
||||||
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
|
zh_160x_set_cursor(&_lcd_160x_handle, 1, 0);
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "A:");
|
zh_160x_print_char(&_lcd_160x_handle, "A:");
|
||||||
@@ -724,11 +643,17 @@ static void _zh_a_tachometer_processing_task(void *pvParameter)
|
|||||||
vTaskDelete(NULL);
|
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)
|
static void _zh_b_tachometer_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
(void)pvParameter;
|
uint16_t prev_rpm = 0, current_rpm = 0;
|
||||||
uint16_t prev_rpm = 0;
|
|
||||||
uint16_t current_rpm = 0;
|
|
||||||
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
||||||
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
|
zh_160x_set_cursor(&_lcd_160x_handle, 1, 8);
|
||||||
zh_160x_print_char(&_lcd_160x_handle, "B:");
|
zh_160x_print_char(&_lcd_160x_handle, "B:");
|
||||||
@@ -753,54 +678,63 @@ static void _zh_b_tachometer_processing_task(void *pvParameter)
|
|||||||
vTaskDelete(NULL);
|
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)
|
static void _zh_stats_processing_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
(void)pvParameter;
|
|
||||||
const esp_app_desc_t *esp_app_desc = esp_app_get_description();
|
const esp_app_desc_t *esp_app_desc = esp_app_get_description();
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char buffer[1024] = {0};
|
char buffer[1024] = {0};
|
||||||
const zh_encoder_stats_t *encoder_stats = zh_encoder_get_stats();
|
const zh_encoder_stats_t *encoder_stats = zh_encoder_get_stats();
|
||||||
const zh_pcf8574_stats_t *pcf8574_stats = zh_pcf8574_get_stats();
|
const zh_pcf8574_stats_t *pcf8574_stats = zh_pcf8574_get_stats();
|
||||||
snprintf(buffer, sizeof(buffer), "A inclinometer task minimum free stack size: %lu.\n", _task_stats.a_inclinometer_task_min_stack_size);
|
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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "B inclinometer task minimum free stack size: %lu.\n", _task_stats.b_inclinometer_task_min_stack_size);
|
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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "A tachometer task minimum free stack size: %lu.\n", _task_stats.a_tachometer_task_min_stack_size);
|
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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "B tachometer task minimum free stack size: %lu.\n", _task_stats.b_tachometer_task_min_stack_size);
|
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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Stats task minimum free stack size: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Encoder task minimum free stack size: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Expander task minimum free stack size: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of event post error: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of i2c driver error: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of event post error: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of vector error: %lu.\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);
|
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);
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
snprintf(buffer, sizeof(buffer), "Number of queue overflow error: %lu.\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);
|
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);
|
vTaskDelay(60000 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
+77
-60
@@ -1,27 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* @file ate0005.h
|
* @file ate0005.h
|
||||||
*
|
*
|
||||||
* @brief Configuration and interface definitions of the ATE0005 universal
|
* @brief Header file for ATE0005 — Universal Optical 2-Channel Angle Gauge / Tachometer
|
||||||
* optical 2-channel angle gauge and tachometer.
|
|
||||||
*
|
*
|
||||||
* This header aggregates the includes required by the application and defines
|
* This header defines task priorities, stack sizes, mode constants,
|
||||||
* the FreeRTOS task priorities and stack sizes, the operating mode constants
|
* WiFi configuration, GPIO pin mappings, and data structures for the
|
||||||
* with the main menu labels, the WiFi access point and station parameters and
|
* ATE0005 device running on ESP32 platform with FreeRTOS.
|
||||||
* the GPIO pin mapping of the peripherals of the ATE0005 device running on the
|
|
||||||
* ESP32 platform.
|
|
||||||
*
|
*
|
||||||
* Key features:
|
* Key features:
|
||||||
* - Task priorities and stack sizes for the inclinometer, tachometer, encoder,
|
* - Task configuration for inclinometer, tachometer, encoder, and stats
|
||||||
* expander and statistics tasks
|
* - WiFi AP/STA mode configuration
|
||||||
* - Operating mode constants and the matching main menu labels
|
* - I2C and GPIO pin definitions for peripherals
|
||||||
* - WiFi access point and station configuration with the remote syslog server
|
* - Mode selection constants for different operating modes
|
||||||
* address
|
* - LCD display and PCF8574 expander macros
|
||||||
* - I2C bus, LCD expander and optical device GPIO pin definitions
|
|
||||||
*
|
|
||||||
* @note The GPIO definitions match the connectors of the ATE0005 board and
|
|
||||||
* must not be changed without a hardware revision.
|
|
||||||
* @note The optical device of each channel is shared by the inclinometer and
|
|
||||||
* the tachometer of the same channel.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -37,62 +28,88 @@
|
|||||||
#include "zh_espnow.h"
|
#include "zh_espnow.h"
|
||||||
#include "zh_syslog.h"
|
#include "zh_syslog.h"
|
||||||
|
|
||||||
#define STATS_TASK_PRIORITY tskIDLE_PRIORITY /*!< Priority of the statistics logging task */
|
#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 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_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 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_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 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_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 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_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 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_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 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_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 EXPANDER_TASK_STACK_SIZE configMINIMAL_STACK_SIZE /*!< Stack size for the PCF8574 expander task. */
|
||||||
|
|
||||||
#define MODE_ANGLE_ANGLE 0 /*!< Operating mode: both inclinometers active */
|
#define TRIAC_ON true /*!< Triac enabled state. */
|
||||||
#define MAIN_MENU_ANGLE_ANGLE_NAME "ANGLE/ANGLE" /*!< Menu label for angle/angle mode */
|
#define TRIAC_OFF false /*!< Triac disabled state. */
|
||||||
#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" /*!< WiFi AP SSID */
|
#define ENCODER_BUTTON_RELEASED true /*!< Encoder button released state. */
|
||||||
#define WIFI_AP_PASS "repairlab" /*!< WiFi AP password */
|
#define ENCODER_BUTTON_PRESSED false /*!< Encoder button pressed state. */
|
||||||
#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" /*!< WiFi STA SSID */
|
#define LED_OFF ZH_PCF8574_GPIO_LOW /*!< LED turned off (low level). */
|
||||||
#define WIFI_STA_PASS "repairlab" /*!< WiFi STA password */
|
#define LED_ON ZH_PCF8574_GPIO_HIGH /*!< LED turned on (high level). */
|
||||||
#define WIFI_STA_MAXIMUM_RETRY 10 /*!< Maximum number of reconnection attempts for STA mode */
|
|
||||||
|
|
||||||
#define SYSLOG_IP "77.51.201.8" /*!< IP address of the remote syslog server */
|
#define RELAY_OFF ZH_PCF8574_GPIO_LOW /*!< Relay disconnected (low level). */
|
||||||
|
#define RELAY_ON ZH_PCF8574_GPIO_HIGH /*!< Relay connected (high level). */
|
||||||
|
|
||||||
#define I2C_SCL_GPIO GPIO_NUM_22 /*!< I2C clock line GPIO */
|
#define MODE_ANGLE_ANGLE 0 /*!< Operating mode: both inclinometers active. */
|
||||||
#define I2C_SDA_GPIO GPIO_NUM_21 /*!< I2C data line GPIO */
|
#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 LCD_I2C_ADDRESS 0x20 /*!< I2C address of the LCD PCF8574 expander (U2) */
|
#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 MODE_ENCODER_NUMBER 1 /*!< Encoder instance number (Connector EN1) */
|
#define WIFI_STA_SSID "rick_professor" /*!< WiFi STA SSID. */
|
||||||
#define MODE_ENCODER_A_GPIO GPIO_NUM_27 /*!< Mode encoder A-phase GPIO */
|
#define WIFI_STA_PASS "repairlab" /*!< WiFi STA password. */
|
||||||
#define MODE_ENCODER_B_GPIO GPIO_NUM_26 /*!< Mode encoder B-phase GPIO */
|
#define WIFI_STA_MAXIMUM_RETRY 10 /*!< Maximum number of reconnection attempts for STA mode. */
|
||||||
#define MODE_ENCODER_S_GPIO GPIO_NUM_25 /*!< Mode encoder switch GPIO */
|
|
||||||
|
|
||||||
#define A_OPTICAL_DEVICE_A_GPIO GPIO_NUM_14 /*!< Optical device A-phase GPIO for channel A (Connector EN2) */
|
#define SYSLOG_IP "77.51.201.8" /*!< IP address of the remote syslog server. */
|
||||||
#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 /*!< Optical device A-phase GPIO for channel B (Connector EN3) */
|
#define I2C_SCL_GPIO GPIO_NUM_22 /*!< I2C clock line GPIO. */
|
||||||
#define B_OPTICAL_DEVICE_B_GPIO GPIO_NUM_19 /*!< Optical device B-phase GPIO for channel B */
|
#define I2C_SDA_GPIO GPIO_NUM_21 /*!< I2C data line GPIO. */
|
||||||
|
|
||||||
|
#define LCD_I2C_ADDRESS 0x20 /*!< I2C address of the LCD PCF8574 expander (U2). */
|
||||||
|
|
||||||
|
#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 /*!< 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 /*!< 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
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#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; /*!< 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
description: Universal Optical 2-Channel Angle Gauge / Tachometer
|
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/ate0005
|
url: https://github.com/aZholtikov/ate0002
|
||||||
version: "1.1.2"
|
version: "1.2.0"
|
||||||
license: "Apache-2.0"
|
license: "Apache-2.0"
|
||||||
maintainers:
|
maintainers:
|
||||||
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
|
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
|
||||||
|
|||||||
+15
-2
@@ -1,8 +1,21 @@
|
|||||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||||
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y
|
||||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
||||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_GPTIMER_ISR_CACHE_SAFE=y
|
||||||
|
CONFIG_I2C_ISR_IRAM_SAFE=y
|
||||||
|
CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_PCNT_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_PCNT_ISR_IRAM_SAFE=y
|
||||||
|
CONFIG_RMT_RECV_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_RMT_TX_ISR_CACHE_SAFE=y
|
||||||
|
CONFIG_RMT_RX_ISR_CACHE_SAFE=y
|
||||||
|
CONFIG_SPI_MASTER_ISR_IN_IRAM=y
|
||||||
|
CONFIG_UART_ISR_IN_IRAM=y
|
||||||
|
CONFIG_HTTPD_WS_SUPPORT=y
|
||||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||||
CONFIG_FREERTOS_HZ=1000
|
CONFIG_FREERTOS_HZ=1000
|
||||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
|
||||||
-337
@@ -1,337 +0,0 @@
|
|||||||
# Описание компонента ate0005
|
|
||||||
|
|
||||||
## Общие сведения
|
|
||||||
|
|
||||||
**ATE0005** — это прошивка ESP32 для ESP-IDF, реализующая универсальный оптический двухканальный угломер/тахометр.
|
|
||||||
|
|
||||||
Приложение измеряет угол поворота оси и/или скорость её вращения по двум независимым каналам A и B, подключённым к оптическим энкодерам, и выводит результат на LCD-экран 16x2. Управление приборами осуществляется поворотным энкодером MODE с кнопкой.
|
|
||||||
|
|
||||||
### Основные возможности
|
|
||||||
|
|
||||||
1. **Два независимых канала** — каналы A и B работают с собственными оптическими энкодерами.
|
|
||||||
2. **Четыре режима работы** — `ANGLE/ANGLE`, `TACHO/TACHO`, `ANGLE/TACHO`, `REMOTE`.
|
|
||||||
3. **Измерение угла** — текущий угол с разрешением 0.1°, сброс длительным нажатием кнопки.
|
|
||||||
4. **Измерение скорости** — обороты в минуту (RPM) с обновлением каждые 100 мс.
|
|
||||||
5. **LCD 16x2 через PCF8574** — заставка, анимированный прогресс-бар загрузки, живой вывод значений.
|
|
||||||
6. **Меню на поворотном энкодере** — выбор режима поворотом, вход/выход коротким нажатием.
|
|
||||||
7. **WiFi AP+STA** — точка доступа для локального доступа и подключение к внешней сети.
|
|
||||||
8. **OTA-обновления** — встроенный веб-сервер с самопроверкой образа и откатом (rollback).
|
|
||||||
9. **Удалённый syslog** — периодическая отправка статистики задач и счётчиков ошибок драйверов.
|
|
||||||
10. **FreeRTOS-многозадачность** — отдельные задачи на каждый канал, энкодер, расширитель и статистику.
|
|
||||||
|
|
||||||
## Архитектура прошивки
|
|
||||||
|
|
||||||
Приложение состоит из единственного исходного файла `main/ate0005.c` и заголовка конфигурации `main/ate0005.h`. Вся прикладная логика построена вокруг компонентов проекта (`zh_*`), которые выполняют непосредственную работу с железом; `ate0005.c` отвечает за инициализацию, коммутацию режимов и вывод.
|
|
||||||
|
|
||||||
Точка входа `app_main()` выполняет последовательную инициализацию подсистем:
|
|
||||||
|
|
||||||
1. `nvs_flash_init()` с очисткой и повторной инициализацией при пустом разделе или несовпадении версии.
|
|
||||||
2. `esp_event_loop_create_default()` — общая петля событий.
|
|
||||||
3. `_zh_pcf8574_init()` — шина I2C, расширитель PCF8574, драйвер LCD, мьютекс экрана.
|
|
||||||
4. `_zh_system_load()` — заставка и прогресс-бар загрузки.
|
|
||||||
5. `_zh_encoder_init()` — энкодер выбора режима с обработчиком событий.
|
|
||||||
6. `_zh_timer_init()` — программный таймер сброса угла.
|
|
||||||
7. `_zh_a_inclinometer_init()`, `_zh_b_inclinometer_init()` — угломеры каналов A и B.
|
|
||||||
8. `_zh_wifi_init()` — WiFi в режиме AP+STA.
|
|
||||||
9. `_zh_webserver_init()` — HTTP-сервер с OTA-страницей.
|
|
||||||
10. `_zh_espnow_init()` — заготовка под ESP-NOW (пока пуста).
|
|
||||||
|
|
||||||
После инициализации выполняется самопроверка OTA: если запущенный образ находится в состоянии `ESP_OTA_IMG_PENDING_VERIFY`, задача ждёт 60 секунд и помечает образ валидным через `esp_ota_mark_app_valid_cancel_rollback()`.
|
|
||||||
|
|
||||||
### Задачи FreeRTOS
|
|
||||||
|
|
||||||
| Задача | Приоритет | Стек, слов | Назначение |
|
|
||||||
| -------- | ----------- | ------------ | ------------ |
|
|
||||||
| `_zh_a_inclinometer_processing_task` | `tskIDLE_PRIORITY + 5` | `configMINIMAL_STACK_SIZE + 512` | Вывод угла канала A |
|
|
||||||
| `_zh_b_inclinometer_processing_task` | `tskIDLE_PRIORITY + 5` | `configMINIMAL_STACK_SIZE + 512` | Вывод угла канала B |
|
|
||||||
| `_zh_a_tachometer_processing_task` | `tskIDLE_PRIORITY + 5` | `configMINIMAL_STACK_SIZE + 512` | Вывод RPM канала A |
|
|
||||||
| `_zh_b_tachometer_processing_task` | `tskIDLE_PRIORITY + 5` | `configMINIMAL_STACK_SIZE + 512` | Вывод RPM канала B |
|
|
||||||
| `_zh_encoder_event_handler` | обрабатывается в задаче `zh_encoder` | — | События энкодера и кнопки |
|
|
||||||
| `_zh_pcf8574_*` (внутри `zh_pcf8574`) | `tskIDLE_PRIORITY + 4` | `configMINIMAL_STACK_SIZE` | Обмен с расширителем LCD |
|
|
||||||
| `_zh_stats_processing_task` | `tskIDLE_PRIORITY` | `configMINIMAL_STACK_SIZE + 1280` | Отправка статистики в syslog |
|
|
||||||
|
|
||||||
Все задачи создаются через `xTaskCreatePinnedToCore()` с `tskNO_AFFINITY`, то есть планировщик сам выбирает ядро.
|
|
||||||
|
|
||||||
## Внутреннее устройство
|
|
||||||
|
|
||||||
### Структура `_task_stats_t`
|
|
||||||
|
|
||||||
Хранит минимальный свободный объём стека (high water mark) каждой измерительной задачи. Статистическая задача читает эти значения для отправки в syslog.
|
|
||||||
|
|
||||||
| Поле | Тип | Описание |
|
|
||||||
| ------ | ----- | ---------- |
|
|
||||||
| `a_inclinometer_task_min_stack_size` | `uint32_t` | Минимальный свободный стек задачи угломера A |
|
|
||||||
| `b_inclinometer_task_min_stack_size` | `uint32_t` | Минимальный свободный стек задачи угломера B |
|
|
||||||
| `a_tachometer_task_min_stack_size` | `uint32_t` | Минимальный свободный стек задачи тахометра A |
|
|
||||||
| `b_tachometer_task_min_stack_size` | `uint32_t` | Минимальный свободный стек задачи тахометра B |
|
|
||||||
|
|
||||||
Статический экземпляр `_task_stats` инициализируется полными размерами стеков, чтобы до первого замера статистика не давала ложных предупреждений.
|
|
||||||
|
|
||||||
### Глобальные дескрипторы и флаги
|
|
||||||
|
|
||||||
| Переменная | Тип | Описание |
|
|
||||||
| ------------ | ----- | ---------- |
|
|
||||||
| `_http_server_handle` | `httpd_handle_t` | Дескриптор HTTP-сервера |
|
|
||||||
| `_lcd_handle` | `zh_pcf8574_handle_t *` | Расширитель PCF8574 |
|
|
||||||
| `_lcd_160x_handle` | `zh_160x_i2c_handle_t *` | Драйвер LCD 16x2 |
|
|
||||||
| `_mode_encoder_handle` | `zh_encoder_handle_t *` | Энкодер выбора режима |
|
|
||||||
| `_a_inclinometer_handle`, `_b_inclinometer_handle` | `zh_inclinometer_handle_t *` | Угломеры каналов A/B |
|
|
||||||
| `_a_tachometer_handle`, `_b_tachometer_handle` | `zh_tachometer_handle_t *` | Тахометры каналов A/B |
|
|
||||||
| `_inclinometer_reset_timer_handle` | `esp_timer_handle_t` | Таймер сброса угла |
|
|
||||||
| `_lcd_mutex` | `SemaphoreHandle_t` | Мьютекс доступа к LCD |
|
|
||||||
| `_is_menu_mode` | `volatile bool` | Флаг нахождения в меню выбора режима |
|
|
||||||
| `_is_reset_mode` | `volatile bool` | Флаг нажатия кнопки (идёт отсчёт долгого нажатия) |
|
|
||||||
| `_is_reset_occurred` | `volatile bool` | Признак того, что сброс уже выполнен, чтобы отпустить кнопку «вхолостую» |
|
|
||||||
| `_current_menu_value` | `volatile uint8_t` | Индекс выбранного режима |
|
|
||||||
| `_wifi_reconnect_retry_num` | `volatile uint8_t` | Счётчик попыток переподключения STA |
|
|
||||||
| `_is_wifi_connected` | `volatile bool` | Флаг подключения STA |
|
|
||||||
|
|
||||||
Все разделяемые между задачами и обработчиками прерываний флаги объявлены `volatile`, поскольку изменяются из разных контекстов без блокировок.
|
|
||||||
|
|
||||||
### Константы конфигурации
|
|
||||||
|
|
||||||
Режимы работы заданы константами и подписями меню:
|
|
||||||
|
|
||||||
| Константа | Значение | Подпись меню |
|
|
||||||
| ----------- | ---------- | -------------- |
|
|
||||||
| `MODE_ANGLE_ANGLE` | `0` | `"ANGLE/ANGLE"` |
|
|
||||||
| `MODE_TACHO_TACHO` | `1` | `"TACHO/TACHO"` |
|
|
||||||
| `MODE_ANGLE_TACHO` | `2` | `"ANGLE/TACHO"` |
|
|
||||||
| `MODE_REMOTE` | `3` | `"REMOTE"` |
|
|
||||||
|
|
||||||
Массив `_main_menu[]` хранит подписи в порядке их индексов; энкодер режима ограничен диапазоном `0 .. sizeof(_main_menu)/sizeof(_main_menu[0]) - 1`.
|
|
||||||
|
|
||||||
### Распиновка GPIO
|
|
||||||
|
|
||||||
| Периферия | Вывод A | Вывод B | Вывод S | Примечание |
|
|
||||||
| ----------- | --------- | --------- | --------- | ------------ |
|
|
||||||
| I2C-шина | `SDA = GPIO_NUM_21` | `SCL = GPIO_NUM_22` | — | Внутренние подтяжки включены |
|
|
||||||
| Расширитель LCD | адрес `0x20` | — | — | PCF8574 (U2) |
|
|
||||||
| Энкодер MODE | `GPIO_NUM_27` | `GPIO_NUM_26` | `GPIO_NUM_25` | Разъём EN1, экземпляр `1` |
|
|
||||||
| Оптический энкодер A | `GPIO_NUM_14` | `GPIO_NUM_13` | — | Разъём EN2 |
|
|
||||||
| Оптический энкодер B | `GPIO_NUM_18` | `GPIO_NUM_19` | — | Разъём EN3 |
|
|
||||||
|
|
||||||
Оптическое устройство каждого канала общее для угломера и тахометра этого канала: они не могут работать одновременно на одном канале.
|
|
||||||
|
|
||||||
## API (интерфейс)
|
|
||||||
|
|
||||||
Прикладной код `ate0005.c` не экспортирует публичных функций — это законченное приложение, а не библиотека. Все функции модуля объявлены `static` и вызываются только из `app_main()` и обработчиков событий. Ниже приведена их роль как внутренняя справка.
|
|
||||||
|
|
||||||
### Инициализация подсистем
|
|
||||||
|
|
||||||
| Функция | Описание |
|
|
||||||
| --------- | ---------- |
|
|
||||||
| `app_main()` | Точка входа: NVS, событийная петля, LCD, энкодер, таймер, угломеры, WiFi, веб-сервер, ESP-NOW, самопроверка OTA. |
|
|
||||||
| `_zh_pcf8574_init()` | Создаёт шину I2C, инициализирует PCF8574 и LCD 16x2, создаёт мьютекс экрана. |
|
|
||||||
| `_zh_encoder_init()` | Регистрирует обработчик энкодера, задаёт GPIO, диапазон и шаг, сбрасывает позицию в 0. |
|
|
||||||
| `_zh_timer_init()` | Создаёт одноразовый программный таймер сброса угла. |
|
|
||||||
| `_zh_a_inclinometer_init()` | Инициализирует угломер канала A и создаёт его задачу. |
|
|
||||||
| `_zh_b_inclinometer_init()` | Инициализирует угломер канала B и создаёт его задачу. |
|
|
||||||
| `_zh_a_tachometer_init()` | Инициализирует тахометр канала A и создаёт его задачу. |
|
|
||||||
| `_zh_b_tachometer_init()` | Инициализирует тахометр канала B и создаёт его задачу. |
|
|
||||||
| `_zh_wifi_init()` | Настраивает AP и STA, регистрирует обработчики событий WiFi/IP, запускает WiFi. |
|
|
||||||
| `_zh_webserver_init()` | Запускает HTTP-сервер и регистрирует OTA-страницу на `"/"`. |
|
|
||||||
| `_zh_espnow_init()` | Заготовка под ESP-NOW, тело пусто. |
|
|
||||||
|
|
||||||
### Обработчики событий
|
|
||||||
|
|
||||||
| Функция | Описание |
|
|
||||||
| --------- | ---------- |
|
|
||||||
| `_zh_encoder_event_handler()` | Обрабатывает нажатие/отпускание кнопки и поворот энкодера: переключение меню, сброс угла. |
|
|
||||||
| `_zh_wifi_event_handler()` | Подключение STA, переподключение с ограничением попыток, инициализация syslog и создание задачи статистики при получении IP. |
|
|
||||||
| `_zh_espnow_event_handler()` | Заготовка под ESP-NOW, тело пусто. |
|
|
||||||
| `_zh_inclinometer_reset_timer_on_alarm_cb()` | Колбэк долгого нажатия: сбрасывает угол активных угломеров текущего режима. |
|
|
||||||
|
|
||||||
### Задачи обработки и вывода
|
|
||||||
|
|
||||||
| Функция | Описание |
|
|
||||||
| --------- | ---------- |
|
|
||||||
| `_zh_a_inclinometer_processing_task()` | Выводит угол канала A на 2-ю строку LCD с 1 знаком после запятой. |
|
|
||||||
| `_zh_b_inclinometer_processing_task()` | Выводит угол канала B на 2-ю строку LCD с 1 знаком после запятой. |
|
|
||||||
| `_zh_a_tachometer_processing_task()` | Выводит RPM канала A на 2-ю строку LCD. |
|
|
||||||
| `_zh_b_tachometer_processing_task()` | Выводит RPM канала B на 2-ю строку LCD. |
|
|
||||||
| `_zh_stats_processing_task()` | Периодически отправляет свободный стек задач и счётчики ошибок в syslog. |
|
|
||||||
|
|
||||||
### Служебные функции вывода
|
|
||||||
|
|
||||||
| Функция | Описание |
|
|
||||||
|---------|----------|
|
|
||||||
| `_zh_system_load()` | Показывает заставку с названием и версией, затем прогресс-бар загрузки. |
|
|
||||||
|
|
||||||
## Потокобезопасность
|
|
||||||
|
|
||||||
1. **Мьютекс `_lcd_mutex`** — защищает последовательности обращений к LCD (`set_cursor` → `print_*`) от одновременного доступа нескольких задач. Захватывается на время вывода значения и освобождается сразу после.
|
|
||||||
2. **`volatile` флаги состояния** — `_is_menu_mode`, `_is_reset_mode`, `_is_reset_occurred`, `_current_menu_value` изменяются из обработчика событий энкодера и читаются из задач и колбэка таймера; прямой доступ без блокировки допустим для однобайтовых флагов.
|
|
||||||
3. **`_task_stats`** — каждая измерительная задача пишет только своё поле, читатель (статистическая задача) только читает; гонка исключена за счёт разделения полей.
|
|
||||||
4. **Событийная модель `zh_encoder`** — прерывания энкодера постят события в очередь, обработка идёт в задаче компонента, а не в ISR.
|
|
||||||
|
|
||||||
## Алгоритмы
|
|
||||||
|
|
||||||
### Переключение режима работы
|
|
||||||
|
|
||||||
1. Короткое нажатие кнопки энкодера инвертирует `_is_menu_mode`.
|
|
||||||
2. При входе в меню текущие активные устройства режима останавливаются: удаляются соответствующие задачи через `vTaskDelete()`, затем вызываются `zh_inclinometer_deinit()` / `zh_tachometer_deinit()`.
|
|
||||||
3. Включается курсор LCD, очищается 2-я строка, на 1-й отображается текущая позиция меню.
|
|
||||||
4. При выходе из меню курсор выключается, инициализируются устройства выбранного режима и запускаются их задачи.
|
|
||||||
5. Поворот энкодера в меню обновляет `_current_menu_value` и подпись режима; вне меню позиция энкодера принудительно возвращается к `_current_menu_value` через `zh_encoder_set()`.
|
|
||||||
|
|
||||||
### Сброс угла долгим нажатием
|
|
||||||
|
|
||||||
1. При нажатии кнопки запускается одноразовый таймер на 2 секунды и выставляется `_is_reset_mode`.
|
|
||||||
2. Если кнопка отпущена раньше 2 секунд, таймер останавливается, событие считается коротким нажатием (переключение меню).
|
|
||||||
3. Если таймер сработал, колбэк выставляет `_is_reset_occurred` и вызывает `zh_inclinometer_reset()` для активных угломеров режима (в `ANGLE/ANGLE` — оба, в `ANGLE/TACHO` — только A).
|
|
||||||
4. Последующее отпускание кнопки игнорируется, если сброс уже произошёл (`_is_reset_occurred == true`).
|
|
||||||
|
|
||||||
### Обновление значения на LCD
|
|
||||||
|
|
||||||
1. Задача кэширует предыдущее показание (`prev_angle` / `prev_rpm`).
|
|
||||||
2. Каждые 100 мс читается новое значение через `zh_inclinometer_get()` / `zh_tachometer_get()`.
|
|
||||||
3. Значение перерисовывается только при изменении: сначала область значения затишается пробелами, затем печатается новое число. Это снижает нагрузку на I2C и мигание экрана.
|
|
||||||
|
|
||||||
### Статистика и syslog
|
|
||||||
|
|
||||||
1. Задача статистики создаётся только после получения STA-адреса (`IP_EVENT_STA_GOT_IP`).
|
|
||||||
2. Каждые ~60 секунд (с промежуточными задержками по 500 мс) отправляются сообщения о свободном стеке каждой задачи и о счётчиках ошибок `zh_encoder` и `zh_pcf8574`.
|
|
||||||
3. Уровень `ZH_WARNING` ставится, если свободный стек задачи меньше 100 слов или счётчик ошибок ненулевой; иначе `ZH_INFO`.
|
|
||||||
4. При разрыве WiFi задача статистики удаляется, syslog деинициализируется.
|
|
||||||
|
|
||||||
## Примеры использования
|
|
||||||
|
|
||||||
### Инициализация угломера канала
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_inclinometer.h"
|
|
||||||
|
|
||||||
static zh_inclinometer_handle_t *_a_inclinometer_handle = NULL;
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT();
|
|
||||||
config.a_gpio_number = A_OPTICAL_DEVICE_A_GPIO; // GPIO_NUM_14
|
|
||||||
config.b_gpio_number = A_OPTICAL_DEVICE_B_GPIO; // GPIO_NUM_13
|
|
||||||
config.encoder_pulses = 3600; // импульсов на оборот
|
|
||||||
zh_inclinometer_init(&config, &_a_inclinometer_handle);
|
|
||||||
// ... использование ...
|
|
||||||
zh_inclinometer_deinit(&_a_inclinometer_handle); // освобождение ресурсов
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Вывод угла на LCD под мьютексом
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_160x_i2c.h"
|
|
||||||
|
|
||||||
static zh_160x_i2c_handle_t *_lcd_160x_handle = NULL;
|
|
||||||
static SemaphoreHandle_t _lcd_mutex = NULL;
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
_lcd_mutex = xSemaphoreCreateMutex();
|
|
||||||
float current_angle = 0.0;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// читаем угол и выводим только при изменении
|
|
||||||
zh_inclinometer_get(&_a_inclinometer_handle, ¤t_angle);
|
|
||||||
xSemaphoreTake(_lcd_mutex, portMAX_DELAY);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
vSemaphoreDelete(_lcd_mutex);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Инициализация удалённого syslog
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_syslog.h"
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
|
|
||||||
strcpy(config.syslog_ip, SYSLOG_IP); // адрес сервера из ate0005.h
|
|
||||||
esp_err_t err = zh_syslog_init(&config);
|
|
||||||
if (err != ESP_OK)
|
|
||||||
{
|
|
||||||
printf("Syslog initialization error %s\n", esp_err_to_name(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zh_syslog_send(ZH_KERN, ZH_INFO, "ATE0005", "zh_syslog", "Stats task started.\n");
|
|
||||||
zh_syslog_deinit(); // освобождение ресурсов
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Конфигурация сборки
|
|
||||||
|
|
||||||
Прошивка опирается на следующие параметры `sdkconfig.defaults`:
|
|
||||||
|
|
||||||
| Параметр | Значение | Назначение |
|
|
||||||
| ---------- | ---------- | ------------ |
|
|
||||||
| `CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE` | `y` | Разрешает OTA-откат при непройденной самопроверке |
|
|
||||||
| `CONFIG_BOOTLOADER_LOG_LEVEL_NONE` | `y` | Отключает лог загрузчика |
|
|
||||||
| `CONFIG_ESPTOOLPY_FLASHSIZE_8MB` | `8MB` | Размер флеш под две OTA-партиции |
|
|
||||||
| `CONFIG_PARTITION_TABLE_CUSTOM` | `y` | Используется `partitions.csv` |
|
|
||||||
| `CONFIG_COMPILER_OPTIMIZATION_SIZE` | `y` | Оптимизация по размеру |
|
|
||||||
| `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240` | `240` | Максимальная частота CPU |
|
|
||||||
| `CONFIG_FREERTOS_HZ` | `1000` | Тик 1 мс для точных задержек |
|
|
||||||
| `CONFIG_LOG_DEFAULT_LEVEL_NONE` | `y` | Отключает ESP-IDF лог по умолчанию |
|
|
||||||
|
|
||||||
### Партиции (`partitions.csv`)
|
|
||||||
|
|
||||||
| Имя | Тип | Подтип | Размер | Назначение |
|
|
||||||
| ----- | ----- | -------- | -------- | ------------ |
|
|
||||||
| `nvs` | data | nvs | `0x6000` | Хранилище настроек |
|
|
||||||
| `otadata` | data | ota | `0x2000` | Состояние OTA |
|
|
||||||
| `phy_init` | data | phy | `0x1000` | Калибровка PHY |
|
|
||||||
| `ota_0` | app | ota_0 | 3500K | Образ прошивки A |
|
|
||||||
| `ota_1` | app | ota_1 | 3500K | Образ прошивки B |
|
|
||||||
| `storage` | data | fat | 1M | Файловое хранилище |
|
|
||||||
|
|
||||||
## Ошибки
|
|
||||||
|
|
||||||
Прикладной код не возвращает пользовательских кодов ошибок и полагается на коды ESP-IDF, транслируемые компонентами `zh_*`. Ниже типовые коды, встречающиеся в путях инициализации.
|
|
||||||
|
|
||||||
| Код | Описание |
|
|
||||||
| ----- | ---------- |
|
|
||||||
| `ESP_OK` | Операция выполнена успешно |
|
|
||||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент функции (например, нулевой дескриптор) |
|
|
||||||
| `ESP_ERR_NO_MEM` | Недостаточно памяти |
|
|
||||||
| `ESP_ERR_NOT_FOUND` | Устройство или партиция не найдены |
|
|
||||||
| `ESP_ERR_TIMEOUT` | Таймаут операции (I2C, WiFi) |
|
|
||||||
| `ESP_ERR_NVS_NO_FREE_PAGES` | Раздел NVS переполнен (требует очистки) |
|
|
||||||
| `ESP_ERR_NVS_NEW_VERSION_FOUND` | Версия формата NVS не совпадает (требует очистки) |
|
|
||||||
|
|
||||||
## Ограничения
|
|
||||||
|
|
||||||
1. **Режим `REMOTE` не реализован** — функции `_zh_espnow_init()` и `_zh_espnow_event_handler()` имеют пустое тело, поэтому в этом режиме на экране выводится `NOT CONNECTED`.
|
|
||||||
2. **Общее оптическое устройство на канал** — угломер и тахометр одного канала используют одни GPIO, поэтому не могут работать одновременно; при переключении режима предыдущие устройства деинициализируются.
|
|
||||||
3. **WiFi-кредиты зашиты в прошивку** — `WIFI_STA_SSID` и `WIFI_STA_PASS` заданы константами в `ate0005.h` и требуют пересборки для смены сети.
|
|
||||||
4. **Жёсткая распиновка GPIO** — номера выводов соответствуют разъёмам платы ATE0005 и не должны меняться без аппаратной ревизии.
|
|
||||||
5. **Самопроверка OTA блокирует на 60 секунд** — при первом запуске после OTA-обновления `app_main()` выдерживает задержку перед подтверждением образа.
|
|
||||||
6. **Порог предупреждения стека — 100 слов** — статистика выдаёт `ZH_WARNING` при свободном стеке ниже 100 слов; значение подобрано под конкретные размеры стеков задач.
|
|
||||||
|
|
||||||
## Требования для сборки
|
|
||||||
|
|
||||||
- ESP-IDF версии 5.0 или выше.
|
|
||||||
- FreeRTOS (входит в ESP-IDF).
|
|
||||||
- Компоненты проекта: `zh_160x_i2c`, `zh_encoder`, `zh_inclinometer`, `zh_tachometer`, `zh_espnow`, `zh_ota_server`, `zh_syslog`.
|
|
||||||
- Встроенные компоненты ESP-IDF: `esp_wifi`, `nvs_flash`, `esp_timer`, `esp_app_format`.
|
|
||||||
- Флеш-память 8 МБ под две OTA-партиции по 3500K.
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd your_projects_folder
|
|
||||||
git clone https://github.com/aZholtikov/ate0005
|
|
||||||
cd ate0005
|
|
||||||
idf.py set-target esp32
|
|
||||||
idf.py build
|
|
||||||
idf.py -p /dev/ttyUSB0 flash monitor
|
|
||||||
```
|
|
||||||
|
|
||||||
Зависимости-компоненты подтягиваются автоматически из `main/idf_component.yml` при первой сборке. Для подключения в собственном коде используется единый заголовок конфигурации:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "ate0005.h"
|
|
||||||
```
|
|
||||||
Reference in new issue
Block a user