Calculating the speed with on off signals and fixed lengths

So im geting a high/low signal from holes in a gear rack (with an IR Sensor) which my motor is towing towards me. Since i know the distance between the holes (same distance) and i need to regulate the speed(while "towing"), my idea is to measure the time while the motor is working and calculate the current velocity. If its too low/high i use PWM to increase/decrease the speed of the DC Motor.
Since there should be a LCD Display and two other sensors aswell I thougt about two ideas on how i could realise this in code:

1)Interrupt: get the current millis() when the IR Sensor detects a signal and substracting it from the last millis() the signal changed to get delta_time.
1.1) Is this possible since in tutorials i saw that millis isnt allowed in interrupts? (And it seems i cant give the interrupt function a parameter like for example the last time it triggered)
1.2) Will this eventually slow down the whole code since there will be a lot of interrupts every few millimeters?

2)Write a function that calculates the current velocity and only insert it at certain points so the general working procedure from the code doesnt get interrupted that often.

Thank you in advance:)

You can use millis() in interrupts, you can't use delay(). If you do use millis() in an interrupt, don't
expect it to change if you wait - that's what delay assumes!

Millis() uses the overflow interrupt of timer0 to count clock ticks. Whilst interrupts are disabled upon entry into an ISR, the counter associated with tomer0 keeps working. What will not happen if you use millis() in an ISR is the current increment if the counter was just about to overflow and increment prior to servicing your ISR. Your timing may therefore be out by that amount. In reality though, when using millis() you perceive a 1ms elapsed time between two values, n and n+1, when the two may be only microseconds apart or closer to 2ms, depending on where in the count you call the function. If you're in and out of your ISR quickly, you're likely not losing any resolution that isn't inherent anyhow.

If timing is super critical, don't use millis() or micros(). Unfortunately timer0 overflow is at the bottom rung of the timer interrupt ladder and cannot affect any of the external or pin change interrupts. Set up timer1 with a convenient prescaler (pick your resolution) and do the math with TCNT1, which will count your prescaled pulses up to 65,535.

So this wont work i guess? Is there a tip for me so I can get this running otherwise?

attachInterrupt(0, deltaTime, CHANGE); //0 bezieht sich auf D2 (Arduino MEGA 2560)

void loop(){

calcSpeed();

}
}

float calcSpeed(){
return abs(p/(delta)); //distance divided by deltaTime
}

void deltaTime(){

t0 = millis();
delta = abs(t0-t1)/1000; //Delta in Sec.
// speedval = p/(delta); //distance divided by deltaTime
t1 = millis();
}

So this wont work i guess?

I'd guess that you are correct. Look at that code. Think seriously about what times you are recording, and whether those times mean a fart in a whirlwind.

The idea was to get the new time as soon as a signal change emerges. Then i get the time change between the current time and the last time and lastly set the current time to the next time so this might work again. I thougt it might have been a good solution to my problem.

Actually it seems to work as the "speed" increases as soon as im getting faster in changing the value but it fluctuates for some reason between the value I want and a way lower value.
Is there anything I am getting wrong?
Thank you very much for caring:)

The idea was to get the new time as soon as a signal change emerges.

So, you have one time. You need two times to determine the speed.

If the idea is to capture the current time each time a change happens, and save the old value as the previous time, that would make sense, and give you the current time and the previous time.

But, the value stored in the previous time variable will NOT come directly from a call to millis().

attachInterrupt(0, deltaTime, CHANGE);

Why "CHANGE"? You might get 2 interrupts for each hole, try RISING or FALLING.

outsider:
Why "CHANGE"? You might get 2 interrupts for each hole, try RISING or FALLING.

It's the same distance in the middle of a gear rack :wink:

PaulS:
So, you have one time. You need two times to determine the speed.

If the idea is to capture the current time each time a change happens, and save the old value as the previous time, that would make sense, and give you the current time and the previous time.

But, the value stored in the previous time variable will NOT come directly from a call to millis().

Maybe i get something wrong, but i still think that im kinda on the right track (maybe im wrong than i would be sorry :wink: ):

Ill run you through my thougt:

  1. Interrupt happens -> t0 will get set (for ex. 1 sec)
  2. delta will be 1/1000 since t1 is still 0 sec
  3. now t1 will get set to 1 sec (so for now t0 and t1 are equal)
    Next interrupt-->4) t0 is getting set (for ex. now 3 sec)
  4. delta will be (3-1)/1000... so it has changed.

I measure the speed of a small DC motor with a single pulse per revolution with this code. It should work just as well for your linear detector

const byte fwdPin = 9;
const byte revPin = 10;
const byte potPin = A1;

int potVal;
int pwmVal;

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;

unsigned long prevDisplayMillis;
unsigned long  displayInterval = 1000;

    // variables for the ISR
volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile bool newIsrMicros = false;



void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");
    pinMode (fwdPin, OUTPUT);
    pinMode (revPin, OUTPUT);

    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();
        readPot();
        updateMotorSpeed();
    }
}

//===========

 void readPot() {
    potVal = analogRead(potPin);
}

//===========

void updateMotorSpeed() {
    pwmVal = potVal >> 2;

    digitalWrite(revPin,LOW);
    analogWrite(fwdPin, pwmVal);
 }

//===========

void getIsrData() {
    if (newIsrMicros == true) {
        prevRevMicros = revMicros; // save the previous value
        noInterrupts();
            revMicros = isrMicros;
            revCount = isrCount;
            newIsrMicros = false;
        interrupts();
        revDuration = revMicros - prevRevMicros;
    }
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("PWM Val "); Serial.println(pwmVal);
    Serial.print("  Rev Duration ");
    Serial.print(revDuration);
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

...R

Could you maybe explain your code a little? Thank you

I had hoped the the short program would have been self-explanatory.

The ISR is triggered by the detector once every revolution. I saves the value of micros(), increments a counter, and sets newISRMicros to true.

The loop() function repeats frequently and every time it calls getIsrData() which checks to see if newIsrMicros is true. if it is it saves the values of revMicros and revCount and sets newIsrMicros back to false. Then it calculates the duration for that revolution.

If that is not sufficient explanation then please let me know exactly what you don't understand.

...R

Time for the hippo dentist. :slight_smile:
Post a picture of the gear rack and sensor. How far from the center of one hole to the center of the next hole? What is the maximum travel speed?

I tried to play around a little and thougt that a regular rpm measurement would be easier to realise as the distance between teeth in a rack varies if you measure a little above or lower. Ill try to update when im finished

Please make your image visible here. See this Simple Image Guide

...R