ESP32 Rotary Encoder

Trong hướng dẫn này, bạn sẽ học cách sử dụng rotary encoder với ESP32. Đây là những gì chúng ta sẽ học:

Linh Kiện Cần Thiết

1×mô-đun phát triển ESP-WROOM-32
1×Alternatively, ESP32 Uno-form board
1×Alternatively, ESP32 S3 Uno-form board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×encoder xoay
1×breadboard
1×dây jumper
1×(Khuyến nghị) Screw Terminal Expansion Board for ESP32
1×(Khuyến nghị) Breakout Expansion Board for ESP32
1×(Khuyến nghị) Power Splitter for ESP32

Or you can buy the following kits:

1×DIYables ESP32 Starter Kit (ESP32 included)
1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)

Giới Thiệu Rotary Encoder

Một núm xoay, giống như trên radio, có thể gửi tín hiệu biến thành điện. Nó giúp chúng ta biết nó đã xoay bao nhiều và đặt ở đâu. Có hai loại chính:

  • Incremental encoder: Loại này sử dụng tín hiệu nhanh để đo lượng thay đổi vị trí của vật thể.
  • Absolute encoder: Loại này cung cấp mã riêng biệt cho mỗi vị trí, giúp chúng ta tìm ra vị trí của vật thể, ngay cả khi mất điện.

Bài học này chủ yếu về loại đầu tiên, incremental encoder.

Sơ Đồ Chân Rotary Encoder Module

encoder xoay sơ đồ chân

Module rotary encoder có 4 chân:

  • CLK pin (Output A): là xung chính cho biết lượng xoay đã xảy ra. Mỗi khi bạn xoay núm một detent (click) theo bất kỳ hướng nào, chân CLK xuất ra tín hiệu hoàn thành một chu kỳ đầy đủ (LOW HIGH LOW).
  • DT pin (Output B): hoạt động giống chân CLK nhưng xuất ra tín hiệu chậm hơn tín hiệu CLK 90 độ. Nó giúp chúng ta xác định hướng xoay (thuận chiều kim đồng hồ hay ngược chiều kim đồng hồ).
  • SW pin: đến từ nút bấm của encoder. Nó thường mở. Khi chúng ta thêm điện trở pull-up vào chân này, chân SW sẽ ở mức HIGH khi núm không được nhấn và LOW khi được nhấn.
  • VCC pin (+): cần được kết nối với VCC (giữa 3.3 và 5 volt)
  • GND pin: cần được kết nối với GND (0V)

Rotary Encoder so với Potentiometer

Bạn có thể nhầm lẫn rotary encoder với ESP32 - Potentiometer (Biến Trở Xoay). nhưng chúng là các linh kiện khác biệt. Dưới đây là so sánh giữa chúng:

  • Rotary encoder giống như phiên bản hiện đại của potentiometer, nhưng chúng có thể làm nhiều việc hơn.
  • Rotary encoder có thể xoay vòng tròn hoàn chỉnh mà không dừng lại, trong khi potentiometer chỉ có thể xoay khoảng ba phần tư vòng tròn.
  • Rotary encoder xuất ra xung, trong khi potentiometer xuất ra điện áp analog.
  • Rotary encoder tiện dụng khi bạn chỉ cần biết núm đã di chuyển bao nhiều, không cần biết chính xác vị trí. Potentiometer hữu ích khi bạn thực sự cần biết chính xác vị trí của núm.

Cách Hoạt Động Của Rotary Encoder

encoder xoay output

Bên trong encoder, có một đĩa nhỏ với các rãnh được kết nối với chân C, hoạt động như mass chung. Bạn có thêm hai chân A và B.

  • Khi bạn xoay núm, chân A và B chạm vào chân mass chung C, nhưng thứ tự chúng chạm phụ thuộc vào hướng bạn xoay núm (thuận chiều kim đồng hồ hay ngược chiều kim đồng hồ).
  • Những lần chạm này tạo ra hai tín hiệu. Chúng hơi khác nhau về thời gian vì một chân chạm mass trước chân kia. Những tín hiệu này lệch nhau 90 độ, được gọi là quadrature encoding.
  • Nếu bạn xoay núm theo chiều kim đồng hồ, chân A chạm mass trước chân B. Nhưng nếu xoay ngược chiều kim đồng hồ, chân B chạm mass trước chân A.
  • Bằng cách kiểm tra khi nào mỗi chân chạm hoặc rời khỏi mass, chúng ta có thể xác định hướng xoay của núm. Chúng ta làm điều này bằng cách kiểm tra chuyện gì xảy ra với chân B khi chân A thay đổi.
how encoder xoay works

