Use one or more Interrupts for velocity determination

I have an e trike and I want to measure the velocities of the three wheels. The front wheel is passive while the two back wheels should be controlled via an electronic differential. So I want to use at least one timer interrupt like the following

ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz to measure wheel sensors
  
// velocity v_x determination with front wheel sensor   
  frontwheelsensorVal = digitalRead(frontwheelsensorPin);//get val of A4
  if (frontwheelsensorVal){//if wheelsensor switch is closed
    if (frontwheelsensorCounter == 0){//min time between pulses has passed
      v_x = 3.6*float(circumference)/(float(timer));//calculate kilometers per hour
      timer = 0;//reset timer
      frontwheelsensorCounter = maxfrontwheelsensorCounter;//reset frontwheelsensor Counter
    }
    else{
      if (frontwheelsensorCounter > 0){//don't let frontwheelsensorCounter go negative
        frontwheelsensorCounter -= 1;//decrement frontwheelsensorCounter
      }
    }
  }
  else{//if wheelsensor is not active
    if (frontwheelsensorCounter > 0){//don't let frontwheelsensorCounter go negative
      frontwheelsensorCounter -= 1;//decrement frontwheelsensorCounter
    }
  }
  if (timer > 2000){
    v_x = 0;//if no new pulses from wheel sensor switch- tire is still, set v_x to 0
    
  }
  else{
    timer += 1;//increment timer
  } 
}

Can I simply add the two back wheel velocity calculations into the interrupt as I did it with the front wheel?

It looks like you want to, every millisecond, check each of three sensors to determine their status, the trigger intervals and, hence, the speeds of the three wheels.
You should be able to do all that in the same interrupt service routine. You'd need to triple everything eg timer would be timer[3] etc. If the wheel sizes (front/rear) are different you'd have to do the same for those.
What sensors are you using, incidentally ? reed switches ? Measuring only once per rotation is sufficiently accurate for your application ?

for the back I am using the hallsensors which are already inside the synchronous hub motors. Each motor has got three of them for position detection. For the front I tend to use one as well. I am searching one which complies with the IP68 safety standard.
If I want to read the three sensors I could do it with a for loop and incrementing all parameters by one.

So you want to read three sensors from each of the two rear wheels and one sensor from the front wheel. You now need seven pins and the calculation your the rear wheel speed is now different because the interval between pulses is now one third of a revolution. But, in principle, it is the same.

Wouldn't it be a lot simpler to trigger a separate interrupt from each of the wheel sensors and record the value of micros(). Then you can figure out the speed from the number of microsecs between pulses. The code for one ISR could be as simple as

void myISR() {
  frontWheelPulseMicros = micros();
  newFrontPulse = true;
}

...R

I think one hall sensor shall be enough for each wheel.

So with the For loop it should be something like the following

long timer[3]                = {0, 0 ,0};    // time between one full wheel rotation (in ms)
const float circumference[3] = {480, 480, 480};  //in mm
int wheelsensorPin[3]        = {A7, A8, A9}, //A7 front wheel A8 left rear wheel A9 right rear wheel
    wheelsensorVal[3],
    maxwheelsensorCounter[3] = {100, 100, 100}, //min time (in ms) of one rotation (for debouncing)
    wheelsensorCounter[3];   
float               v_x_max = 45.0, // maximum velocity is 45 kph
               v_x_setpoint = 0.0,   //determined out of throttle grip position
                     v_x[3] = {0.0, 0.0, 0.0};  // actual velocity of front, rear left and right wheel

// wheel sensors velocity determination 
ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz to measure wheel sensors
// velocities determination with front, rear left and right wheel sensor  
for ( int i = 1; i <= 3; i++){ 
  wheelsensorVal[i] = digitalRead(wheelsensorPin[i]);//get val of A4
  if (wheelsensorVal[i]){//if wheelsensor switch is closed
    if (wheelsensorCounter[i] == 0){//min time between pulses has passed
      v_x[i] = 3.6*float(circumference[i])/(float(timer[i]));//calculate kilometers per hour
      timer[i] = 0;//reset timer
      wheelsensorCounter[i] = maxwheelsensorCounter[i];//reset wheelsensor Counter
    }
    else{
      if (wheelsensorCounter[i] > 0){//don't let wheelsensorCounter go negative
        wheelsensorCounter[i] -= 1;//decrement wheelsensorCounter
      }
    }
  }
  else{//if wheelsensor is not active
    if (wheelsensorCounter[i] > 0){//don't let wheelsensorCounter go negative
      wheelsensorCounter[i] -= 1;//decrement wheelsensorCounter
    }
  }
  if (timer[i] > 2000){
    v_x[i] = 0;//if no new pulses from wheel sensor switch- tire is still, set v_x to 0
    
  }
  else{
    timer[i] += 1;//increment timer
  }
 } 
}

