Skip to content
Electronics Basics

Encoder Sensors and Motor Encoders: Quadrature, Wiring and Arduino Code

12 min readBeginner

An encoder sensor converts rotation or position into an electrical signal. A panel rotary encoder is used to navigate a menu or turn a virtual knob; a shaft encoder on a motor helps measure rotation, speed or direction. The important distinction is whether the device reports absolute position or produces pulses as the shaft moves, and whether it provides one signal or two phase-shifted signals for direction detection.

Quadrature channels A and B are phase-shifted; their sequence indicates direction.

Incremental versus absolute encoders

An incremental encoder produces pulses as the shaft moves. The controller counts transitions to estimate displacement. It usually cannot know the shaft's absolute position after power-up until it finds a reference mark or performs a homing move. An absolute encoder provides a position code that identifies shaft angle even after power is restored, depending on its interface and retained state.

Common incremental outputs include:

  • Single-channel pulse: count pulses for speed or approximate displacement if direction is known elsewhere.
  • Quadrature A/B: two signals offset by a quarter cycle. Their phase order reveals direction, and decoding both channels can improve position resolution.
  • Index/Z pulse: an extra reference pulse once per revolution on many shaft encoders.
  • Hall-based encoder: magnets and Hall sensors detect rotation; useful in some motor assemblies.
  • Optical encoder: a slotted disk or code track interrupts or modulates light.
  • Mechanical rotary encoder: metal contacts switch as the knob turns and may require debounce.

A panel rotary encoder such as a KY-040 is not the same device as an industrial servo encoder or the Hall sensors integrated into a motor. The outputs, voltage levels, resolution, current and mechanical construction vary.

Quadrature signals and direction

The A and B square waves are phase-shifted. If A changes before B, the motion direction is one way; if B changes before A, it is the other. Which direction is called positive depends on the wiring and the firmware's convention. Swapping A and B reverses the reported sign.

A decoder can count only rising edges on A, both edges on A, or transitions from both A and B. More transitions can improve the number of counts per shaft revolution but increase interrupt load. A datasheet may state pulses per revolution (PPR) or counts per revolution (CPR), and manufacturers do not always use these terms consistently. Confirm whether a value counts cycles on one channel or all decoded transitions.

For a simple example, if an encoder outputs 500 A-channel cycles per motor-shaft revolution and the firmware counts four edges per cycle, it may observe 2,000 counts per revolution at that shaft. If a gearbox has a 30:1 ratio, the output-shaft count is 60,000 counts per output revolution under the assumptions that the quoted 500 PPR is motor-shaft cycles and every transition is captured. Gearbox ratio, encoder location and decoding mode must be stated when interpreting counts.

RPM and distance calculations

If N counts are observed in a measurement interval t seconds, and the effective counts per shaft revolution is C, the speed magnitude is:

[ RPM = \frac{N}{C}\times\frac{60}{t} ]

The sign can be retained for direction if the quadrature decoder accumulates signed counts. The equation assumes the counter is not missing transitions and that C refers to the shaft being measured. A motor-mounted encoder measures motor shaft motion; a gearbox output may have different speed and effective counts per revolution.

For a wheel-driven robot, linear distance can be estimated from output-shaft revolutions and wheel circumference. Real distance also depends on wheel diameter, gear ratio, tire compression, slip and the actual path taken. Odometry is an estimate rather than a substitute for an external position reference.

Wiring an encoder to an Arduino Uno

For many 5 V panel rotary encoders, the contacts are intended for logic inputs, but verify the module. A basic quadrature setup connects encoder A to D2, B to D3 and GND to GND, with pull-ups appropriate to the module's circuit. The Uno R3's external interrupt pins are D2 and D3, which make them convenient for a simple example.

// Arduino Uno R3, counts one A-channel rising edge and samples B.
const uint8_t ENC_A = 2;
const uint8_t ENC_B = 3;
volatile long encoderCount = 0;

