I am working on a project, where I need to get reliable data from 4 incremental rotary encoders. The motors can spin at a maximum of 100 RPM, with 420 pulses per revolution. This makes a single encoder produce a square signal of around 700 Hz. Currently, the encoders are connected to the GPIO expander via I2C (400kHz), that fires an interrupt everytime one of the pins changes states (channel A and B x 4 = 8 pin changes). This works well for one motor, however, when I use multiple encoders, the frequency varies with time, as some motors rotate with a slightly different RPM at the same voltage.

I am using an ESP32 (DEVKIT-V1) microcontroller, and it is loaded with other tasks (I2C readings, PWM outputs, algorithm calculations, LCD control, etc.). Reading the data therefore produces innacurate results, as the processor misses ticks, when it is busy doing some other task.

My goal is to get the RPM of 4 motors to the ESP32 at around 10 Hz, so data can be used in the algorithm.

I currently see a few options:

  • Try to find a multi-channel quadrature decoder IC that would communicate the number of ticks via I2C (not yet found).
  • Include an additional dedicated microcontroller, which would obtain the data from the 4 encoders, and send the RPM values via I2C to the host processor in 10 Hz intervals. I am thinking about STM32's that include a quadrature encoder interface hardware.

What do you think would be the most practical and robust solution to get reliable RPM data from 4 encoders?

Thank you for your time.

  • \$\begingroup\$ STM32 doesn't have an encoder process unit like QEI, QEP,...etc. So you'd better to use PIC, Freescale, TI, ....MCU if you want to measure trouble free. \$\endgroup\$ Mar 19, 2022 at 12:49
  • \$\begingroup\$ Thank you for your reply @Marko. I think it does, at least it is stated in the datasheet. However, I have never used encoders on STM32's, so I am relatively new in this ecosystem. \$\endgroup\$
    – Tony
    Mar 19, 2022 at 13:46
  • \$\begingroup\$ Why are you using an i2c device, rather than directly connecting the encoder outputs to digital I/P pins? Since you only want RPM, there is no need for 2 signals in quadrature (that would also indicate the direction of rotation), and no need to interrupt on both edges of the signal. By eliminating the i2c interface, and interrupting on one edge only, you'll dramatically reduce the CPU workload, so the ESP32 should be able to handle the I/P interrupt rate. \$\endgroup\$
    – jayben
    Mar 23, 2022 at 17:27

2 Answers 2


You can derive the speed from an quadrature encoder with two methods: frequency or period measurement. Both methods have a certain error, which is related to

  1. frequency measurement : the measured signal frequency, gating time
  2. period measurement : the measured signal frequency , internal clock gate frequency

QEP unit is hybrid device that switches both modes with enhanced internal gate clock generation and timing. Without it, you have to calculate the error of both methods and then choose one of them. STM32 will only count up/down the encoder pulses, it is up to you to implement the software to get the speed of it.

However you can find some article describing the frequency/period measurement with FPGA, maybe you could get a hint.

IMO, for 700Hz max input frequency you should use period measurement method - at each pulse you read/reset the internal fast timer.

  • \$\begingroup\$ Hvala za predloge @Marko. I will research your suggestions some more. \$\endgroup\$
    – Tony
    Mar 20, 2022 at 7:41

If you have 4 A/D channels available, I'd recommend putting each encoder through an F/V converter, then sample the 4 analog inputs to get a direct rpm measurement.

This is an "impure" approach, as it doesn't keep your data in the digital domain, and requires external hardware. However, it drastically offloads processing to the converters - and you apparently don't have the bandwidth you need to service the sensors.

  • \$\begingroup\$ Hey, thank you for your reply. It is the ESP32 DEVKIT-V1. Very interesting solution, however, most of my pins are already reserved for other parts of the system. I am looking for the encoder data to be available via I2C, if possible. \$\endgroup\$
    – Tony
    Mar 19, 2022 at 13:52
  • \$\begingroup\$ @Tony - Well, your problem is clearly dealing with an extra 2800 interrupts a second. I think you need an external uC. As a matter of fact, you could do more than just the motors on this second board, which would free up pins on your primary. \$\endgroup\$ Mar 19, 2022 at 13:59
  • \$\begingroup\$ I agree with your suggestion @WhatRoughBeast. Have you had any similiar experiences, or do you recommend any uC in particular? \$\endgroup\$
    – Tony
    Mar 19, 2022 at 14:11
  • \$\begingroup\$ @Tony - I'm a retired aerospace design engineer, so I've done MUCH bigger projects. Just not using this sort of component. Can't you use the onboard resources to get two ESP32s talking to each other? If it comes to that, you could do the transfer as 4 separate transfers with a fixed interval between them, so the slave is never occupied for long enough doing a transfer to miss consecutive motor interrupts. \$\endgroup\$ Mar 19, 2022 at 14:21
  • \$\begingroup\$ Sounds like a great idea. Using an additonal ESP32 and not the STM32 would also allow using the same software ecosystem for both processors, which is a great advantage. Additionally, the second ESP32 could be running on two cores via FreeRTOS, as it does not need to communicate via Bluetooth. Thank you for your insight. \$\endgroup\$
    – Tony
    Mar 19, 2022 at 14:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.