Compare commits
	
		
			5 Commits
		
	
	
		
			v1.0.0
			...
			f4ec446606
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f4ec446606 | |||
| db0e997aad | |||
| 1b872f8b82 | |||
| 27f59c6d92 | |||
| ff58109ff8 | 
							
								
								
									
										107
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,44 +1,20 @@ | |||||||
| # ESP32 ESP-IDF and ESP8266 RTOS SDK component for rotary encoder | # esp_component_template | ||||||
|  |  | ||||||
| ## Tested on | esp_component_template | ||||||
|  |  | ||||||
| 1. [ESP8266 RTOS_SDK v3.4](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/index.html#) |  | ||||||
| 2. [ESP32 ESP-IDF v5.4](https://docs.espressif.com/projects/esp-idf/en/release-v5.4/esp32/index.html) |  | ||||||
|  |  | ||||||
| ## Features |  | ||||||
|  |  | ||||||
| 1. Support some encoders on one device. |  | ||||||
|  |  | ||||||
| ## Note |  | ||||||
|  |  | ||||||
| 1. Encoder pins must be pull up to the VCC via 0.1 µf capacitors. |  | ||||||
|  |  | ||||||
| ## Using |  | ||||||
|  |  | ||||||
| In an existing project, run the following command to install the components: |  | ||||||
|  |  | ||||||
| ```text |  | ||||||
| cd ../your_project/components |  | ||||||
| git clone http://git.zh.com.ru/alexey.zholtikov/zh_encoder |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| In the application, add the component: |  | ||||||
|  |  | ||||||
| ```c |  | ||||||
| #include "zh_encoder.h" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Examples |  | ||||||
|  |  | ||||||
| One encoder on device: |  | ||||||
|  |  | ||||||
| ```c |  | ||||||
| #include "zh_encoder.h" | #include "zh_encoder.h" | ||||||
|  |  | ||||||
| zh_encoder_handle_t encoder_handle = {0}; | zh_encoder_handle_t encoder_handle = {0}; | ||||||
|  |  | ||||||
| void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); | void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); | ||||||
|  |  | ||||||
|  | // #define ROT_ENC_A_GPIO (CONFIG_ROT_ENC_A_GPIO) | ||||||
|  | // #define ROT_ENC_B_GPIO (CONFIG_ROT_ENC_B_GPIO) | ||||||
|  |  | ||||||
|  | // #define ENABLE_HALF_STEPS false  // Set to true to enable tracking of rotary encoder at half step resolution | ||||||
|  | // #define RESET_AT          0      // Set to a positive non-zero number to reset the position if this value is exceeded | ||||||
|  | // #define FLIP_DIRECTION    false  // Set to true to reverse the clockwise/counterclockwise sense | ||||||
|  |  | ||||||
| void app_main(void) | void app_main(void) | ||||||
| { | { | ||||||
|     esp_log_level_set("zh_encoder", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. |     esp_log_level_set("zh_encoder", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. | ||||||
| @@ -49,23 +25,64 @@ void app_main(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); | ||||||
| #endif | #endif | ||||||
|     zh_encoder_init_config_t encoder_init_config = ZH_ENCODER_INIT_CONFIG_DEFAULT(); |     zh_encoder_init_config_t encoder_init_config = ZH_ENCODER_INIT_CONFIG_DEFAULT(); | ||||||
|     encoder_init_config.a_gpio_number = GPIO_NUM_27; |     encoder_init_config.a_gpio_number = GPIO_NUM_26; | ||||||
|     encoder_init_config.b_gpio_number = GPIO_NUM_26; |     encoder_init_config.b_gpio_number = GPIO_NUM_27; | ||||||
|     // encoder_init_config.encoder_min_value = -10; // Just for example. |     encoder_init_config.encoder_min_value = -10; // Just for example. | ||||||
|     // encoder_init_config.encoder_max_value = 20;  // Just for example. |     encoder_init_config.encoder_max_value = 20;  // Just for example. | ||||||
|     // encoder_init_config.encoder_step = 0.1;      // Just for example. |     encoder_init_config.encoder_step = 0.1;      // Just for example. | ||||||
|     encoder_init_config.encoder_number = 1; |     encoder_init_config.encoder_number = 1; | ||||||
|     zh_encoder_init(&encoder_init_config, &encoder_handle); |     zh_encoder_init(&encoder_init_config, &encoder_handle); | ||||||
|     double position = 0; |     zh_encoder_reset(&encoder_handle);  // Just for example. | ||||||
|     zh_encoder_get(&encoder_handle, &position); |     zh_encoder_set(&encoder_handle, 5); // Just for example. | ||||||
|     printf("Encoder position %0.2f.\n", position); // For ESP8266 first disable "Component config -> Newlib -> Enable ‘nano’ formatting options for printf/scanf family" via menuconfig. |  | ||||||
|     // zh_encoder_set(&encoder_handle, 5); // Just for example. |     // esp32-rotary-encoder requires that the GPIO ISR service is installed before calling rotary_encoder_register() | ||||||
|     // zh_encoder_reset(&encoder_handle);  // Just for example. |     //     ESP_ERROR_CHECK(gpio_install_isr_service(0)); | ||||||
|  |  | ||||||
|  |     //     // Initialise the rotary encoder device with the GPIOs for A and B signals | ||||||
|  |     //     rotary_encoder_info_t info = { 0 }; | ||||||
|  |     //     ESP_ERROR_CHECK(rotary_encoder_init(&info, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO)); | ||||||
|  |     //     ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&info, ENABLE_HALF_STEPS)); | ||||||
|  |     // #ifdef FLIP_DIRECTION | ||||||
|  |     //     // ESP_ERROR_CHECK(rotary_encoder_flip_direction(&info)); | ||||||
|  |     // #endif | ||||||
|  |  | ||||||
|  |     //     // Create a queue for events from the rotary encoder driver. | ||||||
|  |     //     // Tasks can read from this queue to receive up to date position information. | ||||||
|  |     //     QueueHandle_t event_queue = rotary_encoder_create_queue(); | ||||||
|  |     //     ESP_ERROR_CHECK(rotary_encoder_set_queue(&info, event_queue)); | ||||||
|  |  | ||||||
|  |     //     while (1) | ||||||
|  |     //     { | ||||||
|  |     //         // Wait for incoming events on the event queue. | ||||||
|  |     //         rotary_encoder_event_t event = { 0 }; | ||||||
|  |     //         if (xQueueReceive(event_queue, &event, portMAX_DELAY) == pdTRUE) | ||||||
|  |     //         { | ||||||
|  |     //             ESP_LOGI(TAG, "Event: position %ld, direction %s", event.state.position, | ||||||
|  |     //                      event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET"); | ||||||
|  |     //         } | ||||||
|  |     //         // else | ||||||
|  |     // { | ||||||
|  |     //     // Poll current position and direction | ||||||
|  |     //     rotary_encoder_state_t state = { 0 }; | ||||||
|  |     //     ESP_ERROR_CHECK(rotary_encoder_get_state(&info, &state)); | ||||||
|  |     //     ESP_LOGI(TAG, "Poll: position %ld, direction %s", state.position, | ||||||
|  |     //              state.direction ? (state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET"); | ||||||
|  |  | ||||||
|  |     //     // Reset the device | ||||||
|  |     //     if (RESET_AT && (state.position >= RESET_AT || state.position <= -RESET_AT)) | ||||||
|  |     //     { | ||||||
|  |     //         ESP_LOGI(TAG, "Reset"); | ||||||
|  |     //         ESP_ERROR_CHECK(rotary_encoder_reset(&info)); | ||||||
|  |     //     } | ||||||
|  |     // } | ||||||
|  |     // } | ||||||
|  |     // ESP_LOGE(TAG, "queue receive failed"); | ||||||
|  |  | ||||||
|  |     // ESP_ERROR_CHECK(rotary_encoder_uninit(&info)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) | void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) | ||||||
| { | { | ||||||
|     zh_encoder_event_on_isr_t *event = event_data; |     zh_encoder_event_on_isr_t *event = event_data; | ||||||
|     printf("Encoder number %d position %.2f.\n", event->encoder_number, event->encoder_position); // For ESP8266 first disable "Component config -> Newlib -> Enable ‘nano’ formatting options for printf/scanf family" via menuconfig. |     printf("Uncoder number %d position %f.\n", event->encoder_number, event->encoder_position); | ||||||
| } | } | ||||||
| ``` |  | ||||||
|   | |||||||
| @@ -40,9 +40,9 @@ extern "C" | |||||||
|     { |     { | ||||||
|         uint8_t a_gpio_number;     // Encoder A GPIO number. |         uint8_t a_gpio_number;     // Encoder A GPIO number. | ||||||
|         uint8_t b_gpio_number;     // Encoder B GPIO number. |         uint8_t b_gpio_number;     // Encoder B GPIO number. | ||||||
|         int32_t encoder_min_value; // Encoder min value. |         int32_t encoder_min_value; // Encoder min value. @note Must be less than encoder_max_value. | ||||||
|         int32_t encoder_max_value; // Encoder max value. |         int32_t encoder_max_value; // Encoder max value. @note Must be greater than encoder_min_value. | ||||||
|         double encoder_step;       // Encoder step. |         double encoder_step;       // Encoder step. @note Must be greater than 0. | ||||||
|         double encoder_position;   // Encoder position. |         double encoder_position;   // Encoder position. | ||||||
|         uint8_t encoder_number;    // Encoder unique number. |         uint8_t encoder_number;    // Encoder unique number. | ||||||
|         uint8_t encoder_state;     // Encoder internal state. |         uint8_t encoder_state;     // Encoder internal state. | ||||||
| @@ -83,16 +83,6 @@ extern "C" | |||||||
|      */ |      */ | ||||||
|     esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position); |     esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @brief Get encoder position. |  | ||||||
|      * |  | ||||||
|      * @param[in] handle Pointer to unique encoder handle. |  | ||||||
|      * @param[out] position Encoder position. |  | ||||||
|      * |  | ||||||
|      * @return ESP_OK if success or an error code otherwise. |  | ||||||
|      */ |  | ||||||
|     esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Reset encoder position. |      * @brief Reset encoder position. | ||||||
|      * |      * | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								zh_encoder.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								zh_encoder.c
									
									
									
									
									
								
							| @@ -17,13 +17,22 @@ | |||||||
| #define ZH_ENCODER_DIRECTION_CW 0x10 | #define ZH_ENCODER_DIRECTION_CW 0x10 | ||||||
| #define ZH_ENCODER_DIRECTION_CCW 0x20 | #define ZH_ENCODER_DIRECTION_CCW 0x20 | ||||||
|  |  | ||||||
|  | // Create the half-step state table (emits a code at 00 and 11) | ||||||
|  | #define R_START 0x0 | ||||||
|  | #define H_CCW_BEGIN 0x1 | ||||||
|  | #define H_CW_BEGIN 0x2 | ||||||
|  | #define H_START_M 0x3 | ||||||
|  | #define H_CW_BEGIN_M 0x4 | ||||||
|  | #define H_CCW_BEGIN_M 0x5 | ||||||
|  |  | ||||||
| static const uint8_t _encoder_matrix[7][4] = { | static const uint8_t _encoder_matrix[7][4] = { | ||||||
|     {0x03, 0x02, 0x01, 0x00}, |     // 00                  01              10            11                   // BA | ||||||
|     {0x23, 0x00, 0x01, 0x00}, |     {H_START_M, H_CW_BEGIN, H_CCW_BEGIN, R_START},            // R_START (00) | ||||||
|     {0x13, 0x02, 0x00, 0x00}, |     {H_START_M | DIR_CCW, R_START, H_CCW_BEGIN, R_START},     // H_CCW_BEGIN | ||||||
|     {0x03, 0x05, 0x04, 0x00}, |     {H_START_M | DIR_CW, H_CW_BEGIN, R_START, R_START},       // H_CW_BEGIN | ||||||
|     {0x03, 0x03, 0x04, 0x00}, |     {H_START_M, H_CCW_BEGIN_M, H_CW_BEGIN_M, R_START},        // H_START_M (11) | ||||||
|     {0x03, 0x05, 0x03, 0x00}, |     {H_START_M, H_START_M, H_CW_BEGIN_M, R_START | DIR_CW},   // H_CW_BEGIN_M | ||||||
|  |     {H_START_M, H_CCW_BEGIN_M, H_START_M, R_START | DIR_CCW}, // H_CCW_BEGIN_M | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static QueueHandle_t _queue_handle = NULL; | static QueueHandle_t _queue_handle = NULL; | ||||||
| @@ -80,15 +89,6 @@ esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position) | |||||||
|     return ESP_OK; |     return ESP_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
| esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position) |  | ||||||
| { |  | ||||||
|     ZH_ENCODER_LOGI("Encoder get position started."); |  | ||||||
|     ZH_ENCODER_CHECK(handle->is_initialized == true, ESP_FAIL, "Encoder get position failed. Encoder not initialized."); |  | ||||||
|     *position = handle->encoder_position; |  | ||||||
|     ZH_ENCODER_LOGI("Encoder get position completed successfully."); |  | ||||||
|     return ESP_OK; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle) | esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle) | ||||||
| { | { | ||||||
|     ZH_ENCODER_LOGI("Encoder reset started."); |     ZH_ENCODER_LOGI("Encoder reset started."); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user