void encoderEdgeA() {
  if (digitalRead(ENC_B) == HIGH) encoderCount++;
  else encoderCount--;
}

void setup() {
  Serial.begin(115200);
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENC_A), encoderEdgeA, RISING);
}

void loop() {
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint >= 250) {
lastPrint = millis();
noInterrupts();
long count = encoderCount;
interrupts();
Serial.print("Encoder count: ");
Serial.println(count);
  }
}

This sketch is appropriate only when the signal speed is low enough for the selected decoding method and interrupt routine. If the encoder is a mechanical knob, contact bounce may create extra transitions. Use a suitable decoder library, interrupt strategy, filtering or hardware counter for the actual module and speed.

Wiring an encoder to an ESP32

Choose two input-capable GPIO pins that are available on your exact ESP32 board. GPIO numbers and restrictions differ between the classic ESP32, S2/S3, C3 and other variants. A classic ESP32-WROOM example might use GPIO18 and GPIO19 for A and B; do not copy those numbers to every board without checking the pinout.

Keep encoder outputs inside the ESP32's 3.3 V GPIO limits. Do not assume a 5 V encoder output is safe just because the ESP32 can read digital signals. For a 5 V encoder, use an appropriate level translator, open-collector wiring with a pull-up to 3.3 V when supported by the encoder output, or a compatible interface. Use a shared signal reference unless the interface is isolated by design.

Internal pull-ups may work for a low-speed encoder with short wires, but an industrial encoder or long cable often needs defined external biasing, shielding, input filtering and a line receiver suited to the encoder's output type. Differential RS-422 encoder outputs should be handled with the correct receiver, not connected directly to a GPIO.

Counting correctly: important implementation choices

  1. Atomic counter reads: if an interrupt modifies a multi-byte counter while the main program reads it, use a critical section or atomic technique appropriate to the platform.
  2. Wraparound and sign: plan how counters reset or overflow and how signed direction is handled.
  3. Interrupt load: decoding every transition at high speed can saturate CPU time. Consider a dedicated hardware counter such as a pulse-counter peripheral or a decoder IC.
  4. Bounce and noise: mechanical encoders can bounce; motor encoders can pick up switching noise. Use a suitable signal path, not arbitrary long delays in the interrupt.
  5. Index/reference handling: absolute positioning may require a homing sensor or index pulse after startup.
  6. Measurement interval: shorter windows respond faster but can give coarse RPM readings at low speed. Reciprocal timing between edges can improve low-speed estimates.
  7. Gear ratio: document whether counts are measured at the motor shaft, gearbox output or wheel.

How to select an encoder

Match the shaft diameter, mounting, resolution, maximum RPM, output type, supply voltage, signal levels, environmental protection, cable length, bearing life and connector. For a small user-interface knob, a basic mechanical quadrature encoder may be enough. For motor speed control, an encoder should match the controller's maximum edge rate and noise environment. For precise position or safety-related motion, evaluate accuracy, repeatability, missed counts, fault detection and system-level requirements.

Troubleshooting

SymptomPossible causeCheck
Count only increasesOne channel is disconnected or direction isn't decodedVerify A/B waveforms and pin mapping
Direction is reversedA and B are swapped or sign convention differsSwap channels or invert the sign in code
Count jumps while stationaryContact bounce, floating inputs or electrical noiseAdd correct biasing and signal filtering
Works slowly but fails at speedInterrupts are missed or decoder is overloadedReduce interrupt work, improve signal quality or use hardware decoding
ESP32 GPIO is damagedEncoder output exceeds 3.3 V toleranceVerify output type and use level translation
RPM fluctuates at low speedFew pulses fall in each fixed time windowMeasure period between edges or average longer
Position is lost after resetIncremental encoder has no absolute referenceHome the mechanism or use an absolute encoder

Choosing between common encoder types

