diff --git a/components/.build-rules.yml b/components/.build-rules.yml index 9ab035f7f..c128c29a6 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -39,8 +39,8 @@ components/motor/esp_sensorless_bldc_control/test_apps/bldc_adc: - if: SOC_MCPWM_SUPPORTED == 1 components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer: - disable: - - if: IDF_TARGET in ["esp32c2"] + enable: + - if: SOC_MCPWM_SUPPORTED == 1 components/led/lightbulb_driver/test_apps: enable: diff --git a/components/motor/esp_sensorless_bldc_control/CHANGELOG.md b/components/motor/esp_sensorless_bldc_control/CHANGELOG.md index d6db690e4..2993fd98d 100644 --- a/components/motor/esp_sensorless_bldc_control/CHANGELOG.md +++ b/components/motor/esp_sensorless_bldc_control/CHANGELOG.md @@ -1,5 +1,13 @@ # ChangeLog + +## v0.3.0 - 2024-6-20 + +### Bug fix + +* Fix drag time and modify the filter cap to type uint32_t +* Fix Comparator Acquisition Method for MCPWM Trigger + ## v0.2.0 - 2023-11-14 ### Enhancements diff --git a/components/motor/esp_sensorless_bldc_control/README.md b/components/motor/esp_sensorless_bldc_control/README.md index fef154cb4..3a24f2107 100644 --- a/components/motor/esp_sensorless_bldc_control/README.md +++ b/components/motor/esp_sensorless_bldc_control/README.md @@ -1,29 +1,29 @@ [![Component Registry](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control/badge.svg)](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) -## ESP Sensorless bldc control 组件介绍 +## ESP Sensorless BLDC Control Component Introduction -``esp_sensorless_bldc_control`` 是基于 ESP32 系列芯片的 BLDC 无感方波控制库,支持以下功能: +``esp_sensorless_bldc_control`` is a sensorless BLDC control library based on ESP32 series chips, supporting the following features: -* 支持基于 ADC 采样检测过零点 -* 支持基于比较器检测过零点 -* 支持基于脉冲法实现转子初始相位检测 -* 支持堵转保护 -* 支持过流,过欠压保护[feature] -* 支持缺相保护[feature] +* Supports zero-crossing detection based on ADC sampling +* Supports zero-crossing detection based on comparators +* Supports initial rotor position detection using pulse method +* Supports stall protection +* Supports overcurrent and undervoltage protection [feature] +* Supports phase loss protection [feature] -### ESP Sensorless bldc control 用户指南 +### ESP Sensorless BLDC Control User Guide -请参考:https://docs.espressif.com/projects/esp-iot-solution/zh_CN/latest/motor/index.html +Please refer to:https://docs.espressif.com/projects/esp-iot-solution/zh_CN/latest/motor/index.html -### 添加组件到工程 +### Adding the Component to Your Project -1. 请使用组件管理器指令 `add-dependency` 将 `esp_sensorless_bldc_control` 添加到项目的依赖项, 在 `CMake` 执行期间该组件将被自动下载到工程目录。 +1. Use the component manager command `add-dependency` to add `esp_sensorless_bldc_control` to your project's dependencies. The component will be automatically downloaded to the project directory during `CMake` execution. ``` idf.py add-dependency "espressif/esp_sensorless_bldc_control=*" ``` -2. 将 `esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h.tpl` 文件复制到工程目录下,并更名为 `bldc_user_cfg.h`。并在 `main/CMakeLists.txt` 文件中加入: +2. Copy the `esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h` file to your project directory and rename it to `bldc_user_cfg.h`. Add the following to your `main/CMakeLists.txt` file: ``` idf_component_get_property(bldc_lib espressif__esp_sensorless_bldc_control COMPONENT_LIB) @@ -31,9 +31,9 @@ target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) ``` - Note: 该文件用于设置电机控制相关参数,一定要包含在工程中。 + Note: This file is used to set motor control related parameters and must be included in the project. -## 使用示例 +## Example Usage ```C esp_event_loop_create_default(); diff --git a/components/motor/esp_sensorless_bldc_control/bldc_control.c b/components/motor/esp_sensorless_bldc_control/bldc_control.c index a2cbc834a..5af9913a0 100644 --- a/components/motor/esp_sensorless_bldc_control/bldc_control.c +++ b/components/motor/esp_sensorless_bldc_control/bldc_control.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -103,13 +103,19 @@ esp_err_t bldc_control_init(bldc_control_handle_t *handle, bldc_control_config_t xTaskCreate(bldc_control_task, "bldc_control_task", 1024 * 4, control, 10, NULL); switch (config->alignment_mode) { - case ALIGNMENT_COMPARER: + case ALIGNMENT_COMPARER: { ret = bldc_zero_cross_comparer_init(&control->zero_cross_handle, &config->zero_cross_comparer_config, &control->control_param); BLDC_CHECK_GOTO(ret == ESP_OK, "bldc_zero_cross_comparer_init failed", deinit); control->zero_cross = &bldc_zero_cross_comparer_operation; + mcpwm_timer_event_callbacks_t cbs = { + .on_full = &read_comparer_on_full, + }; + config->six_step_config.upper_switch_config.bldc_mcpwm.cbs = &cbs; + config->six_step_config.upper_switch_config.bldc_mcpwm.timer_cb_user_data = (void *)control->zero_cross_handle; break; + } #if CONFIG_SOC_MCPWM_SUPPORTED - case ALIGNMENT_ADC: + case ALIGNMENT_ADC: { ret = bldc_zero_cross_adc_init(&control->zero_cross_handle, &config->zero_cross_adc_config, &control->control_param); BLDC_CHECK_GOTO(ret == ESP_OK, "bldc_zero_cross_comparer_init failed", deinit); control->zero_cross = &bldc_zero_cross_adc_operation; @@ -123,6 +129,7 @@ esp_err_t bldc_control_init(bldc_control_handle_t *handle, bldc_control_config_t config->six_step_config.upper_switch_config.bldc_mcpwm.cbs = &cbs; config->six_step_config.upper_switch_config.bldc_mcpwm.timer_cb_user_data = (void *)control->zero_cross_handle; break; + } #endif default: ESP_LOGE(TAG, "control_mode error"); @@ -276,6 +283,8 @@ esp_err_t bldc_control_stop(bldc_control_handle_t *handle) default: break; } + + bldc_control_dispatch_event(BLDC_CONTROL_STOP, NULL, 0); return ESP_OK; } diff --git a/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c index a9a497ef6..23ff7ea62 100644 --- a/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c +++ b/components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,7 +15,7 @@ typedef struct { control_param_t *control_param; int alignment_pin[PHASE_MAX]; int zero_stable_flag; /*!< Over-zero stabilization flag bit */ - uint32_t alignment_queue_value[PHASE_MAX]; + uint64_t alignment_queue_value[PHASE_MAX]; uint16_t queue_filter_state[PHASE_MAX]; /*!< State after three-phase filtering */ } bldc_zero_cross_comparer_t; @@ -48,6 +48,13 @@ static uint8_t bldc_umef_edge(uint8_t val) return 2; } +bool read_comparer_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data) +{ + bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)user_data; + alignment_comparer_get_value(zero_cross); + return false; +} + esp_err_t bldc_zero_cross_comparer_init(bldc_zero_cross_comparer_handle_t *handle, bldc_zero_cross_comparer_config_t *config, control_param_t *control_param) { BLDC_CHECK(config != NULL, "bldc zero cross config is NULL", ESP_ERR_INVALID_ARG); @@ -92,7 +99,6 @@ uint8_t bldc_zero_cross_comparer_operation(void *handle) bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)handle; uint16_t filterEdge; /*!< Edge detection after filtering */ - alignment_comparer_get_value(zero_cross); zero_cross->control_param->speed_count++; for (int i = 0; i < PHASE_MAX; i++) { diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h index ad2f00c7f..cb5f0506a 100644 --- a/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_control_param.h @@ -54,7 +54,7 @@ typedef struct { uint8_t phase_change_done; /*!< Motor phase change successful */ uint16_t duty; /*!< duty cycle */ uint16_t duty_max; /*!< duty cycle max */ - uint16_t drag_time; /*!< drag time */ + int16_t drag_time; /*!< drag time */ alignment_mode_t alignment_mode; /*!< alignment mode */ /* speed */ uint32_t expect_speed_rpm; /*!< Expected speed */ diff --git a/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h index b635a6281..3538c7778 100644 --- a/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h +++ b/components/motor/esp_sensorless_bldc_control/control/include/bldc_zero_cross_comparer.h @@ -22,6 +22,16 @@ typedef struct { */ typedef void *bldc_zero_cross_comparer_handle_t; +/** + * @brief Used to detect the value of gpio when the top tube and bottom tube are open + * + * @param timer MCPWM timer handle + * @param edata MCPWM timer event data, fed by driver + * @param user_data User data, set in `mcpwm_timer_register_event_callbacks()` + * @return Whether a high priority task has been waken up by this function + */ +bool read_comparer_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data); + /** * @brief Initialize zero cross comparer * diff --git a/components/motor/esp_sensorless_bldc_control/idf_component.yml b/components/motor/esp_sensorless_bldc_control/idf_component.yml index c782faf0b..998a71881 100644 --- a/components/motor/esp_sensorless_bldc_control/idf_component.yml +++ b/components/motor/esp_sensorless_bldc_control/idf_component.yml @@ -1,4 +1,10 @@ -version: 0.2.0 +version: 0.3.0 +targets: + - esp32 + - esp32s3 + - esp32c6 + - esp32h2 + - esp32p4 description: ESP32 Sensorless BLDC Control Component url: https://github.com/espressif/esp-iot-solution/tree/master/components/motor/esp_sensorless_bldc_control repository: https://github.com/espressif/esp-iot-solution.git diff --git a/examples/motor/bldc_fan_rainmaker/README.md b/examples/motor/bldc_fan_rainmaker/README.md index 8b25aaed2..ffaebaf02 100644 --- a/examples/motor/bldc_fan_rainmaker/README.md +++ b/examples/motor/bldc_fan_rainmaker/README.md @@ -1,22 +1,24 @@ # bldc_fan_rainmaker -`bldc_fan_rainmaker` 例程将无刷电机驱动的电风扇接入 ESP Rainmaker 云,实现了以下功能 +The `bldc_fan_rainmaker` example connects a brushless motor-driven fan to the ESP Rainmaker cloud, achieving the following functionalities: -* 风扇无极调速 -* 摇头 -* 自然风 -* 远程启动关闭 -* OTA -* BLE 配网 +* Stepless fan speed control +* Oscillation +* Natural wind mode +* Remote start and stop +* OTA updates +* BLE provisioning -## 组件介绍 +![rainmaker_fan](https://dl.espressif.com/AE/esp-iot-solution/esp_bldc_rainmaker.gif) -* [esp_sensorless_bldc_control](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) 是基于 ESP32 系列芯片的 BLDC 无感方波控制库。支持以下功能: - * 支持基于 ADC 采样检测过零点 - * 支持基于比较器检测过零点 - * 支持基于脉冲法实现转子初始相位检测 - * 支持堵转保护 - * 支持过流,过欠压保护[feature] - * 支持缺相保护[feature] +## Component Overview -* [esp_rainmaker](https://components.espressif.com/components/espressif/esp_rainmaker) 是一个完整且轻量级的 AIoT 解决方案,能够以简单、经济高效且高效的方式为您的企业实现私有云部署。 \ No newline at end of file +* [esp_sensorless_bldc_control](https://components.espressif.com/components/espressif/esp_sensorless_bldc_control) is a sensorless BLDC square wave control library based on the ESP32 series chips. It supports the following features: + * Zero-crossing detection based on ADC sampling + * Zero-crossing detection based on a comparator + * Initial rotor position detection using pulse method + * Stall protection + * Overcurrent, over/under-voltage protection [feature] + * Phase loss protection [feature] + +* [esp_rainmaker](https://components.espressif.com/components/espressif/esp_rainmaker) is a complete and lightweight AIoT solution that enables private cloud deployment for your business in a simple, cost-effective, and efficient manner. \ No newline at end of file diff --git a/examples/motor/bldc_fan_rainmaker/main/CMakeLists.txt b/examples/motor/bldc_fan_rainmaker/main/CMakeLists.txt index edda17dc4..206881532 100644 --- a/examples/motor/bldc_fan_rainmaker/main/CMakeLists.txt +++ b/examples/motor/bldc_fan_rainmaker/main/CMakeLists.txt @@ -6,6 +6,15 @@ set(INC_DIRS "app/include/" "hal/include/") idf_component_register(SRCS ${SRC_FILES} ${SOURCES} INCLUDE_DIRS "." ${INC_DIRS} ) -idf_component_get_property(bldc_lib esp_sensorless_bldc_control COMPONENT_LIB) -cmake_policy(SET CMP0079 NEW) -target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) + +idf_build_get_property(build_components BUILD_COMPONENTS) +if("esp_sensorless_bldc_control" IN_LIST build_components) + idf_component_get_property(bldc_lib esp_sensorless_bldc_control COMPONENT_LIB) + cmake_policy(SET CMP0079 NEW) + target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) +endif() +if("espressif__esp_sensorless_bldc_control" IN_LIST build_components) + idf_component_get_property(bldc_lib espressif__esp_sensorless_bldc_control COMPONENT_LIB) + cmake_policy(SET CMP0079 NEW) + target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB}) +endif() diff --git a/examples/motor/bldc_fan_rainmaker/main/app/app_rainmaker.c b/examples/motor/bldc_fan_rainmaker/main/app/app_rainmaker.c index da6d5c7ba..b03fba94b 100644 --- a/examples/motor/bldc_fan_rainmaker/main/app/app_rainmaker.c +++ b/examples/motor/bldc_fan_rainmaker/main/app/app_rainmaker.c @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "app_rainmaker.h" -#include "esp_rmaker_core.h" #include "esp_rmaker_standard_types.h" #include "esp_rmaker_standard_params.h" #include "esp_rmaker_standard_devices.h" @@ -23,13 +22,14 @@ #include "app_variable.h" #include "hal_bldc.h" #include "hal_stepper_motor.h" +#include "bldc_fan_config.h" -static const char *TAG = "APP_RMKER"; -esp_rmaker_device_t *fan_device; -esp_rmaker_param_t *mode_param; -esp_rmaker_param_t *speed_param; -esp_rmaker_param_t *power_param; -esp_rmaker_param_t *shake_param; +const static char *TAG = "app_rainmaker"; +static esp_rmaker_device_t *fan_device; +static esp_rmaker_param_t *mode_param; +static esp_rmaker_param_t *speed_param; +static esp_rmaker_param_t *power_param; +static esp_rmaker_param_t *shake_param; static esp_err_t app_rainmaker_write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param, esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx) { @@ -43,6 +43,10 @@ static esp_err_t app_rainmaker_write_cb(const esp_rmaker_device_t *device, const val.val.b ? "true" : "false", esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param)); motor_parameter.is_start = val.val.b ? 1 : 0; + if (motor_parameter.is_start == 0) { + motor_parameter.start_count = 0; + motor_parameter.target_speed = BLDC_MIN_SPEED; + } hal_bldc_start_stop(motor_parameter.is_start); } else if (strcmp(esp_rmaker_param_get_name(param), ESP_RMAKER_DEF_SPEED_NAME) == 0) { //*!< get target speed value */ @@ -156,15 +160,8 @@ static void app_rainmaker_event_handler(void *arg, esp_event_base_t event_base, } } -esp_err_t app_rainmaker_init() +static void rainmaker_task(void *arg) { - esp_err_t err = nvs_flash_init(); //*!< init nvs */ - if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - err = nvs_flash_init(); - } - ESP_ERROR_CHECK(err); - esp_rmaker_console_init(); //*!< init rainmaker console */ app_wifi_init(); //*!< init wifi, choose wifi or ble */ @@ -215,13 +212,41 @@ esp_err_t app_rainmaker_init() }; esp_rmaker_system_service_enable(&system_serv_config); //*!< enable system service */ + esp_rmaker_schedule_enable(); //*!< enable schedule + esp_rmaker_start(); //*!< start rainmaker */ - err = app_wifi_start(POP_TYPE_NONE); + esp_err_t err = app_wifi_start(POP_TYPE_NONE); if (err != ESP_OK) { ESP_LOGE(TAG, "Could not start Wifi. Aborting!!!"); vTaskDelay(5000 / portTICK_PERIOD_MS); abort(); } - return ESP_OK; + vTaskDelete(NULL); +} + +esp_err_t app_rainmaker_init() +{ + esp_err_t err = nvs_flash_init(); //*!< init nvs */ + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + xTaskCreate(rainmaker_task, "rainmaker_task", 4096, NULL, 5, NULL); + return ESP_OK; //*!< create stepper motor task */ +} + +esp_rmaker_param_t *app_rainmaker_get_param(const char *name) +{ + if (!strcmp(name, "Power")) { + return power_param; + } else if (!strcmp(name, "Speed")) { + return speed_param; + } else if (!strcmp(name, "Natural")) { + return mode_param; + } else if (!strcmp(name, "Shake")) { + return shake_param; + } + return NULL; } diff --git a/examples/motor/bldc_fan_rainmaker/main/app/app_variable.c b/examples/motor/bldc_fan_rainmaker/main/app/app_variable.c index c6d4d6fd7..76098a2df 100644 --- a/examples/motor/bldc_fan_rainmaker/main/app/app_variable.c +++ b/examples/motor/bldc_fan_rainmaker/main/app/app_variable.c @@ -1,18 +1,19 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "app_variable.h" #include "string.h" +#include "bldc_fan_config.h" motor_parameter_t motor_parameter; void app_variable_init() { memset(&motor_parameter, 0x00, sizeof(motor_parameter)); - motor_parameter.target_speed = 300; /*!< motor target speed */ - motor_parameter.min_speed = 300; /*!< motor min speed */ - motor_parameter.max_speed = 1000; /*!< motor max speed */ + motor_parameter.target_speed = BLDC_MIN_SPEED; /*!< motor target speed */ + motor_parameter.min_speed = BLDC_MIN_SPEED; /*!< motor min speed */ + motor_parameter.max_speed = BLDC_MAX_SPEED; /*!< motor max speed */ } diff --git a/examples/motor/bldc_fan_rainmaker/main/app/include/app_rainmaker.h b/examples/motor/bldc_fan_rainmaker/main/app/include/app_rainmaker.h index 85db95d89..af5f02fc8 100644 --- a/examples/motor/bldc_fan_rainmaker/main/app/include/app_rainmaker.h +++ b/examples/motor/bldc_fan_rainmaker/main/app/include/app_rainmaker.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,18 +7,31 @@ #pragma once #include "esp_err.h" +#include "esp_rmaker_core.h" #ifdef __cplusplus extern "C" { #endif /** - * @brief rainmaker init + * @brief Initializes the rainmaker * - * @return esp_err_t + * @return + * - ESP_OK: Success in initializing the rainmaker + * - other: Specific error what went wrong during initialization. */ esp_err_t app_rainmaker_init(); +/** + * @brief Return rainmaker parameter + * + * @param name rainmaker parameter name + * @return + * - esp_rmaker_param_t*: Success return rainmaker parameter + * - NULL: Fail to get the parameter + */ +esp_rmaker_param_t *app_rainmaker_get_param(const char *name); + #ifdef __cplusplus } #endif diff --git a/examples/motor/bldc_fan_rainmaker/main/app/include/app_variable.h b/examples/motor/bldc_fan_rainmaker/main/app/include/app_variable.h index 87cd2ce85..8109b4c6a 100644 --- a/examples/motor/bldc_fan_rainmaker/main/app/include/app_variable.h +++ b/examples/motor/bldc_fan_rainmaker/main/app/include/app_variable.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,8 @@ extern "C" { typedef struct { uint8_t is_start; /*!< motor status 0:off 1:start */ uint8_t is_natural; /*!< motor natural wind 0: off 1: start */ + uint8_t start_count; /*!< number of motor starts */ + uint8_t restart_count; /*!< number of motor restarts */ uint16_t min_speed; /*!< motor min speed */ uint16_t max_speed; /*!< motor max speed */ uint16_t target_speed; /*!< motor target speed */ @@ -26,7 +28,7 @@ typedef struct { extern motor_parameter_t motor_parameter; /** - * @brief variable init + * @brief Initializes the system variables * */ void app_variable_init(); diff --git a/examples/motor/bldc_fan_rainmaker/main/app/include/app_wifi.h b/examples/motor/bldc_fan_rainmaker/main/app/include/app_wifi.h index 0924c4619..4740787d9 100644 --- a/examples/motor/bldc_fan_rainmaker/main/app/include/app_wifi.h +++ b/examples/motor/bldc_fan_rainmaker/main/app/include/app_wifi.h @@ -5,6 +5,7 @@ */ #pragma once + #include #include @@ -37,7 +38,19 @@ typedef enum { POP_TYPE_NONE } app_wifi_pop_type_t; +/** + * @brief Initializes the wifi + * + */ void app_wifi_init(); + +/** + * @brief Start the wifi + * + * @return + * - ESP_OK: Success in starting the wifi + * - other: Specific error code indicating what went wrong during start wifi. + */ esp_err_t app_wifi_start(app_wifi_pop_type_t pop_type); #ifdef __cplusplus diff --git a/examples/motor/bldc_fan_rainmaker/main/bldc_fan_config.h b/examples/motor/bldc_fan_rainmaker/main/bldc_fan_config.h new file mode 100644 index 000000000..9c2e4dce7 --- /dev/null +++ b/examples/motor/bldc_fan_rainmaker/main/bldc_fan_config.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// Bldc pins configuration +#define UPPER_SWITCH_PIN_U 17 +#define UPPER_SWITCH_PIN_V 16 +#define UPPER_SWITCH_PIN_W 15 +#define LOWER_SWITCH_PIN_U 12 +#define LOWER_SWITCH_PIN_V 11 +#define LOWER_SWITCH_PIN_W 10 +#define COMPARER_PIN_U 3 +#define COMPARER_PIN_V 46 +#define COMPARER_PIN_W 9 + +// Button pins configuration +#define SETTING_START_PIN 41 +#define SETTING_MODE_PIN 40 +#define SETTING_SHACKING_HEAD_PIN 39 +#define SETTING_TIMER_PIN 38 + +// Stepper pins configuration +#define STEPPER_A_PIN 14 +#define STEPPER_B_PIN 21 +#define STEPPER_C_PIN 47 +#define STEPPER_D_PIN 48 + +// Timing Parameter Configuration +#define MAXINUM_TIMING_COUNT 5 +#define SIGNLE_TIMING_HOUR_DURACTION 2 + +// Bldc Running Configuration +#define BLDC_MAX_SPEED 1000 +#define BLDC_MIN_SPEED 300 +#define BLDC_MID_SPEED (int)(BLDC_MIN_SPEED + (BLDC_MAX_SPEED - BLDC_MIN_SPEED) / 3) +#define BLDC_HIGH_SPEED (int)(BLDC_MIN_SPEED + (BLDC_MAX_SPEED - BLDC_MIN_SPEED) * 2 / 3) +#define BLDC_MAX_RESTART_COUNT 3 + +#ifdef __cplusplus +} +#endif diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/hal_bldc.c b/examples/motor/bldc_fan_rainmaker/main/hal/hal_bldc.c index bf54abdf3..c29237a98 100644 --- a/examples/motor/bldc_fan_rainmaker/main/hal/hal_bldc.c +++ b/examples/motor/bldc_fan_rainmaker/main/hal/hal_bldc.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,12 @@ #include "hal_bldc.h" #include "iot_button.h" #include "esp_timer.h" +#include "bldc_fan_config.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "app_rainmaker.h" +#include "esp_rmaker_core.h" #define PI 3.14159265f @@ -18,6 +24,7 @@ ((VALUE) < (MIN) ? (MIN) : ((VALUE) > (MAX) ? (MAX) : (VALUE))) static bldc_control_handle_t bldc_control_handle = NULL; +const static char *TAG = "hal_bldc"; static void hal_bldc_button_ctrl(void *arg, void *data) { @@ -67,45 +74,101 @@ esp_err_t hal_bldc_button_ctrl_init(gpio_num_t pin) return ESP_OK; } +static void bldc_control_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + esp_rmaker_param_t *rmaker_param = NULL; + switch (event_id) { + case BLDC_CONTROL_START: + ESP_LOGI(TAG, "BLDC_CONTROL_START"); + break; + case BLDC_CONTROL_STOP: + ESP_LOGI(TAG, "BLDC_CONTROL_STOP"); + motor_parameter.is_start = false; + motor_parameter.target_speed = BLDC_MIN_SPEED; + break; + case BLDC_CONTROL_ALIGNMENT: + ESP_LOGI(TAG, "BLDC_CONTROL_ALIGNMENT"); + break; + case BLDC_CONTROL_CLOSED_LOOP: + motor_parameter.is_start = true; + ESP_LOGI(TAG, "BLDC_CONTROL_CLOSED_LOOP"); + break; + case BLDC_CONTROL_DRAG: + ESP_LOGI(TAG, "BLDC_CONTROL_DRAG"); + break; + case BLDC_CONTROL_BLOCKED: + ESP_LOGI(TAG, "BLDC_CONTROL_BLOCKED"); + motor_parameter.is_start = false; + hal_bldc_start_stop(0); + if (++motor_parameter.restart_count < BLDC_MAX_RESTART_COUNT) { + vTaskDelay(500 / portTICK_PERIOD_MS); + motor_parameter.target_speed = BLDC_MIN_SPEED; + hal_bldc_start_stop(1); + motor_parameter.is_start = true; + } + break; + } + + rmaker_param = app_rainmaker_get_param("Power"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.b = motor_parameter.is_start; + val.type = RMAKER_VAL_TYPE_BOOLEAN; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current motor start status */ + } + + rmaker_param = app_rainmaker_get_param("Speed"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.i = motor_parameter.target_speed; + val.type = RMAKER_VAL_TYPE_INTEGER; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current motor speed */ + } +} + esp_err_t hal_bldc_init(dir_enum_t direction) { + esp_event_loop_create_default(); + ESP_ERROR_CHECK(esp_event_handler_register(BLDC_CONTROL_EVENT, ESP_EVENT_ANY_ID, &bldc_control_event_handler, NULL)); + switch_config_t_t upper_switch_config = { .control_type = CONTROL_TYPE_MCPWM, .bldc_mcpwm = { .group_id = 0, - .gpio_num = {17, 16, 15}, + .gpio_num = {UPPER_SWITCH_PIN_U, UPPER_SWITCH_PIN_V, UPPER_SWITCH_PIN_W}, }, }; switch_config_t_t lower_switch_config = { .control_type = CONTROL_TYPE_GPIO, .bldc_gpio[0] = { - .gpio_num = 12, + .gpio_num = LOWER_SWITCH_PIN_U, .gpio_mode = GPIO_MODE_OUTPUT, }, .bldc_gpio[1] = { - .gpio_num = 11, + .gpio_num = LOWER_SWITCH_PIN_V, .gpio_mode = GPIO_MODE_OUTPUT, }, .bldc_gpio[2] = { - .gpio_num = 10, + .gpio_num = LOWER_SWITCH_PIN_W, .gpio_mode = GPIO_MODE_OUTPUT, }, }; bldc_zero_cross_comparer_config_t zero_cross_comparer_config = { .comparer_gpio[0] = { - .gpio_num = 3, + .gpio_num = COMPARER_PIN_U, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0, }, .comparer_gpio[1] = { - .gpio_num = 46, + .gpio_num = COMPARER_PIN_V, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0, }, .comparer_gpio[2] = { - .gpio_num = 9, + .gpio_num = COMPARER_PIN_W, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0, }, diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/hal_fan_button.c b/examples/motor/bldc_fan_rainmaker/main/hal/hal_fan_button.c new file mode 100644 index 000000000..cbdbdcacc --- /dev/null +++ b/examples/motor/bldc_fan_rainmaker/main/hal/hal_fan_button.c @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "string.h" +#include "hal_fan_button.h" +#include "hal_stepper_motor.h" +#include "esp_rmaker_core.h" +#include "hal_bldc.h" +#include "app_variable.h" +#include "app_rainmaker.h" +#include "iot_button.h" +#include "esp_log.h" +#include "bldc_fan_config.h" + +#define SIGNLE_TIMING_DURATION SIGNLE_TIMING_HOUR_DURACTION*60*60*1000*1000UL /*!< Converting hours to us*/ +hal_fan_button_t hal_fan_button; +static button_handle_t hal_fan_button_handle[4] = {0}; +static const char *TAG = "fan button"; + +static int hal_fan_get_button_index(button_handle_t btn) +{ + for (size_t i = 0; i < 4; i++) { + if (btn == hal_fan_button_handle[i]) { + return i; + } + } + return -1; +} + +static void hal_fan_button_signle_click_cb(void *arg, void *data) +{ + static int bldc_speed[3] = {BLDC_MIN_SPEED, BLDC_MID_SPEED, BLDC_HIGH_SPEED}; + esp_rmaker_param_t *rmaker_param = NULL; + if (iot_button_get_event(arg) == BUTTON_SINGLE_CLICK) { + switch (hal_fan_get_button_index((button_handle_t)arg)) { + case SETTING_START: + motor_parameter.start_count++; + ESP_LOGI(TAG, "Setting start, count:%d", motor_parameter.start_count); + + if (!motor_parameter.is_start) { + motor_parameter.target_speed = BLDC_MIN_SPEED; + hal_bldc_start_stop(1); + motor_parameter.restart_count = 0; + motor_parameter.start_count = 1; + } else if (motor_parameter.is_start && motor_parameter.start_count == 4) { + hal_bldc_start_stop(0); + motor_parameter.start_count = 0; + } else { + hal_bldc_set_speed(bldc_speed[motor_parameter.start_count - 1]); + motor_parameter.target_speed = bldc_speed[motor_parameter.start_count - 1]; + } + + rmaker_param = app_rainmaker_get_param("Power"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.b = motor_parameter.is_start; + val.type = RMAKER_VAL_TYPE_BOOLEAN; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current motor start status */ + } + rmaker_param = app_rainmaker_get_param("Speed"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.i = motor_parameter.target_speed; + val.type = RMAKER_VAL_TYPE_INTEGER; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current motor speed */ + } + break; + case SETTING_MODE: + ESP_LOGI(TAG, "Setting mode"); + motor_parameter.is_natural = !motor_parameter.is_natural; + rmaker_param = app_rainmaker_get_param("Natural"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.b = motor_parameter.is_natural; + val.type = RMAKER_VAL_TYPE_BOOLEAN; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current motor mode */ + } + break; + case SETTING_SHAKING_HEAD: + ESP_LOGI(TAG, "Setting shaking head"); + stepper_motor.is_start = !stepper_motor.is_start; + rmaker_param = app_rainmaker_get_param("Shake"); + if (rmaker_param != NULL) { + esp_rmaker_param_val_t val = {0}; + val.val.b = stepper_motor.is_start; + val.type = RMAKER_VAL_TYPE_BOOLEAN; + esp_rmaker_param_update_and_report(rmaker_param, val); /*!< Update current stepper motor start status */ + } + break; + case SETTING_TIME: + if (++hal_fan_button.fan_timing_count >= MAXINUM_TIMING_COUNT) { + hal_fan_button.fan_timing_count = 1; + } + ESP_LOGI(TAG, "Setting time:%lu us", hal_fan_button.fan_timing_count * SIGNLE_TIMING_DURATION); + if (esp_timer_is_active(hal_fan_button.fan_oneshot_timer)) { + esp_timer_stop(hal_fan_button.fan_oneshot_timer); /*!< Shut down the original timer before starting the timer */ + } + ESP_ERROR_CHECK(esp_timer_start_once(hal_fan_button.fan_oneshot_timer, hal_fan_button.fan_timing_count * SIGNLE_TIMING_DURATION)); + break; + default: + break; + } + } +} + +static void hal_fan_oneshot_timer_cb(void *arg) +{ + ESP_LOGI(TAG, "Count is over"); + if (motor_parameter.is_start) { + motor_parameter.is_start = ! motor_parameter.is_start; + hal_bldc_start_stop(motor_parameter.is_start); /*!< Shut down the motor only when it is running */ + } +} + +esp_err_t hal_fan_button_init(gpio_num_t start_pin, gpio_num_t mode_pin, gpio_num_t shacking_head_pin, gpio_num_t timer_pin) +{ + gpio_num_t pin[] = {start_pin, mode_pin, shacking_head_pin, timer_pin}; + + gpio_config_t io_config = { + .pin_bit_mask = (1ULL << start_pin) | (1ULL << mode_pin) | (1ULL << shacking_head_pin) | (1ULL << timer_pin), + .mode = GPIO_MODE_INPUT, + .pull_down_en = 0, + .pull_up_en = 0, + .intr_type = GPIO_INTR_DISABLE, + }; + memcpy(hal_fan_button.pin, pin, sizeof(pin)); + + if (gpio_config(&io_config) != ESP_OK) { + return ESP_FAIL; + } + + button_config_t cfg = { + .type = BUTTON_TYPE_GPIO, + .long_press_time = 5000, + .short_press_time = 200, + .gpio_button_config = { + .gpio_num = pin[0], + .active_level = 0, + }, + }; + + for (int i = 0; i < 4; i++) { + cfg.gpio_button_config.gpio_num = pin[i]; + hal_fan_button_handle[i] = iot_button_create(&cfg); + iot_button_register_cb(hal_fan_button_handle[i], BUTTON_SINGLE_CLICK, hal_fan_button_signle_click_cb, NULL); + } + + hal_fan_button.fan_timing_count = 0; + const esp_timer_create_args_t oneshot_timer_args = { + .callback = &hal_fan_oneshot_timer_cb, + }; + ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &hal_fan_button.fan_oneshot_timer)); + + return ESP_OK; +} diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/include/bldc_user_cfg.h b/examples/motor/bldc_fan_rainmaker/main/hal/include/bldc_user_cfg.h index 97e3aa9fa..10aa5379e 100644 --- a/examples/motor/bldc_fan_rainmaker/main/hal/include/bldc_user_cfg.h +++ b/examples/motor/bldc_fan_rainmaker/main/hal/include/bldc_user_cfg.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -72,7 +72,7 @@ extern "C" { * Used to lock the motor in a specific phase * before strong dragging. */ -#define ALIGNMENTNMS (300) /*!< Duration of alignment, too short may not reach the position, too long may cause the motor to overheat. */ +#define ALIGNMENTNMS (500) /*!< Duration of alignment, too short may not reach the position, too long may cause the motor to overheat. */ #define ALIGNMENTDUTY (PWM_DUTYCYCLE_20) /*!< alignment torque. */ /** @@ -81,10 +81,10 @@ extern "C" { * @note If the control cycle speeds up, corresponding reductions * should be made to the RAMP_TIM_STA, RAMP_TIM_END, RAMP_TIM_STEP */ -#define RAMP_TIM_STA (900) /*!< The start step time for climbing. A smaller value results in faster startup but may lead to overcurrent issues. */ +#define RAMP_TIM_STA (1500) /*!< The start step time for climbing. A smaller value results in faster startup but may lead to overcurrent issues. */ #define RAMP_TIM_END (180) /*!< The end step time for climbing, adjusted based on the load. If loaded, this value should be relatively larger. */ #define RAMP_TIM_STEP (15) /*!< Decremental increment for climbing step time—adjusted in accordance with RAMP_TIM_STA. */ -#define RAMP_DUTY_STA (PWM_DUTYCYCLE_20) /*!< The starting torque for climbing. */ +#define RAMP_DUTY_STA (PWM_DUTYCYCLE_15) /*!< The starting torque for climbing. */ #define RAMP_DUTY_END (PWM_DUTYCYCLE_40) /*!< The ending torque for climbing. */ #define RAMP_DUTY_INC (13) /*!< The incremental torque step for climbing—too small a value may result in failure to start, while too large a value may lead to overcurrent issues. */ @@ -101,7 +101,7 @@ extern "C" { * */ #define ZERO_STABLE_FLAG_CNT (4) /*!< After stable detection for multiple revolutions, it is considered to enter a sensorless state. */ -#define ZERO_CROSS_DETECTION_ACCURACY 0xFFFF /*!< Count a valid comparator value every consecutive detection for how many times. */ +#define ZERO_CROSS_DETECTION_ACCURACY 0xFFFFF /*!< Count a valid comparator value every consecutive detection for how many times. */ /** * @brief Common parameter for compensated commutation time calculation diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_bldc.h b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_bldc.h index 53bf5f222..d656e27b7 100644 --- a/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_bldc.h +++ b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_bldc.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,26 +8,40 @@ #include "bldc_control.h" +#ifdef __cplusplus +extern "C" { +#endif + /** - * @brief bldc init + * @brief Initializes the bldc * - * @param direction - * @return esp_err_t + * @param direction CW or CCW + * @return + * - ESP_OK: Success in initializing the bldc + * - ESP_FAIL: BLDC parameter or resource configuration error */ esp_err_t hal_bldc_init(dir_enum_t direction); /** - * @brief set bldc start or stop + * @brief Sets bldc operation status * - * @param status - * @return esp_err_t + * @param status 0 for stopping the motor, 1 for starting the motor + * @return + * - ESP_OK: Success in setting the status of bldc + * - other: Specific error what went wrong during setting. */ esp_err_t hal_bldc_start_stop(uint8_t status); /** - * @brief set bldc speed + * @brief Sets bldc speed * - * @param speed - * @return esp_err_t + * @param speed Desired speed of the BLDC + * @return + * - ESP_OK: Success in setting the speed of bldc + * - other: Specific error what went wrong during setting. */ esp_err_t hal_bldc_set_speed(uint16_t speed); + +#ifdef __cplusplus +} +#endif diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_fan_button.h b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_fan_button.h new file mode 100644 index 000000000..3228b80b7 --- /dev/null +++ b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_fan_button.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/gpio.h" +#include "esp_timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SETTING_START = 0, + SETTING_MODE, + SETTING_SHAKING_HEAD, + SETTING_TIME +} hal_fan_setting_t; + +typedef struct { + gpio_num_t pin[4]; + esp_timer_handle_t fan_oneshot_timer; + uint8_t fan_timing_count; +} hal_fan_button_t; + +/** + * @brief Initializes the fan button + * + * @param start_pin GPIO pin number + * @param mode_pin GPIO pin number + * @param shacking_head_pin GPIO pin number + * @param timer_pin GPIO pin number + * @return + * - ESP_OK: Success in initializing the fan button + * - ESP_FAIL: GPIO resource is not available + */ +esp_err_t hal_fan_button_init(gpio_num_t start_pin, gpio_num_t mode_pin, gpio_num_t shacking_head_pin, gpio_num_t timer_pin); + +#ifdef __cplusplus +} +#endif diff --git a/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_stepper_motor.h b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_stepper_motor.h index 8b2e23148..79b85e433 100644 --- a/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_stepper_motor.h +++ b/examples/motor/bldc_fan_rainmaker/main/hal/include/hal_stepper_motor.h @@ -9,6 +9,10 @@ #include "driver/gpio.h" #include "esp_timer.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { STEP_CW = 0, STEP_CCW, @@ -24,12 +28,18 @@ typedef struct { extern stepper_motor_t stepper_motor; /** - * @brief stepper init + * @brief Initializes the stepper * - * @param pin_1 - * @param pin_2 - * @param pin_3 - * @param pin_4 - * @return esp_err_t + * @param pin_1 GPIO number + * @param pin_2 GPIO number + * @param pin_3 GPIO number + * @param pin_4 GPIO number + * @return + * - ESP_OK:Success in initializing the stepper + * - ESP_FAIL: Esp timer or gpio resource is not available. */ esp_err_t hal_stepper_motor_init(gpio_num_t pin_1, gpio_num_t pin_2, gpio_num_t pin_3, gpio_num_t pin_4); + +#ifdef __cplusplus +} +#endif diff --git a/examples/motor/bldc_fan_rainmaker/main/main.c b/examples/motor/bldc_fan_rainmaker/main/main.c index 4511eda49..60538b192 100644 --- a/examples/motor/bldc_fan_rainmaker/main/main.c +++ b/examples/motor/bldc_fan_rainmaker/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,12 +13,15 @@ #include "app_variable.h" #include "app_rainmaker.h" #include "hal_stepper_motor.h" +#include "hal_fan_button.h" #include "hal_bldc.h" +#include "bldc_fan_config.h" void app_main(void) { app_variable_init(); app_rainmaker_init(); hal_bldc_init(CW); - hal_stepper_motor_init(GPIO_NUM_14, GPIO_NUM_21, GPIO_NUM_47, GPIO_NUM_48); + hal_stepper_motor_init(STEPPER_A_PIN, STEPPER_B_PIN, STEPPER_C_PIN, STEPPER_D_PIN); + hal_fan_button_init(SETTING_START_PIN, SETTING_MODE_PIN, SETTING_SHACKING_HEAD_PIN, SETTING_TIMER_PIN); }