Khi A thay đổi trạng thái từ LOW sang HIGH:

  • Nếu B ở mức HIGH, núm được xoay ngược chiều kim đồng hồ.
  • Nếu B ở mức LOW, núm được xoay theo chiều kim đồng hồ.

※ Lưu ý:

Chân A và B được kết nối với chân CLK và DT. Tuy nhiên, tùy thuộc vào nhà sản xuất, thứ tự có thể khác nhau. Các đoạn code được cung cấp dưới đây đã được thử nghiệm với rotary encoder từ DIYables

Cách Lập Trình Cho Rotary Encoder

  • ESP32 đọc tín hiệu từ chân CLK
  • Nếu trạng thái thay đổi từ LOW sang HIGH, thì ESP32 đọc trạng thái của chân DT.
    • Nếu chân DT ở mức HIGH, núm được xoay ngược chiều kim đồng hồ, ESP32 tăng bộ đếm lên 1
    • Nếu chân DT ở mức LOW, núm được xoay theo chiều kim đồng hồ, ESP32 giảm bộ đếm đi 1

Sơ Đồ Kết Nối

ESP32 encoder xoay sơ đồ đấu dây

This image is created using Fritzing. Click to enlarge image

Nếu bạn chưa rõ cách cấp nguồn cho ESP32 và các linh kiện khác, xem: Cách Cung Cấp Nguồn Điện Cho ESP32.

Code ESP32 – Rotary Encoder

Code ESP32 dưới đây thực hiện:

  • Phát hiện hướng và lượng xoay của encoder.
    • Nếu phát hiện núm xoay một detent (click) theo chiều kim đồng hồ, tăng bộ đếm lên một.
    • Nếu phát hiện núm xoay một detent (click) ngược chiều kim đồng hồ, giảm bộ đếm đi một.
  • Phát hiện nếu nút được nhấn.
/* * Mã ESP32 này được phát triển bởi newbiely.vn * Mã ESP32 này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/esp32/esp32-rotary-encoder */ #include <ezButton.h> // the library to use for SW pin #define CLK_PIN 25 // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin #define DT_PIN 26 // ESP32 pin GPIO26 connected to the rotary encoder's DT pin #define SW_PIN 27 // ESP32 pin GPIO27 connected to the rotary encoder's SW pin #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // counter-clockwise direction int counter = 0; int direction = DIRECTION_CW; int CLK_state; int prev_CLK_state; ezButton button(SW_PIN); // create ezButton object that attach to pin 7; void setup() { Serial.begin(9600); // configure encoder pins as inputs pinMode(CLK_PIN, INPUT); pinMode(DT_PIN, INPUT); button.setDebounceTime(50); // set debounce time to 50 milliseconds // read the initial state of the rotary encoder's CLK pin prev_CLK_state = digitalRead(CLK_PIN); } void loop() { button.loop(); // MUST call the loop() function first // read the current state of the rotary encoder's CLK pin CLK_state = digitalRead(CLK_PIN); // If the state of CLK is changed, then pulse occurred // React to only the rising edge (from LOW to HIGH) to avoid double count if (CLK_state != prev_CLK_state && CLK_state == HIGH) { // if the DT state is HIGH // the encoder is rotating in counter-clockwise direction => decrease the counter if (digitalRead(DT_PIN) == HIGH) { counter--; direction = DIRECTION_CCW; } else { // the encoder is rotating in clockwise direction => increase the counter counter++; direction = DIRECTION_CW; } Serial.print("Rotary Encoder:: direction: "); if (direction == DIRECTION_CW) Serial.print("Clockwise"); else Serial.print("Counter-clockwise"); Serial.print(" - count: "); Serial.println(counter); } // save last CLK state prev_CLK_state = CLK_state; if (button.isPressed()) { Serial.println("The button is pressed"); } }

Để đơn giản hóa code cho button debouncing, thư viện ezButton được sử dụng.

Các Bước Thực Hiện

Nếu đây là lần đầu bạn sử dụng ESP32, hãy xem ESP32 - Cài Đặt Phần Mềm.

  • Cài đặt thư viện ezButton trên Arduino IDE.
  • Copy code trên và mở bằng Arduino IDE
  • Click nút Upload trên Arduino IDE để upload code lên ESP32
  • Xoay núm theo chiều kim đồng hồ, sau đó ngược chiều kim đồng hồ
  • Nhấn núm
  • Xem kết quả trên Serial Monitor.