Or do I have a mistake?

To minimize quantization errors, it is better in such situations to record and average the interval between pulses than to count transitions during an arbitrary fixed interval.

aarg:
To minimize quantization errors, it is better in such situations to record and average the interval between pulses than to count transitions during an arbitrary fixed interval.

And very much simpler.

...R

For each wheel I will need an interrupt pin. The MEGA offers 2,3,18,19,20 and 21. I use 20 and 21 for I2C already. 2 and 3 are used as well. So only 18 and 19 could be used for two wheels. Is there another option to circumvent pin controlled interrupts or even measure without interrupts.

Why can't you re-allocate pins 2 and 3 so they are available for interrupts?

If you use pinChange interrupts almost any I/O pin can be used for an interrupt.

...R

Hi,
Can I suggest you work on just getting ONE tacho code to work on its own first, so you can evaluate if it updates and is precise enough for your application.

Tom... :slight_smile:

Hello TomGeorge
The first code I posted works pretty well. But now I need to change the method if three signals need to be
processed. I also recognized that transforming this code in to the changed next code I posted will disable any other processing.
I managed to switch over to 2,3 and 18 as interrupt pins. To which Interrupt groups do they belong?
Is it pin 2 -> INT 0
3 -> INT 1
18 -> INT 2?

What happens if two or more interrupt pins are set by hall sensors at the same time? Which interrupt will be set?

What happens if two or more interrupt pins are set by hall sensors at the same time? Which interrupt will be set?

All the interrupts are recognized and placed in a queue for processing. The ISR's are executed in an order of priority which is defined in the datasheet for the processor. For external interrupts on the Mega, they are processed in numerical order-- INT 0 > INT1 > INT2 > INT3 > etc.

I managed to switch over to 2,3 and 18 as interrupt pins. To which Interrupt groups do they belong?

You are better off using the recommended syntax

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);

I want to use two functions as proposed for rpm measurement of the three wheels by indexing as the following

//record of actual rpm of each wheel. Index = 1 front wheel 2 rear left wheel 3 rear right wheel
void actualrpm(int index){
  int interrupt; 
  if (index == 1){ // front wheel
    interrupt = 0;
  } 
  if (index == 2){ // rear left wheel
    interrupt = 5;
  }  
  if (index ==3){ //rear right wheel
    interrupt = 4;
  }  
  if (millis() - lastmillis[index] == 1000){  /*Update every one second*/
   detachInterrupt(interrupt);    //Disable interrupt when calculating
   rpm[index] = rpmcount[index] * 60;  /* Convert frequency to RPM*/
   Serial.print("RPM =\t"); //print the word "RPM" and tab.
   Serial.print(rpm[index]); // print the rpm value.
   Serial.print("\t Hz=\t"); //print the word "Hz".
   Serial.println(rpmcount[index]); /*print revolutions per second or Hz. And print new line or enter.*/
   rpmcount[index] = 0; // Restart the RPM counter
   lastmillis[index] = millis(); 
   attachInterrupt(interrupt, rpmcounter(index), FALLING); //enable interrupt
  }
}

void rpmcounter(int index){ /* this code will be executed every time the interrupt 0,4 or 5 is active*/
  rpmcount[index]++;
}

The compiler tells me that there is an invalid use of void expression
for

attachInterrupt(interrupt, rpmcounter(index), FALLING); //enable interrupt

the actual rpms shall be used for PID control of rear wheels in order to maintain the set velocity of the passive driven front wheel.

To get a better idea of my vehicle I attached a picture of it, which is momentarily front driven.

Vehicle Picture