ISR's and Servo Timer Issues?

Hello All

I am trying to use dc42's code (from this forum post to measure a low frequency speed from the flywheel of an internal combustion engine.

// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;

void isr()
{
 unsigned long now = micros();
 if (numPulses == 1)
 {
   firstPulseTime = now;
 }
 else
 {
   lastPulseTime = now;
 }
 ++numPulses;
}

void setup()
{
 Serial.begin(19200);    // this is here so that we can print the result
 pinMode(3, OUTPUT);     // put a PWM signal on pin 3, then we can connect pin 3 to pin 2 to test the counter
 analogWrite(3, 128);
}

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
 numPulses = 0;                      // prime the system to start a new reading
 attachInterrupt(0, isr, RISING);    // enable the interrupt
 delay(sampleTime);
 detachInterrupt(0);
 return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime);
}

void loop()
{
 float freq = readFrequency(1000);
 Serial.println(freq);
 delay(1000);
}

The signal of interest is a 5V square wave in the order of 30 to 40Hz. However, using dc40's code there are rogue high values (50 to 60Hz) appearing which is completely throwing my speed control system.

I am using a servo motor to control the throttle angle based on speed to keep the speed at a setpoint, using a PID library. Would this be throwing the ISR timer? Can the two work together? Where are these rogue values coming from? They appear randomly, with every 20 to 40 seconds of running:

#include <PID_v1.h> //include PID library in sketch
#include <Wire.h>
#include <Servo.h>

#define ECU2_RESET 8 //was 3
#define SERVO_PIN 9 //was 5
#define OVERSPEED_PIN 10 //overspeed to master
#define RELAY_PIN 11 //local overspeed relay

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;

unsigned long over_speed_setpoint = 50; //define an over speed setpoint (i.e 3000rpm) here in correct units = 3000rpm
 double speed_setpoint = 41 ; // see engine calcs overview for this calc. or onenote To Do sheet. 48000=2500rpm, 50000=2400rpm
unsigned int sample_time = 100;
unsigned long num_iterations;




/////////////////////PID SETTINGS///////////////////////////////////////
double Kp = 1.0; //was o.008
double Ki = 0.7; // was 0.0022
double Kd=0.0;


////////////////////SERVO SETTINGS//////////////////////////////////////////////////
double throttle_setpoint;    // variable to store the desired throttle angle - WHAT SHOULD THIS BE INITIALISED AT?? 
int throttle_sig; //whats set to the servo = (90-throttle_setpoint) calculated by map() function
Servo myservo; //initalising servo as servo object

double freq_1=0; //variable for frequency readout


boolean engine_running = false; // this is false until the engine starter motor is engaged

PID myPID(&freq_1, &throttle_setpoint, &speed_setpoint, Kp, Ki, Kd, DIRECT); //PID setup using speed avg //REVERSE




void isr()
{
 unsigned long now = micros();
 if (numPulses == 1)
 {
   firstPulseTime = now;
 }
 else
 {
   lastPulseTime = now;
 }
 ++numPulses;
}

void setup()
{

Serial.begin(9600);
Serial.println("Setup Finished"); 
Serial.print("This  run we have :  ");
Serial.print("Kp = ");
Serial.print(Kp);
Serial.print(" Ki = ");
Serial.print(Ki);
Serial.print(" Kd = ");
Serial.print(Kd);
Serial.print("Sample time = ");
Serial.print(sample_time);
Serial.print("Speed setpoint = ");
Serial.println(speed_setpoint);

pinMode(21, INPUT);     //input for the HES signal
digitalWrite(21,HIGH); // added to prevent floating values???


pinMode(OVERSPEED_PIN, OUTPUT); //pin 4 will be HIGH when engine speed is too high and shutdown is required, LOW when ok to keep running.OVERSPEED PIN
pinMode(RELAY_PIN,OUTPUT); //pin 7 as local overspeed pin

digitalWrite(OVERSPEED_PIN,LOW); //initailly set this to low to say that there is no overspeed on system intialisation
digitalWrite(RELAY_PIN,HIGH); //initailly set this to low to say that there is no overspeed on system intialisation


myservo.attach(SERVO_PIN);//attaching the servo to PIN 9. 
myservo.write(8);// setting inital value of 85 to get it started on idle
myPID.SetMode(AUTOMATIC); //this sets the PID algoithm to automatic, i.e. it will compute every loop





}

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
 numPulses = 0;                      // prime the system to start a new reading
 attachInterrupt(2, isr, RISING);    // enable the interrupt
 delay(sampleTime);
 detachInterrupt(2);
 return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime); // if the number of pulses is less than 3, then 0, otherwise - 


}