Use caseTypical suitable choiceKey caution
Menu or setting knobMechanical incremental quadratureNeeds debounce and low-speed handling
Wheel or DC motor speed feedbackOptical or Hall incremental encoderMatch edge rate, resolution and voltage
Motor control with directionQuadrature A/B or a suitable differential encoderDecode direction and manage noise
Known shaft angle after power-upAbsolute encoder or homing routineInterface, code and reference method matter
Long industrial cableDifferential encoder interface such as RS-422 when supportedUse the correct line receiver and termination

For related concepts, study ESP32 GPIO input/output, PWM on the ESP32 and the NEMA 17 stepper reference. Encoder terminology varies across manufacturers, so always check how the datasheet defines PPR, CPR, resolution and shaft location.

Worked RPM example and the effect of decoding mode

Assume the encoder data sheet specifies 500 A-channel cycles per revolution of the measured shaft. The firmware counts four transitions per cycle, so the effective count is 2,000 counts/revolution. If the counter changes by 1,000 counts over 0.5 s, the estimated speed is RPM = (1000 / 2000) × (60 / 0.5) = 60 rpm.

If the firmware instead counts only one rising edge on A per cycle, the effective count is 500 counts/revolution and the same physical motion would produce a different counter delta. This is why a software parameter named PPR or CPR must match the actual decode method. If the encoder is mounted before a gearbox, convert through the gear ratio to estimate output-shaft RPM.

At low speed, a fixed 250 ms measurement window may contain only one or two pulses, causing the calculated RPM to jump between coarse values. Measuring the elapsed time between consecutive edges often gives a smoother low-speed estimate, while a longer count window works well at higher speed. A combined method can use edge timing at low speed and pulse counting at higher speed.

Noise, cable type and maximum edge rate

Motor drive switching can couple noise into encoder wiring, especially when signal and motor leads run side by side. Keep encoder signals and their returns together, route them away from switching nodes, add appropriate filtering at the receiver, and use differential line drivers/receivers for long industrial runs where the encoder supports them. Shielding and grounding need to follow the system's EMC strategy; connecting a cable shield at random points can create other problems.

Calculate the worst-case pulse frequency from maximum RPM and encoder cycles or edges per revolution before choosing an interrupt-based method. If the processor cannot reliably service the edge rate, use a hardware pulse counter, timer capture peripheral or dedicated quadrature decoder. Missing counts create an error that often looks like a motor-control tuning problem, when the actual limitation is signal acquisition.

Homing, absolute position and fault behavior

Incremental position is based on accumulated counts and can be lost if power resets or an edge is missed. A machine requiring a repeatable physical reference needs a homing sensor or index reference and a controlled startup sequence. Absolute encoders avoid a normal homing move only if the selected device, its interface and the controller's startup behavior are designed to use the absolute position reliably.

For any application that can collide with people or damage equipment, include independent limit detection and a fault response appropriate to the risk. A hobby encoder count is a measurement input, not a certified safety function.

Encoder bring-up checklist

Before calculating RPM, confirm the electrical output type and signal amplitude with the datasheet or an appropriate instrument. Some modules use push-pull logic, some use open collector outputs that need pull-ups, and industrial encoders may provide differential pairs. The correct receiver, cable and pull-up scheme depend on the output type; a generic GPIO connection is not suitable for every encoder.

Then turn the shaft slowly and observe A and B. Verify the order of transitions, confirm whether a full cycle is counted once or four times, and check the direction sign. Compare counts over a known number of turns before connecting the encoder to a closed-loop motor controller. If the count disagrees, inspect the gearbox ratio and whether the encoder measures the motor shaft or the output shaft before changing code.

For a long cable, high maximum RPM or electrically noisy motor, determine the maximum edge rate and signal-integrity requirement first. Use appropriate line receivers, shielding/grounding practice and hardware counters if required. A clean low-speed bench result does not prove that a high-speed or long-cable installation will count every edge.