# DC Motor Speed Measurement using Rotary Encoder

I have written the following code to measure the speed of a unidirectional DC Motor.I have attempted to measure the time between successive interrupts in order to calculate speed.

``````boolean check;
boolean start;
unsigned int angle = 5;   //angle moved per encoder tick
unsigned long volatile time1;
unsigned long volatile time_11;
unsigned long interrupt_time;

void setup() {
Serial.begin(9600);
while (!Serial) ;
pinMode(21, INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
attachInterrupt(digitalPinToInterrupt(2), speed, FALLING);
time1 = 0;
time_11 = 0;
interrupt_time = 0;
check = true;
start = false;
time_11 = millis(); //Start Clock
}

void loop() {
if(time1 == interrupt_time && start) //Both times same , hence bool start.
{
if(check)
{
Serial.print(millis());  //Time taken to rise to steady speed.
check = false;
}
Serial.println((angle/time1)*1000);   //Display Speed.
}
interrupt_time = time1;
start = false;
while(!start)
{}
}

void speed(void) {
start = true;
time1 = millis()-time_11;
time_11 = millis();
}
``````

I want the code to be as efficient as possible.I have around 100 ticks per 360 degrees from my encoder.Is there any chance of "missing" an interrupt?

• I think you'd be better of calculating the speed based on the number of pulses during a certain amount of time, instead of the time of a single pulse. Secondly, you have to use atomic operations on multi-byte variables that are changed by the interrupt-routine. As e.g. during the comparing of `time1` in the `loop` the ISR might be called and the value of `time1` would change. The if statement might have compared the first two bytes of the long, but then checks the last two bytes of the new value. I hope this makes sense. May 21, 2016 at 13:44
• millis() probably won't work for this, unless your motor is slow and your encoder low resolution there would typically be less than a millisecond between encoder events. If you really want to time individual events, learn to use the hardware timer and wire the inputs accordingly. Otherwise counting events per period of time is better. Depending on wiring and details you may be able to count events with the hardware timer's counter mode. Oct 19, 2016 at 15:34
• The HW timer counter mode doesn't scale well, though. Taking a time stamp of the even and calculating the delta from the previous allows to use even the PCINT pins. Nov 18, 2016 at 17:47
• `Serial.println((angle/time1)*1000);` Be careful to not divide by 0. Also be aware that this is integer division and thus it will truncate (before multiplying by 1000). Mar 14, 2018 at 23:11
• How fast do you expect the motor to run? And how fast do you want to report on the speed? Oct 30, 2021 at 18:22

Just a couple of remarks:

1. `start` should be volatile, but you can remove it and simply check that `time1 != 0`.

2. there is no point in calling `millis()` twice inside the interrupt service routine:

``````void speed(void) {
unsigned long now = millis();
time1 = now - time_11;
time_11 = now;
}
``````

Thank you for the feedback! This is my attempt at calculating speed using the averaging method.

``````unsigned long volatile encodercount;
unsigned long encodercount_previous;
unsigned long volatile timer;
unsigned int angle;
boolean first;
boolean firstencodertick;
void setup() {
angle=3;
encodercount=0;
encodercount_previous=0;
Serial.begin(9600);
while (!Serial);
pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
attachInterrupt(digitalPinToInterrupt(2),speed,FALLING);
first=true;
firstecnodertick=true;
}

void loop() {
delay(100);

if(encodercount==encodercount_previous&&encodercount!=0)
{
if(first)
{
first=false;
}
Serial.println((encodercount); //Angle Moved in 0.1second.
}
encodercount_previous=encodercount;
encodercount=0;
}

void speed(void)
{
if(firstencodertick)
{
timer=millis();
firstencodertick=false;
}
encodercount++;
}
``````