Problem when measuring motor RPM with interrupt

Hi all!

I’m new to the forum, so I hope my first post will be all correct.

I am working on building a motor test bed for drone electric motors. The test bed has 3 load cells wired up to measure thrust and torque using 3 separate HX711 chips, which this forum helped me a lot to configure and properly set-up. Then two analog inputs are used to measure the current and voltage to the motor, and finally there is the RPM measurement, which is where I have the issue. The Arduino script then writes the data on the serial port, which is then read, plotted live and recorded by a Matlab script.

This is the set-up for the RPM Measurement.
One LED on one side points at a photodiode on the other side which is connected to PIN 2 of the Arduino, where in code an interrupt is set-up. This set-up should work because has already been used previously. The propeller is in between the LED and the photodiode.
The motor RPM range to be measured is from about 5000 RPM to 30000 RPM; the propeller is two-bladed.

All the other measurements in the main script work fine, but I had strange RPM readings, so I stripped out the code just to the bare minimum to debug what was going on; this is the code:

// Libraries needed
#include <Servo.h>

// Pin Settings
int TachometerPin     = 2;
int MotorPin          = 3;

// Variables needed for propeller angular speed calculations from tachometer
volatile unsigned long TimeOfInterruptOld;
volatile unsigned long TimeOfInterruptNew;
volatile unsigned long BladePeriod;

// Costum objects declarations
Servo Motor;

// -----------------------------------------------------------------------------------------------------------------
void setup() {

  // Begin serial communication with specified Baud rate
  Serial.begin(115200);
  
  // Interrupt routine executed to calculate the motor RPM
  attachInterrupt(digitalPinToInterrupt(TachometerPin), MeasureOmega, FALLING);

  // Set up digital pins
  pinMode(TachometerPin, INPUT);
  Motor.attach(MotorPin);

  // Arm the ESC (needed at first start-up), by proving a signal at its minimum throttle
  Motor.writeMicroseconds(1100);
  delay(2000);
  Motor.writeMicroseconds(1300);
}
// -----------------------------------------------------------------------------------------------------------------

// -----------------------------------------------------------------------------------------------------------------
void loop() {

  // Sent data to serial interface
  Serial.println(BladePeriod);
  delay(100);

}
// -----------------------------------------------------------------------------------------------------------------


// -----------------------------------------------------------------------------------------------------------------
// Interrupt routine, executed in order to calculate the motor angular speed
void MeasureOmega() {

  // Those are the two consecutive time instants when the Interrupt Routine was called (corresponds to the time between the passage of tho consecutive blades)
  TimeOfInterruptOld = TimeOfInterruptNew;

  TimeOfInterruptNew = micros();
  
  BladePeriod = (TimeOfInterruptNew - TimeOfInterruptOld);

}
// -----------------------------------------------------------------------------------------------------------------

The delay of 100 ms simulates the behaviour of the real full script, where all the data readings and serial writings makes the overall sampling frequency of about 8-9 Hz (mainly limited by the HX711).

Now I tested this with 25% throttle, which from the engine datasheet should give around 13800 RPM, or a BladePeriod of about 2170 microSeconds (if I did my calculations correctly).

However, this is the mess that I get from the serial monitor:

18:54:12.965 → 152
18:54:13.060 → 800
18:54:13.199 → 2292
18:54:13.293 → 3796
18:54:13.386 → 2292
18:54:13.479 → 760
18:54:13.570 → 1192
18:54:13.662 → 2296
18:54:13.801 → 2292
18:54:13.894 → 2504
18:54:13.986 → 2228
18:54:14.078 → 2292
18:54:14.171 → 1800
18:54:14.262 → 2296
18:54:14.400 → 2504
18:54:14.492 → 2292
18:54:14.584 → 2304
18:54:14.675 → 3720
18:54:14.769 → 2296
18:54:14.906 → 1412
18:54:14.999 → 2300
18:54:15.092 → 2300
18:54:15.183 → 1604
18:54:15.274 → 2292
18:54:15.368 → 3468
18:54:15.507 → 2304
18:54:15.600 → 2476
18:54:15.692 → 2296
18:54:15.784 → 12
18:54:15.877 → 3696
18:54:15.969 → 636
18:54:16.110 → 1856
18:54:16.202 → 2300
18:54:16.295 → 2504
18:54:16.387 → 2012

( It reached also higher values, like around 5000 )

There seems to be kind of an average value of around 2300 microSeconds (which is fairly reasonable), but I don’t understand why the data is so variable… Any hints?

Many thanks in advance!!!

Thanks for using code tags on your first post!

Multibyte variables must be protected from corruption, if they are modified by an interrupt routine.

In this call, BladePeriod is probably being modified by the interrupt routine while Serial.print() is attempting to work with it.

  Serial.println(BladePeriod);

A common approach is something like this:

void loop() {
  static unsigned long BladePeriodCopy;
  noInterrupts();
  BladePeriodCopy=BladePeriod;
  interrupts();
  Serial.println(BladePeriodCopy);
  delay(100);

If that doesn’t fix the problem completely, you may be getting multiple interrupts as the blade passes though the light beam, similar to “switch bounce”. Post your circuit diagram and links to the components.

You have to disable interrupts in your main loop() and then make a copy of BladePeriod since it is a 4 byte variable and can not be copies in an atomic fashon

void loop() {

  unsigned long period;

  noInterrupts();
  period = BladePeriod;
  interrupts();
  
  // Sent data to serial interface
  Serial.println(period);
  delay(100);

}

Hm code seems to be OK though I'm not that experienced in interrupts.

Some ideas:

is the photodiode sensitiv to ambient light?
even old fashioned glow-wire-lightbulbs flicker at 50/60Hz. some cheap LED_light-bulbs do flicker "really good"
same thing with light-tubes

If it is this trying it with just your smartphone flashlight switched on would make the difference.
Do you have an oscilloscope to analyse the signal of the photodiode?

Do you have a digital multimeter to measure the frequency that is on the output of your photodiode?

best regards Stefan

First of all thanks for all the super quick answers!

So I'll post the wiring diagram of the photodiode, it is really bad because I'm not used to doing these diagrams Then on pin A0 and A1 I have the two analog inputs from the current and voltage sensore, D3 is used to generate the PWM to the motor (via its own dedicated Eleconic Speed Controller), and D4 to D7 are for the 3 HX711 (one for common clock, and 3 for the readings).
I don't know which photodiode is (they were already hanging around in the lab); I should be able to get the laser datasheet tomorrow.

Actually I was mistaken yesterday, we are not using an LED as a source of light but a red laser, with the beam that can be focused.
At this point I don't think that the problem might be flickering, both because it is a laser and because I tried to manually see when the interrupt was called by blocking the light manually with my hand and it looked ok. I'll try with the flashlight on to see if there is any difference.

I'll try the suggestions of blocking the interrupts. One follow-up question about this: if I implement the

  noInterrupts();
period = BladePeriod;
 interrupts();
8 others single calls to Serial.println to send all sensor parameters

Would it cause any problems with respect to either the RPM measurement or the motor PWM control with the servo library?

One last doubt I had: do I have to care about which type of data is sent from Arduino and then read from Matlab over serial (int, float, unsigned long, byte, etc), or is it all taken care of behind the scenes?
Being used to Matlab and almost no experience in C I am struggling a bit with variable declaration and different variables types...

circuit (1).png

circuit (1).png

Update:

Implemented the suggestion of blocking the interrupts and making a copy of the BladePeriod variable and now it works.

Thanks again for the help!!