encoder with interrupt problem

I have a 36 segment wheel and slotted schmitt opto switch on the shaft of 12vDC motor.

This is a new post about a specific issue with timings and interrupts with an encoder.

I have narrowed down a problem with trying to use PID to maintain speed under load at 500rpm (about 20% power)

The encoder is working.
The INT0 on pin 2 is working. Tested with short LED blink sketch.

When I use Serial monitor to look at the pcount values, they change according to velocity of shaft.
But they also do not follow the timing in the loop for what is expected during 200mS.

It depends on the value of a delay() inserted after the Serial.println(pcount);
delay(100), values are around 40.
delay(500), values are from 300 to 400.

I cannot see why this should be?

Hence, for now the PID part of sketch is commented out.

#include <PID_v1.h>

#define motor  3                     //motor connected on pin 3 
#define encoder  2                   //encoder connected on pin 2 
   
int pcount =0;                       //previous count

unsigned long count=0;                               
unsigned long timep, time, etime;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
//PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

void setup() 
{ 
  Serial.begin(9600);
  pinMode(encoder,INPUT);
  digitalWrite(motor,HIGH);//motor control pin HIGH(mosfet off) before setting as OUTPUT
  pinMode(motor,OUTPUT);
  pinMode(13,OUTPUT);    //LED on board
  digitalWrite(13,LOW);  //LED debugging
  
  attachInterrupt(0,counter,RISING); //external interrupt INT0 on pin 2  
  timep=micros();
  Setpoint = 60;                    // pulses in 200mS this will be used to control the speed.
 // myPID.SetMode(MANUAL);
}
  
void counter()
  {
  count++;
  }
  
  
void loop()
{
  Input = pcount; 

//  myPID.Compute();
  Serial.println(pcount);
 
 // if (pcount < 60){
 //   digitalWrite(13,HIGH);
 //   delay(100);
 //   digitalWrite(13,LOW);
    delay(100); 
 // }
  
    Output = 25;
  
  int val=255-Output; //inverted npn driver = 255 the motor mosfet is off and 0 is full on.
  analogWrite(motor,val);
   
    if (micros () - timep >= 200000ul)
  {
    timep += 200000ul ;
    noInterrupts () ;
    pcount = count ;
    count = 0ul ;
    interrupts () ;
  }  
 
}

Just to make clear,
if I comment out the delay(100) then Serial.println(pcount) is always zero.

I only saw this result because I wanted to slow down the scroll display to check I wasn't missing something.

Thanks, have tried making variable.
volatile unsigned long count=0;

Still Serial monitor shows zeros for pcount with no delay(100) added.

You should avoid both the delay() function and use of the Serial monitor when using interrupts. See:

for details.

yes, of course thanks.
I have only put them into the sketch to try and find out what is stopping the timing of the pulses.

simplystupid:
Thanks, have tried making variable.
volatile unsigned long count=0;

Still Serial monitor shows zeros for pcount with no delay(100) added.

What happens if you don't set count back to 0 and calculate the difference from one interval to the next - something like

   if (micros () - timep >= 200000ul)
  {
    timep += 200000ul ;
    noInterrupts () ;
    newCount = count ;
    interrupts () ;
    pcount = newCount - prevCount;
    prevCount = newCount;
  }

with both newCount and prevCount defined the same as count and pcount

...R

I question the signal to your interrupt pin. When I take your code, and generate a 300Hz pulse with the tone function https://www.arduino.cc/en/Reference/Tone and jumper it to pin 2 I can see the count of 60 for the incrementing of pcount every 200ms. I have removed the delay(100)., and I do not see zero for pcount on the serial monitor.

Edit: 500 rpm with 36 segment encoder should be 300 hz. Set tone(8,300)
Initial post was 60Hz

//#include <PID_v1.h>

#define motor  3                     //motor connected on pin 3 
#define encoder  2                   //encoder connected on pin 2 

int pcount = 0;                      //previous count
int last_pcount = 0;
unsigned long count = 0;
unsigned long timep, time, etime;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
//PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

void setup()
{
  Serial.begin(9600);

  tone(8, 300); // generate 300 hz signal on pin8, connect to pin 2

  pinMode(encoder, INPUT);
  digitalWrite(motor, HIGH); //motor control pin HIGH(mosfet off) before setting as OUTPUT
  pinMode(motor, OUTPUT);
  pinMode(13, OUTPUT);   //LED on board
  digitalWrite(13, LOW); //LED debugging

  attachInterrupt(0, counter, RISING); //external interrupt INT0 on pin 2
  timep = micros();
  Setpoint = 60;                    // pulses in 200mS this will be used to control the speed.
  // myPID.SetMode(MANUAL);
}

void counter()
{
  count++;
}


void loop()
{
  Input = pcount;

  //  myPID.Compute();
  Serial.println(pcount);

  // if (pcount < 60){
  //   digitalWrite(13,HIGH);
  //   delay(100);
  //   digitalWrite(13,LOW);
  // delay(100);// remove delay
  // }

  Output = 25;

  int val = 255 - Output; //inverted npn driver = 255 the motor mosfet is off and 0 is full on.
  analogWrite(motor, val);

  if (micros () - timep >= 200000ul)
  {
    timep += 200000ul ;
    noInterrupts () ;
    pcount = count ;
    count = 0ul ;
    interrupts () ;
  }

}

thanks cattledog and robin2,
I am grateful for your input on this.

The check with a tone square wave is something I could do myself as well.

With tone from pin 8 to the interrupt pin 2, I get 60.
On starting the serial monitor, the arduino resets and then there is a string of zeros for a short time before the 60 value takes over.

Now I'm not sure what's wrong with my slotted opto output.
Definitely not analog output. Only has two snap transition states with a DVM, +5v or 0v.
Maybe it has damage to the output circuit or is picking up interference. I'll hook on an oscilloscope while connected to the arduino input.

thanks for making it possible to confirm the timing part of the sketch is good to go with your clever little self-diagnostic method.

Maybe it needs a pull-up? A rotary encoder just attached to an input pin will be floating.

Thanks, Nick. Yes that is always another possibility to check.
On old 20mHz oscilloscope, rising edge of encoder pulse is about 50uS, falling edge hardly visible - ?less than 1uS.

So I've changed to FALLING signal input trigger INT0.

Serial monitor gives readings as expected now.
But PID still doesn't give any Output at all.
After Setup() ramp soft start - motor just stops.

Default p,i,d parameters of 5,2,1.
Maybe try AUTOMATIC just to see if response has changed.

If there is one thing I find confusing, it is tuning PIDs. :slight_smile:

Ok thanks for all your help everyone.
I think the post problem is solved, as far as needing a clean edge to the interrupt pulses goes.

There is already another post active on this project for the PID side of things.