COM6
Send
Rotary Encoder:: direction: CLOCKWISE - count: 1 Rotary Encoder:: direction: CLOCKWISE - count: 2 Rotary Encoder:: direction: CLOCKWISE - count: 3 Rotary Encoder:: direction: CLOCKWISE - count: 4 Rotary Encoder:: direction: CLOCKWISE - count: 5 Rotary Encoder:: direction: ANTICLOCKWISE - count: 4 Rotary Encoder:: direction: ANTICLOCKWISE - count: 3 Rotary Encoder:: direction: ANTICLOCKWISE - count: 2 Rotary Encoder:: direction: ANTICLOCKWISE - count: 1 Rotary Encoder:: direction: ANTICLOCKWISE - count: 0 The button is pressed
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Giải Thích Code

Xem các comment từng dòng trong code

Code ESP32 – Rotary Encoder với Interrupt

Trong code trước, việc sử dụng polling để liên tục kiểm tra trạng thái chân có thể lãng phí tài nguyên ESP32 và dẫn đến bỏ lỡ đếm nếu việc thực thi code khác chậm.

Một giải pháp hiệu quả là sử dụng interrupt, loại bỏ sự cần thiết của polling. Điều này cho phép ESP32 thực hiện các tác vụ khác mà không bỏ lỡ việc đếm. Dưới đây là code ESP32 sử dụng interrupt để đọc hướng và vị trí từ rotary encoder.

/* * Mã ESP32 này được phát triển bởi newbiely.vn * Mã ESP32 này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/esp32/esp32-rotary-encoder */ #include <ezButton.h> // the library to use for SW pin #define CLK_PIN 25 // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin #define DT_PIN 26 // ESP32 pin GPIO26 connected to the rotary encoder's DT pin #define SW_PIN 27 // ESP32 pin GPIO27 connected to the rotary encoder's SW pin #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // counter-clockwise direction volatile int counter = 0; volatile int direction = DIRECTION_CW; volatile unsigned long last_time; // for debouncing int prev_counter; ezButton button(SW_PIN); // create ezButton object that attach to pin 7; void IRAM_ATTR ISR_encoder() { if ((millis() - last_time) < 50) // debounce time is 50ms return; if (digitalRead(DT_PIN) == HIGH) { // the encoder is rotating in counter-clockwise direction => decrease the counter counter--; direction = DIRECTION_CCW; } else { // the encoder is rotating in clockwise direction => increase the counter counter++; direction = DIRECTION_CW; } last_time = millis(); } void setup() { Serial.begin(9600); // configure encoder pins as inputs pinMode(CLK_PIN, INPUT); pinMode(DT_PIN, INPUT); button.setDebounceTime(50); // set debounce time to 50 milliseconds // use interrupt for CLK pin is enough // call ISR_encoder() when CLK pin changes from LOW to HIGH attachInterrupt(digitalPinToInterrupt(CLK_PIN), ISR_encoder, RISING); } void loop() { button.loop(); // MUST call the loop() function first if (prev_counter != counter) { Serial.print("Rotary Encoder:: direction: "); if (direction == DIRECTION_CW) Serial.print("CLOCKWISE"); else Serial.print("ANTICLOCKWISE"); Serial.print(" - count: "); Serial.println(counter); prev_counter = counter; } if (button.isPressed()) { Serial.println("The button is pressed"); } // TO DO: your other work here }

Bây giờ, khi bạn xoay núm, bạn sẽ thấy thông tin xuất hiện trên Serial Monitor, giống như những gì bạn đã thấy trong code trước đó.

※ Lưu ý:

  • Bạn có thể gặp phải các hướng dẫn trên các trang web khác sử dụng hai interrupt cho một encoder, nhưng điều này không cần thiết và lãng phí. Chỉ cần một interrupt là đủ.
  • Quan trọng là phải sử dụng từ khóa volatile cho các biến global được sử dụng trong interrupt. Bỏ qua điều này có thể dẫn đến các vấn đề không mong muốn.
  • Giữ code trong interrupt đơn giản nhất có thể. Tránh sử dụng Serial.print() hoặc Serial.println() bên trong interrupt.

Ứng Dụng ESP32 Rotary Encoder

Với Rotary Encoder, chúng ta có thể thực hiện các ứng dụng sau nhưng không giới hạn:

  • ESP32 - Rotary Encoder điều khiển Vị trí của Servo Motor
  • ESP32 - Rotary Encoder điều khiển Độ sáng của LED
  • ESP32 - Rotary Encoder điều khiển Tốc độ của Stepper Motor

Video Tutorial

Việc sản xuất video tốn rất nhiều thời gian. Nếu video hướng dẫn hữu ích cho việc học của bạn, hãy đăng ký kênh YouTube để ủng hộ. Nếu nhu cầu đủ cao, chúng tôi sẽ cố gắng làm thêm nhiều video.

Tham Chiếu Hàm