Losing encoder pulses from serial print

Hello, I am running motor and printing rpm, but the output at 120 rpm varies 12 rpm. I've narrowed it down to loss of pulses during the serial print. The time between pulses should be 157 usec at 120 rpm. Any ideas on how to fix this?

volatile unsigned long timenew;
volatile unsigned long timeold;
volatile unsigned long difference;
int pwm_value = 255; // set PWM between 0 and 255
double rpm;

void setup()
{
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), counter, CHANGE);
  Serial.begin(9600);
  pinMode(5, OUTPUT);
  rpm = 0;
  timeold = 0;
  analogWrite(5, pwm_value);
}

void loop()
{
    rpm = 1000000 / (difference) * 60 / 288; //3 magnets (6 poles) and 48:1 gearbox
    if (rpm < 1000) // first rpm is very high and screws up the plotter
      {
      Serial.print("  rpm: "); Serial.println(rpm);
      }
}

void counter()
{
  timenew = micros();
  difference = timenew - timeold;
  timeold = timenew;
}

Increase the serial Baud rate to something reasonable, like 115200. Set the serial monitor to match.

Fault number one! As difference is an unsigned long, 32 bits, 4 bytes, and the controller is an 8 bit device then an interrupt can change difference while the execution is on the way to read that long.
In science it's named "critical region", a part of code that can't be interrupted, the reading of four bytes.
I suggest this coding:

void loop()
{
    unsigned long tmpDiff ;
    noInterrupts();
    tmpDiff = difference;
   interrupts();
   rpm = 1000000 / (tmpDiff) * 60 / 288; //3 magnets (6 poles) and 48:1 gearbox

Krank up the baudrate for Serial. I use 115200. My initial feeling is that the Serial.print will be overloaded.
Also add a timer that only enters and prints the RPM every 100 mS. No human eye will read that high speed Serial.print.

Measure and print alternately, by flushing the output buffer after you write to it. As it is now, serial interrupts are being called "behind your back" while you go on and make measurements. The two interrupts are taking whatever time they like to complete.

By calling 'flush()' you can be sure the output buffer is empty, so no serial interrupts will be called until you write something again.

After several permutations, I settled on printing the average every 100 ms, and turning off interrupts during the print.

int pwm_value = 255; // set PWM between 0 and 255
volatile unsigned long timenew;
volatile unsigned long timeold;
volatile int difference_count = 0;
volatile unsigned long difference_sum;
volatile bool first_read = true;
unsigned long difference;
double rpm;
unsigned long currentMillis;
unsigned long previousMillis;
double difference_ave = 0;

void setup() {
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), counter, CHANGE);
  Serial.begin(9600);
  pinMode(5, OUTPUT);
  timeold = 0;
  previousMillis = millis();
  analogWrite(5, pwm_value);
}

void loop() {
  currentMillis = millis();
  if (currentMillis - previousMillis >= 100) { // determine rpm and output
      noInterrupts();
      difference_ave = difference_sum/difference_count;
      rpm = 1000000 / (difference_ave) * 60 / 288; //3 magnets (6 poles) and 48:1 gearbox
      Serial.print("  rpm: "); Serial.println(rpm);
      first_read = true;
      difference_sum = 0;
      difference_count = 0;
      previousMillis = millis();
      interrupts();
  }
}

void counter() {
  if (first_read) {
    timeold = micros();
    first_read = false;
  }
  else {
    timenew = micros();
    difference = timenew - timeold;
    difference_sum += difference;
    difference_count += 1;
    timeold = timenew;
  }
}

Looks like you are asking for TWO interrupts per pulse on pin # 2. Why?
Paul

How did you arrive at this number?

Why do 3 calculations every time through loop() when only 1 variable ever changes? Why not:

static int konstant = 1000000 * 60 / 288; // calculated once at first run of loop()
rpm = konstant / difference;

NOTE: The compiler might be clever enough to do that anyway.

Bad idea. Serial communications are interrupt driven.