diff --git a/src/relay_chn.c b/src/relay_chn.c index 1ad6f8f..917f4e4 100644 --- a/src/relay_chn.c +++ b/src/relay_chn.c @@ -110,9 +110,13 @@ typedef struct relay_chn_type { * 100 / (RELAY_CHN_TILT_RUN_MAX_MS - RELAY_CHN_TILT_RUN_MIN_MS) ) /// @} + +ESP_EVENT_DEFINE_BASE(RELAY_CHN_TILT_CMD_EVENT_BASE); + /// @brief Tilt commands. enum relay_chn_tilt_cmd_enum { RELAY_CHN_TILT_CMD_NONE, ///< No command. + RELAY_CHN_TILT_CMD_STOP, ///< Tilt command stop. RELAY_CHN_TILT_CMD_FORWARD, ///< Tilt command for forward. RELAY_CHN_TILT_CMD_REVERSE ///< Tilt command for reverse. }; @@ -122,9 +126,10 @@ typedef enum relay_chn_tilt_cmd_enum relay_chn_tilt_cmd_t; /// @brief Tilt steps. enum relay_chn_tilt_step_enum { - RELAY_CHN_TILT_STEP_NONE, ///< No step. - RELAY_CHN_TILT_STEP_RUN, ///< Run step. Tilt is either driving for forward or reverse. - RELAY_CHN_TILT_STEP_PAUSE ///< Pause step. Tilt is paused. + RELAY_CHN_TILT_STEP_NONE, ///< No step. + RELAY_CHN_TILT_STEP_PENDING, ///< Pending step. + RELAY_CHN_TILT_STEP_MOVE, ///< Move step. Tilt is driving either for forward or reverse. + RELAY_CHN_TILT_STEP_PAUSE ///< Pause step. Tilt is paused. }; /// @brief Alias for the enum relay_chn_tilt_step_enum. @@ -132,14 +137,14 @@ typedef enum relay_chn_tilt_step_enum relay_chn_tilt_step_t; /// @brief Tilt timing structure to manage tilt pattern timing. typedef struct relay_chn_tilt_timing_struct { - uint8_t sensitivity; ///< Tilt sensitivity in percent value (%). - uint32_t run_time_ms; ///< Run time in milliseconds. + uint8_t sensitivity; ///< Tilt sensitivity in percentage (%). + uint32_t move_time_ms; ///< Move time in milliseconds. uint32_t pause_time_ms; ///< Pause time in milliseconds. } relay_chn_tilt_timing_t; /// @brief Tilt control structure to manage tilt operations. typedef struct relay_chn_tilt_control_struct { - relay_chn_tilt_cmd_t cmd; ///< Current tilt command. + relay_chn_tilt_cmd_t cmd; ///< The tilt command in process. relay_chn_tilt_step_t step; ///< Current tilt step. relay_chn_tilt_timing_t tilt_timing; ///< Tilt timing structure. esp_timer_handle_t tilt_timer; ///< Tilt timer handle. @@ -155,13 +160,11 @@ typedef struct relay_chn_type { relay_chn_output_t output; ///< Output configuration of the relay channel. relay_chn_cmd_t pending_cmd; ///< The command that is pending to be issued esp_timer_handle_t inertia_timer; ///< Timer to handle the opposite direction inertia time. - relay_chn_tilt_control_t tilt_control; + relay_chn_tilt_control_t tilt_control; ///< Tilt control block. } relay_chn_t; static esp_err_t relay_chn_init_tilt_control(relay_chn_t *relay_chn); -static void relay_chn_tilt_state_handler(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state); - -static uint32_t relay_chn_tilting_channels; +static esp_err_t relay_chn_tilt_init(void); #endif // RELAY_CHN_ENABLE_TILTING @@ -338,13 +341,14 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count) } } -#if RELAY_CHN_ENABLE_TILTING == 1 - relay_chn_tilting_channels = 0; -#endif - // Create relay channel command event loop ret |= relay_chn_create_event_loop(); +#if RELAY_CHN_ENABLE_TILTING == 1 + // Must call after the event loop is initialized + ret |= relay_chn_tilt_init(); // Initialize tilt feature +#endif + // Init the state listener manager relay_chn_state_listener_manager.listeners = malloc(sizeof(relay_chn_state_listener_t*)); if (relay_chn_state_listener_manager.listeners == NULL) { @@ -473,17 +477,6 @@ static void relay_chn_update_state(relay_chn_t *relay_chn, relay_chn_state_t new relay_chn_state_t old = relay_chn->state; relay_chn->state = new_state; -#if RELAY_CHN_ENABLE_TILTING == 1 - if (relay_chn->tilt_control.cmd != RELAY_CHN_TILT_CMD_NONE) { - // The channel is tilting, pipe the internal state to the tilt state handler - // unless the state sent from the tilt module - if (relay_chn->state != RELAY_CHN_STATE_TILT_FORWARD && relay_chn->state != RELAY_CHN_STATE_TILT_REVERSE) { - relay_chn_tilt_state_handler(relay_chn->id, old, new_state); - return; - } - } -#endif - for (uint8_t i = 0; i < relay_chn_state_listener_manager.listener_count; i++) { relay_chn_state_listener_t listener = relay_chn_state_listener_manager.listeners[i]; if (listener == NULL) { @@ -688,47 +681,73 @@ relay_chn_direction_t relay_chn_get_direction(uint8_t chn_id) } /* relay_chn APIs */ +static esp_err_t relay_chn_output_stop(relay_chn_t *relay_chn) +{ + esp_err_t ret; + ret = gpio_set_level(relay_chn->output.forward_pin, 0); + ret |= gpio_set_level(relay_chn->output.reverse_pin, 0); + return ret; +} + +static esp_err_t relay_chn_output_forward(relay_chn_t *relay_chn) +{ + esp_err_t ret; + ret = gpio_set_level(relay_chn->output.forward_pin, 1); + ret |= gpio_set_level(relay_chn->output.reverse_pin, 0); + return ret; +} + +static esp_err_t relay_chn_output_reverse(relay_chn_t *relay_chn) +{ + esp_err_t ret; + ret = gpio_set_level(relay_chn->output.forward_pin, 0); + ret |= gpio_set_level(relay_chn->output.reverse_pin, 1); + return ret; +} static void relay_chn_execute_stop(relay_chn_t *relay_chn) { - gpio_set_level(relay_chn->output.forward_pin, 0); - gpio_set_level(relay_chn->output.reverse_pin, 0); + if (relay_chn_output_stop(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_execute_stop: Failed to output stop for relay channel #%d!", relay_chn->id); + } + relay_chn_state_t previous_state = relay_chn->state; relay_chn_update_state(relay_chn, RELAY_CHN_STATE_STOPPED); - -#if RELAY_CHN_ENABLE_TILTING == 1 - // Just stop and update state if tilting is active - if (relay_chn->tilt_control.cmd != RELAY_CHN_TILT_CMD_NONE) return; -#endif + // If there is any pending command, cancel it since the STOP command is issued right after it relay_chn->pending_cmd = RELAY_CHN_CMD_NONE; // Invalidate the channel's timer if it is active esp_timer_stop(relay_chn->inertia_timer); - // If the channel was running, schedule a free command for the channel - if (relay_chn->run_info.last_run_cmd != RELAY_CHN_CMD_NONE) { + // Save the last run time only if the previous state was either STATE FORWARD + // or STATE_REVERSE. Then schedule a free command. + if (previous_state == RELAY_CHN_STATE_FORWARD || previous_state == RELAY_CHN_STATE_REVERSE) { // Record the command's last run time relay_chn->run_info.last_run_cmd_time_ms = esp_timer_get_time() / 1000; // Schedule a free command for the channel relay_chn->pending_cmd = RELAY_CHN_CMD_FREE; relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS); } else { - // If the channel was not running, issue a free command immediately + // If the channel was not running one of the run or fwd, issue a free command immediately relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_FREE); } } static void relay_chn_execute_forward(relay_chn_t *relay_chn) { - gpio_set_level(relay_chn->output.reverse_pin, 0); - gpio_set_level(relay_chn->output.forward_pin, 1); + if (relay_chn_output_forward(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_execute_forward: Failed to output forward for relay channel #%d!", relay_chn->id); + return; + } relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_FORWARD; relay_chn_update_state(relay_chn, RELAY_CHN_STATE_FORWARD); } static void relay_chn_execute_reverse(relay_chn_t *relay_chn) { - gpio_set_level(relay_chn->output.forward_pin, 0); - gpio_set_level(relay_chn->output.reverse_pin, 1); + if (relay_chn_output_reverse(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_execute_reverse: Failed to output reverse for relay channel #%d!", relay_chn->id); + return; + } relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_REVERSE; relay_chn_update_state(relay_chn, RELAY_CHN_STATE_REVERSE); } @@ -831,67 +850,50 @@ char *relay_chn_state_str(relay_chn_state_t state) #if RELAY_CHN_ENABLE_TILTING == 1 -// Timer callback for the relay_chn_tilt_control_t::tilt_timer -static void relay_chn_tilt_timer_cb(void *arg) +/** + * @brief Dispatch a tilt command to the relay channel event loop. + * + * @param relay_chn The relay channel to send the command to. + * @param cmd The tilt command. + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if the command is none. + * - Other error codes on failure. + */ +static esp_err_t relay_chn_dispatch_tilt_cmd(relay_chn_t *relay_chn, relay_chn_tilt_cmd_t cmd) { - uint8_t chn_id = *(uint8_t*) arg; - if (!relay_chn_is_channel_id_valid(chn_id)) { - ESP_LOGE(TAG, "relay_chn_tilt_timer_cb: Invalid relay channel ID!"); - return; - } - relay_chn_t* relay_chn = &relay_channels[chn_id]; - switch (relay_chn->tilt_control.step) - { - case RELAY_CHN_TILT_STEP_RUN: - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_STOP); - break; - - case RELAY_CHN_TILT_STEP_PAUSE: - if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_FORWARD) { - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_REVERSE); - } - else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_REVERSE) { - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FORWARD); - } - break; - - default: - break; - } + if (cmd == RELAY_CHN_TILT_CMD_NONE) return ESP_ERR_INVALID_ARG; + return esp_event_post_to(relay_chn_event_loop, + RELAY_CHN_TILT_CMD_EVENT_BASE, + cmd, + &relay_chn->id, + sizeof(relay_chn->id), portMAX_DELAY); } -// This listener is active until the relay_chn_tilt_stop() is called. -static void relay_chn_tilt_state_handler(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) +/** + * @brief Get the required timing before tilting depending on the last run. + * + * @param relay_chn the relay channel. + * @param cmd The tilt command. + * @return The time that is required in ms. + */ +static uint32_t relay_chn_get_required_timing_before_tilting(relay_chn_t *relay_chn, relay_chn_tilt_cmd_t cmd) { - ESP_LOGD(TAG, "relay_chn_tilt_state_listener: #%u, old_state: %s, new_state: %s", - chn_id, relay_chn_state_str(old_state), relay_chn_state_str(new_state)); + if (cmd == RELAY_CHN_TILT_CMD_FORWARD && relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_REVERSE) + return 0; + else if (cmd == RELAY_CHN_TILT_CMD_REVERSE && relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_FORWARD) + return 0; - relay_chn_t* relay_chn = &relay_channels[chn_id]; - // Check whether this channel is the one that's been tilting - if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_NONE) { - return; - } - - switch (new_state) - { - case RELAY_CHN_STATE_FORWARD: - case RELAY_CHN_STATE_REVERSE: - relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_RUN; - // Start the tilt run timer - esp_timer_start_once(relay_chn->tilt_control.tilt_timer, - relay_chn->tilt_control.tilt_timing.run_time_ms * 1000); - break; - case RELAY_CHN_STATE_STOPPED: - relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PAUSE; - esp_timer_start_once(relay_chn->tilt_control.tilt_timer, - relay_chn->tilt_control.tilt_timing.pause_time_ms * 1000); - break; - - default: - break; - } + uint32_t inertia_time_passed_ms = (uint32_t) (esp_timer_get_time() / 1000) - relay_chn->run_info.last_run_cmd_time_ms; + return RELAY_CHN_OPPOSITE_INERTIA_MS - inertia_time_passed_ms; } +/** + * @brief Issue a tilt command to a specific relay channel. + * + * @param chn_id The channel ID. + * @param cmd The tilt command. + */ static void relay_chn_issue_tilt_cmd(uint8_t chn_id, relay_chn_tilt_cmd_t cmd) { relay_chn_t* relay_chn = &relay_channels[chn_id]; @@ -901,36 +903,70 @@ static void relay_chn_issue_tilt_cmd(uint8_t chn_id, relay_chn_tilt_cmd_t cmd) ESP_LOGD(TAG, "relay_chn_issue_tilt_cmd: Tilt will not be executed since the channel hasn't been run yet"); return; } - else if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_REVERSE && cmd == RELAY_CHN_TILT_CMD_FORWARD) { - ESP_LOGD(TAG, "relay_chn_issue_tilt_cmd: Invalid tilt command: TILT_FORWARD after the REVERSE command issued"); - return; - } - else if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_FORWARD && cmd == RELAY_CHN_TILT_CMD_REVERSE) { - ESP_LOGD(TAG, "relay_chn_issue_tilt_cmd: Invalid tilt command: TILT_REVERSE after the FORWARD command issued"); - return; - } if (relay_chn->tilt_control.cmd == cmd) { ESP_LOGD(TAG, "relay_chn_issue_tilt_cmd: There is already a tilt command in progress!"); return; } - // Set tilt control parameters + // Set the command that will be processed relay_chn->tilt_control.cmd = cmd; - relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_NONE; + switch (relay_chn->state) { + case RELAY_CHN_STATE_FREE: + // Relay channel is free, tilt can be issued immediately + relay_chn_dispatch_tilt_cmd(relay_chn, cmd); + break; + + case RELAY_CHN_STATE_FORWARD_PENDING: + case RELAY_CHN_STATE_REVERSE_PENDING: + // Issue a stop command first so that the timer and pending cmd get cleared + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); + // break not put intentionally + case RELAY_CHN_STATE_STOPPED: { + // Check if channel needs timing before tilting + uint32_t req_timing_ms = relay_chn_get_required_timing_before_tilting(relay_chn, cmd); + if (req_timing_ms == 0) { + relay_chn_dispatch_tilt_cmd(relay_chn, cmd); + } else { + // Channel needs timing before running tilting action, schedule it + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PENDING; + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, req_timing_ms); + } + break; + } - // Set channel tilting active flag - relay_chn_tilting_channels |= (1 << chn_id); + case RELAY_CHN_STATE_FORWARD: + if (cmd == RELAY_CHN_TILT_CMD_FORWARD) { + // Stop the running channel first + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); + // Schedule for tilting + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PENDING; + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, RELAY_CHN_OPPOSITE_INERTIA_MS); + } else if (cmd == RELAY_CHN_TILT_CMD_REVERSE) { + // Stop the running channel first + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); + // If the tilt cmd is TILT_REVERSE then dispatch it immediately + relay_chn_dispatch_tilt_cmd(relay_chn, cmd); + } + break; - if (cmd == RELAY_CHN_TILT_CMD_FORWARD) { - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_REVERSE); - // Emit the tilt state change for the channel - relay_chn_update_state(relay_chn, RELAY_CHN_STATE_TILT_FORWARD); - } - else if (cmd == RELAY_CHN_TILT_CMD_REVERSE) { - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FORWARD); - // Emit the tilt state change for the channel - relay_chn_update_state(relay_chn, RELAY_CHN_STATE_TILT_REVERSE); + case RELAY_CHN_STATE_REVERSE: + if (cmd == RELAY_CHN_TILT_CMD_REVERSE) { + // Stop the running channel first + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); + // Schedule for tilting + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PENDING; + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, RELAY_CHN_OPPOSITE_INERTIA_MS); + } else if (cmd == RELAY_CHN_TILT_CMD_FORWARD) { + // Stop the running channel first + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); + // If the tilt cmd is TILT_FORWARD then dispatch it immediately + relay_chn_dispatch_tilt_cmd(relay_chn, cmd); + } + break; + + default: + ESP_LOGD(TAG, "relay_chn_issue_tilt_cmd: Unexpected relay channel state: %s!", relay_chn_state_str(relay_chn->state)); } } @@ -944,10 +980,10 @@ static void relay_chn_issue_tilt_cmd_on_all_channels(relay_chn_tilt_cmd_t cmd) static void relay_chn_issue_tilt_auto(uint8_t chn_id) { relay_chn_t* relay_chn = &relay_channels[chn_id]; - if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_FORWARD) { + if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_FORWARD || relay_chn->state == RELAY_CHN_STATE_FORWARD) { relay_chn_issue_tilt_cmd(chn_id, RELAY_CHN_TILT_CMD_FORWARD); } - else if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_REVERSE) { + else if (relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_REVERSE || relay_chn->state == RELAY_CHN_STATE_REVERSE) { relay_chn_issue_tilt_cmd(chn_id, RELAY_CHN_TILT_CMD_REVERSE); } } @@ -989,19 +1025,15 @@ void relay_chn_tilt_reverse(uint8_t chn_id) else relay_chn_issue_tilt_cmd(chn_id, RELAY_CHN_TILT_CMD_REVERSE); } -static void relay_chn_issue_tilt_stop(uint8_t chn_id) +static void _relay_chn_tilt_stop(uint8_t chn_id) { relay_chn_t* relay_chn = &relay_channels[chn_id]; if (relay_chn->tilt_control.cmd != RELAY_CHN_TILT_CMD_NONE) { - // Stop the channel's timer if active - esp_timer_stop(relay_chn->tilt_control.tilt_timer); - // Invalidate tilt cmd and step - relay_chn->tilt_control.cmd = RELAY_CHN_TILT_CMD_NONE; - relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_NONE; - // Unset channel tilting active flag - relay_chn_tilting_channels &= ~(1 << chn_id); - // Stop the channel - relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_STOP); + esp_event_post_to(relay_chn_event_loop, + RELAY_CHN_TILT_CMD_EVENT_BASE, + RELAY_CHN_TILT_CMD_STOP, + &relay_chn->id, + sizeof(relay_chn->id), portMAX_DELAY); } } @@ -1010,21 +1042,14 @@ void relay_chn_tilt_stop(uint8_t chn_id) if (!relay_chn_is_channel_id_valid(chn_id)) { return; } - - // Check whether there is an active tilting channel - if (!relay_chn_tilting_channels) { - // No active tilting channels, so nothing to do - return; - } if (chn_id == RELAY_CHN_ID_ALL) { - // Any channel executing tilt? for (int i = 0; i < RELAY_CHN_COUNT; i++) { - relay_chn_issue_tilt_stop(i); + _relay_chn_tilt_stop(i); } } else { - relay_chn_issue_tilt_stop(chn_id); + _relay_chn_tilt_stop(chn_id); } } @@ -1034,7 +1059,7 @@ static void relay_chn_set_tilt_timing_values(relay_chn_tilt_timing_t *tilt_timin uint32_t pause_time_ms) { tilt_timing->sensitivity = sensitivity; - tilt_timing->run_time_ms = run_time_ms; + tilt_timing->move_time_ms = run_time_ms; tilt_timing->pause_time_ms = pause_time_ms; } @@ -1104,13 +1129,135 @@ esp_err_t relay_chn_tilt_sensitivity_get(uint8_t chn_id, uint8_t *sensitivity, s return ESP_OK; } +static void relay_chn_tilt_execute_tilt_stop(relay_chn_t *relay_chn) +{ + // Stop the channel's timer if active + esp_timer_stop(relay_chn->tilt_control.tilt_timer); + // Invalidate tilt cmd and step + relay_chn->tilt_control.cmd = RELAY_CHN_TILT_CMD_NONE; + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_NONE; + // Stop the channel + if (relay_chn_output_stop(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_tilt_execute_tilt_stop: Failed to output stop for relay channel #%d!", relay_chn->id); + } + relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP); +} + +static void relay_chn_tilt_execute_tilt_forward(relay_chn_t *relay_chn) +{ + if (relay_chn_output_reverse(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_tilt_execute_tilt_forward: Failed to output reverse for relay channel #%d!", relay_chn->id); + // Stop tilting because of the error + relay_chn_dispatch_tilt_cmd(relay_chn, RELAY_CHN_TILT_CMD_STOP); + return; + } + // Set the move time timer + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, + relay_chn->tilt_control.tilt_timing.move_time_ms); + // Set to pause step + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PAUSE; +} + +static void relay_chn_tilt_execute_tilt_reverse(relay_chn_t *relay_chn) +{ + if (relay_chn_output_forward(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_tilt_execute_tilt_reverse: Failed to output forward for relay channel #%d!", relay_chn->id); + // Stop tilting because of the error + relay_chn_dispatch_tilt_cmd(relay_chn, RELAY_CHN_TILT_CMD_STOP); + return; + } + // Set the move time timer + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, + relay_chn->tilt_control.tilt_timing.move_time_ms); + // Set to pause step + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_PAUSE; +} + +static void relay_chn_tilt_execute_tilt_pause(relay_chn_t *relay_chn) +{ + // Pause the channel + if (relay_chn_output_stop(relay_chn) != ESP_OK) { + ESP_LOGE(TAG, "relay_chn_tilt_execute_tilt_stop: Failed to output stop for relay channel #%d!", relay_chn->id); + // Stop tilting because of the error + relay_chn_dispatch_tilt_cmd(relay_chn, RELAY_CHN_TILT_CMD_STOP); + return; + } + // Set the pause time timer + relay_chn_start_esp_timer_once(relay_chn->tilt_control.tilt_timer, + relay_chn->tilt_control.tilt_timing.pause_time_ms); + // Set to move step + relay_chn->tilt_control.step = RELAY_CHN_TILT_STEP_MOVE; +} + +static void relay_chn_tilt_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + uint8_t chn_id = *(uint8_t*) event_data; + if (!relay_chn_is_channel_id_valid(chn_id)) { + return; + } + relay_chn_t* relay_chn = &relay_channels[chn_id]; + ESP_LOGD(TAG, "relay_chn_event_handler: Channel %d, Command: %s", relay_chn->id, relay_chn_cmd_str(event_id)); + switch(event_id) { + case RELAY_CHN_TILT_CMD_STOP: + relay_chn_tilt_execute_tilt_stop(relay_chn); + break; + case RELAY_CHN_TILT_CMD_FORWARD: + relay_chn_tilt_execute_tilt_forward(relay_chn); + // Update channel state + relay_chn_update_state(relay_chn, RELAY_CHN_STATE_TILT_FORWARD); + break; + case RELAY_CHN_TILT_CMD_REVERSE: + relay_chn_tilt_execute_tilt_reverse(relay_chn); + // Update channel state + relay_chn_update_state(relay_chn, RELAY_CHN_STATE_TILT_REVERSE); + break; + default: + ESP_LOGW(TAG, "Unexpected relay channel tilt command: %ld!", event_id); + } +} + +// Timer callback for the relay_chn_tilt_control_t::tilt_timer +static void relay_chn_tilt_timer_cb(void *arg) +{ + uint8_t chn_id = *(uint8_t*) arg; + if (!relay_chn_is_channel_id_valid(chn_id)) { + ESP_LOGE(TAG, "relay_chn_tilt_timer_cb: Invalid relay channel ID!"); + return; + } + relay_chn_t* relay_chn = &relay_channels[chn_id]; + + switch (relay_chn->tilt_control.step) + { + case RELAY_CHN_TILT_STEP_MOVE: + if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_FORWARD) { + relay_chn_tilt_execute_tilt_forward(relay_chn); + } + else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_REVERSE) { + relay_chn_tilt_execute_tilt_reverse(relay_chn); + } + break; + + case RELAY_CHN_TILT_STEP_PAUSE: + relay_chn_tilt_execute_tilt_pause(relay_chn); + break; + + case RELAY_CHN_TILT_STEP_PENDING: + // Just dispatch the pending tilt command + relay_chn_dispatch_tilt_cmd(relay_chn, relay_chn->tilt_control.cmd); + break; + + default: + break; + } +} + static esp_err_t relay_chn_init_tilt_control(relay_chn_t *relay_chn) { relay_chn_tilt_control_t *tilt_control = &relay_chn->tilt_control; tilt_control->cmd = RELAY_CHN_TILT_CMD_NONE; tilt_control->step = RELAY_CHN_TILT_STEP_NONE; tilt_control->tilt_timing.sensitivity = RELAY_CHN_TILT_DEFAULT_SENSITIVITY; - tilt_control->tilt_timing.run_time_ms = RELAY_CHN_TILT_DEFAULT_RUN_MS; + tilt_control->tilt_timing.move_time_ms = RELAY_CHN_TILT_DEFAULT_RUN_MS; tilt_control->tilt_timing.pause_time_ms = RELAY_CHN_TILT_DEFAULT_PAUSE_MS; // Create tilt timer for the channel @@ -1124,6 +1271,17 @@ static esp_err_t relay_chn_init_tilt_control(relay_chn_t *relay_chn) return esp_timer_create(&timer_args, &relay_chn->tilt_control.tilt_timer); } +// Should call once from relay_chn_init +static esp_err_t relay_chn_tilt_init(void) +{ + esp_err_t ret; + ret = esp_event_handler_register_with(relay_chn_event_loop, + RELAY_CHN_TILT_CMD_EVENT_BASE, + ESP_EVENT_ANY_ID, + relay_chn_tilt_event_handler, NULL); + return ret; +} + #endif // RELAY_CHN_ENABLE_TILTING /// @} \ No newline at end of file