void loop()
{
  if(engine_running==true){
    num_iterations++;
  }

  
freq_1 = readFrequency(sample_time);


if(freq_1 > 5 && engine_running == false && freq_1 < 1000){ //if the engine is cranking then the engine_running variable is set to on so the PID controller is activated
  Serial.println("Engine is now running, ECU activated");
  engine_running = true;   
     }



  if(engine_running==true ){ //if closed loop control is need then the master controller will set pin 3 to HIGH AND the engine has started and the servo should be activated 
   myPID.Compute();//required for PID to work
   throttle_sig=(int)map(throttle_setpoint, 0, 255, 6, 87);// remapping the PID controller output (0-255), set by the PID setoutput function in the setup to 90 to 0 i.e. 90 degrees throttle_setpoint is actually 0 degrees throttle angle ---23.05.2016 was 0,255,0,90 changed to 87 as was conking on start
   myservo.write(throttle_sig); // this is writing the mapped PID output to the servo
//      Serial.print("The PID OUTPUT is: ");
   Serial.print(throttle_setpoint);
   Serial.print("\t");
//   Serial.print("The servo sig is: ");
   Serial.print(throttle_sig);
   Serial.print("\t");
//   Serial.print("The speed is is: ");
   Serial.print(freq_1);
   Serial.print("\t");
   Serial.print(freq_2);
   Serial.print("\t");
   Serial.println(millis());
  }

  if(freq_1 >= over_speed_setpoint){
  Serial.println(freq_1);
  digitalWrite(OVERSPEED_PIN,HIGH); //sending 5v signal to master to indicate overspeed
  digitalWrite(RELAY_PIN,LOW);      
  Serial.println("OVERSPEED");

  
  }
  else {
  digitalWrite(OVERSPEED_PIN,LOW);//telling master that there is no overspeed present
  digitalWrite(RELAY_PIN,HIGH); //keeping local overspeed relay switched on as there is no issues
  }

}

Would this be throwing the ISR timer?

Yes.
The servo code needs interrupts at a regular time to keep all the servo signals timed correctly. While it is doing this the interrupts are disabled and so the timing ISR could miss an interrupt.

Adafruit have come up with a simplified servo libiary that might help you:-

Hello Grumpy Mike!

I have tried running the engine with the servo aspect removed and the problem persists!

Thanks for the heads up about a different servo library, I will check it out!

Try something without any detach/attach, use interrupts/noInterrupts which doesn't lose any:

float readFrequency(unsigned int sampleTime)
{
  noInterrupts () ;  // disable/defer interrupts whilest reading/writing volatiles
  numPulses = 0;    
  interrupts () ;

  delay(sampleTime);

  noInterrupts () ;  // disable/defer interrupts whilest reading/writing volatiles
  int myNumPulses = numPulses ;
  unsigned long difference = lastPulseTime - firstPulseTime ;
  numPulses = 0;    
  interrupts () ;    // re-enable interrupts before we start doing slow floating point ops.
  
  return (myNumPulses < 3) ? 0 : (1000000.0 * (float)(myNumPulses - 2)) / ((float) difference) ;
}

Hello MarkT

I have tried this code and it does not make a difference. The rogue readings are still varying :frowning:

It could be electrical noise??

It could be your input to pin 2 is floating. How is this wired up to the sensor?

The sensor is connected to Pin 21, which is interrupt number 2, as far as I understand.

Attached is a sketch of the wiring diagram.

Attached is a sketch of the wiring diagram.

There is no ground attached to your sensor. It just says H.E.S. so what is it?

The sensor is connected to Pin 21, which is interrupt number 2, as far as I understand.

What type of Arduino are we talking about here?

The sensor is actually a single ended Variable Reluctance Sensor. I am using a Maxim9924U evaluation kit to convert the signal into a 5V square wave, falling, as shown in the diagram.

The Arduino I am using is a Mega2560.

I am using a Maxim9924U evaluation kit to convert the signal into a 5V square wave, falling, as shown in the diagram.

and the ground?

The sensor is single ended so the ground is internal to the machine. A common ground exists between everything.

There is only one wire output from the VSR. See pics attached.

here is the second picture which could not be uploaded due to size limitations on the upload!

This is a very hostile environment for electronics. It looks like you are getting interference from some other sources hence the false faster speed readings.

It is tricky to suppress these signals but I would start with a 1K pull up resistor on the input, plus a 0.01uF ceramic from the input to ground.

Hello GrumpyMike

I have tried implementing a low pass filter as suggested. There is no notable difference and the random vales are still showing up :confused:

Any other suggestions?

I have tried implementing a low pass filter as suggested.

Well that was not actually a low pass filter, well not if you did it correctly.

The next step is to put an inductor in line with the sensor to stop rapid spikes being picked up.