Incremental rotary encoders (Part 10)

Part 1234567, 8, 9, 10

Let’s have some fun with encoders. As I was thinking about an alternative solution for the stepping motors of our friend Valério’s puppet, I started playing around with some DC motors which are quieter than servo-motors and less greedy and less complex to drive than the steppers. Problem is that DC-motor require additional features for controlling the amount of rotation. And I remembered very, very old projects that used Portescap motors featuring tandem motors: the big one as the real motor and a smaller one acting as a magneto. As the voltage generated by the magneto is proportional to the rotational speed, it performs as a tachometer.


This option is not suitable for my application, but I though about bending the original idea … from a tachometer to an encoder, there are just a few steps to fulfill !

The hardware is very, very simple. R1 and R2 (1 kOhm both), decoupled by C2 (0.1 µF, 10 V) are biasing one end of the motor while its other end is connected to an analog input. The analog reference is wired to the 3.3 output, decoupled by C1 (1.0 µF, 10 V).


Next figure illustrates the voltage read at the analog input A0 of arduino:


The motor that I used is very popular in the scrap boxes: the motor was disassembled from a CD player. It produces a clean positive or negative +/- 1 V output which is fully compatible with the 3.3 V adc range. An obvious improvement  would consist in protecting the analog input against excessive voltages.


The code is very simple and was written in a quick and dirty mode in order to validate the concept.

uint32_t _interval = 250; 	/* In ms */
uint32_t _lastTime; 		/* In ms */
uint16_t _meanValue = 512;
uint8_t _analogPin = 0;
int32_t _counter;

void Calibrate(void)
	uint8_t samples = 16;
	_meanValue = 0;
	for (uint8_t i = 0; i < samples; i++) {
		_meanValue += analogRead(_analogPin);
	_meanValue /= samples;

void setup(void)
	Serial.print("Mean value: ");
	_lastTime = (millis() - _interval);

void loop(void)
	uint32_t now = millis();
	if ((now - _lastTime) >= _interval) {
		_lastTime = now;
		int16_t val = analogRead(_analogPin);
		val -= _meanValue;
		val /= 3;
		/*  */
		if (abs(val) > 0) {
			_counter += val;
			if(_counter > 0) {

The setup routine initializes the serial port and the analog port. It performs an automatic calibration of the mean voltage which corresponds to the idle mode (Motionless motor). The loop routine periodically reads the analog port and converts the positive and negative swings in digital counts. Simple, cheap and easy! Although there is a lot of room for improvements, starting with the reading of the analog voltage which should be performed in an timed interrupt.

Leave a Reply

You must be logged in to post a comment.