I am using the Grove rotary encoder with a Xiao ESP32C6 sitting on a XIAO Expansion Board.
It mostly works, but I get an erroneous reading about 15% of the time (i.e. if I’m rotating solely up, I’ll get a number of erroneous down readings). They’re not extra readings, I still get one reading per detent.
I’m wondering if I’m doing something wrong or if this is caused by the missing capacitors referenced in this post?
Wiring
As the expansion board only has one(!) digital pin on a Grove header, I’ve repurposed the Grove Serial port, which according to the schematic is connected to Xiao pins 7 (TX) and 8 (RX) and nothing else (besides the servo header that I’m not using).
TX is the 3rd pin on the grove connector and corresponds to SIGB on the encoder board.
RX is the 4th pin on the grove connector and corresponds to SIGA on the encoder board.
On the ESP32C6 module itself, pin 7 is indeed TX but is also GPIO16. Pin 8 is RX and is also GPIO17.
So I think it’s wired correctly:
Encoder SIGB→Grove pin 3→TX→GPIO16
Encoder SIGA→Grove pin 4→RX→GPIO17
Code
I’ve stripped it down to a simple test case that does nothing else, besides use a FreeRTOS queue to minimise the time spent in the ISR.
I’ve used INPUT and not INPUT_PULLUP as the encoder board has built-in pullups.
#include <Arduino.h>
#include <Wire.h>
#define PIN_ROTARY_A 17
#define PIN_ROTARY_B 16
#define ENCODER_DOWN 0
#define ENCODER_UP 1
QueueHandle_t encoderQueue;
volatile unsigned long lastEncoderChange = 0;
void setup(void)
{
Serial.begin();
Serial.print("started\n");
pinMode(PIN_ROTARY_A, INPUT);
pinMode(PIN_ROTARY_B, INPUT);
if ((encoderQueue = xQueueCreate(500, 1)) == NULL) {
Serial.println("unable to create queue");
while (1) delay(1000);
}
attachInterrupt(digitalPinToInterrupt(PIN_ROTARY_A), encoderChange, RISING);
}
void ARDUINO_ISR_ATTR encoderChange()
{
if ((millis() - lastEncoderChange) < 50) // debounce time is 50ms
return;
// if (!digitalRead(PIN_ROTARY_A)) return;
uint8_t direction;
if (digitalRead(PIN_ROTARY_B) == HIGH) {
direction = ENCODER_DOWN;
}
else {
direction = ENCODER_UP;
}
if (xQueueSendToBackFromISR(encoderQueue, &direction, NULL) != pdPASS) {
Serial.println("queue full");
while (1) delay(1000);
}
lastEncoderChange = millis();
}
void loop()
{
uint8_t direction;
while (xQueueReceive(encoderQueue, &direction, 0) == pdPASS) {
if (direction == ENCODER_DOWN) {
Serial.println("down");
}
else {
Serial.println("up");
}
}
}
I’ve tried swapping A and B and swapping RISING to FALLING and both. I’ve also tried using random other pin numbers to make sure my mapping above is correct (with other pins it either doesn’t react because the interrupt never triggers, or it always shows one direction, so I think I’m correct).
Thanks!
-davidc






