Clock Timer, Interrupt pins, millis(), unexpected behavior

Hello,
I have been working on a project and I recently started noticing some very strange behavior. My project entails using a homemade linear actuator as a braking mechanism on a winch. The specific area I am having trouble with is measuring the velocity that the winch is lowering a mass at. I am currently using a rotary encoder to measure pulses, which I then translate to linear action using the arc length. I am using a single interrupt pin on only one signal of the encoder because I only care about velocity, not direction. After I count pulses and translate this to linear motion (~36 PPR encoder), (linear distance = NumberPulse/362pi*radius), I calculate velocity by counting the time it takes to traverse this number of pulses using millis(). I am having the very strange behavior that the time interval I calculate using millis is always coming out to be zero (code attached below). I have spent some time checking this, I tried a few things - maybe there is something I dont understand about how arduino uses clocks on interrupt pins - maybe its something with how I store my values? Any help will be appreciated.

The area I am concerned about is the function doEncoderA(), but I included all my code

//.............Encoder and Servo..............//    //H = 187
#define encoder0PinA  2 //encoder writes to pin 2
#define reset 6
#define DistancePin 3
// board writes to pin 9 for servo
#include <Servo.h>
float PulseIn = 50.0;
//................Other Values...............//
const float pi = 3.1415;
volatile int encoder0Pos = 0;
unsigned long TimeNow; unsigned long PreviousTime;
unsigned long Interval;
float distance1 = PulseIn / 36.0 * 2 * pi * 2.54 / 4.0; //[cm]
float distance; float analog;
float Velocity;
int VSetPt; float Error; int SetPos = 150;
//...........Values for Stall Check...........//
int CheckTime1;
int CheckTime2 = 0;
int CheckServo1;
int CheckServo2 = -1;   //servo will never be at this angle, will always be reset in the loop
int inittime;
int currenttime;
Servo myservo;
int a;
void setup() {
  inittime = millis();
  delay(1000);
  Serial.begin(9600);
  Serial.println("setup");

  Serial.println(distance1, DEC);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object

  //......................Encoder Stuff..................//

  pinMode(encoder0PinA, INPUT);
  pinMode(reset, INPUT);
  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(digitalPinToInterrupt(encoder0PinA), doEncoderA, RISING);
  //attachInterrupt(digitalPinToInterrupt(reset), doreset,RISING);
  // encode r pin on interrupt 1 (pin 3)
  //attachInterrupt(digitalPinToInterrupt(encoder0PinB), doEncoderB, CHANGE);

}

void loop() {
  //..........Read Distance..........//
  distance = pulseIn(3, HIGH);
  a = digitalRead(reset);
  //Serial.print("Is the reset high? | ");Serial.println(a);
  if (distance > 45) {
    // Serial.print("Value from distance pin | ");Serial.println(distance);
    //........Check to see if stall..........//
    CheckTime1 = millis();
    CheckServo1 = myservo.read();
    if (CheckServo1 == CheckServo2) {
      if (CheckTime1 - CheckTime2 > 5) {
        myservo.write(CheckServo1 + 20);
      }
    } else {
      CheckServo2 = CheckServo1;
      CheckTime2 = CheckTime1;
    }
  }
  if (a == 1) {
    SetPos = 150;
    myservo.write(SetPos);
    inittime = millis();
    Serial.print("reset position | "); Serial.println(a);
  }
}

void doEncoderA() {
  encoder0Pos++;
  Serial.print("encoder position | "); Serial.println(encoder0Pos);
  if (encoder0Pos % (int)PulseIn == 0) {
    TimeNow = millis();
    Interval = TimeNow - PreviousTime;
    Velocity = distance1 / ((float)Interval / 1000);
    Serial.print("TimeNow | "); Serial.println(TimeNow);
    Serial.print("Previous Time | "); Serial.println(PreviousTime);
    PreviousTime = TimeNow;
    SetServo();
    Serial.print("velocity | "); Serial.println(Velocity);//Serial.println(encoder0Pos);
    Serial.print("interval | "); Serial.println(Interval);


  }
}

void SetServo() {
  //calculate error
  currenttime = millis();
  if (distance < 70 && (currenttime - inittime) > 3000) {
    VSetPt = 80.0 / 20.0 * distance - 80.0 / 120.0 * 20.0;
  }
  else {
    VSetPt = 5;   //HIGH: 150,  LOW: 100?   original: 130
  }
  Error = Velocity - VSetPt;
  if (Error >= 1) { //this is our range
    SetPos = SetPos - 1;
    if (SetPos < 0) {
      SetPos = 0;
    }
    myservo.write(SetPos);
  }
  else if (Error < -1) {
    SetPos = SetPos + 1;
    if (SetPos > 130) {
      SetPos = 130;
    }
    myservo.write(SetPos);
  }
  //...........Switch to rest to 150 degrees.............//

  Serial.print("Set Postiion = "); Serial.println(SetPos);
  //Serial.print("reset position | ");Serial.println(a);

}

Again, the value “Interval” I calculate comes out to be zero very often, which causes my velocity to drive to infinity. The interval seems to go to zero more often when the winch shaft is rotating faster as opposed to slower.

Thank you

  • Don't print from an interrupt, this will lead to disappointment

  • use parenthesis in your calculation unless your really know well your precedence table -for example how do you expect this to be evaluated ==> VSetPt = 80.0 / 20.0 * distance - 80.0 / 120.0 * 20.0; ==> float distance1 = PulseIn / 36.0 * 2 * pi * 2.54 / 4.0;

-

There is more wrong with your doEncoderA than serial printing. Calling the setServo() is wrong because servo.h requires timer interrupts, and interrupts are not enabled within the isr.

See Nick Gammon's excellent tutorial on interrupts https://gammon.com.au/interrupts

The doEncoderA should be as brief as possible. Determine the interval for your counts, set a flag that the interval has been determined, let the loop pick up that flag and do everything else with the value. Here's the idea

volatile int encoder0Pos;
int copy_encoder0Pos;
volatile unsigned long startTime;
volatile unsigned long endTime;
volatile unsigned long interval;
unsigned long copy_interval;
volatile boolean intervalComplete = false;

float PulseIn = 50.0;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");
  attachInterrupt(digitalPinToInterrupt(3), doEncoderA, RISING);//interrupt on pin3
}

void loop()
{
  if (intervalComplete == true)
  {
    intervalComplete = false;//reset flag
    noInterrupts();//make copy protected from changing
    copy_interval = interval;
    copy_encoder0Pos = encoder0Pos;
    interrupts();
    //debug prints
    Serial.println(copy_interval); //total time for numCount
    Serial.println(copy_encoder0Pos);
    //do rest of code with value of copy-interval or Pos
  }
}

void doEncoderA()
{
  encoder0Pos++;
  if ((encoder0Pos % (int)PulseIn) == 1)
  {
    startTime = millis();
  }
  if ((encoder0Pos % (int)PulseIn) == 0) //==50
  {
    endTime = millis();
    interval = endTime - startTime;
    intervalComplete = true;
  }
}

Thank you for the replies, I made changes that were suggested and am getting better results - I think I still have a lot to learn about interrupts & thanks for the link