Deceleration time logger between frequencies

Hello everyone, i need to measure the air resistance and roll resistance of a vehicle. I plan on using a toothed disc with a proximity sensor which will output a square wave signal in to an arduino. Then I will drive until I reach a certain speed, after which I will disconnect the drive from the wheels and let the vehicle roll until it has reached another certain speed (2 or 3 kph lower than the speed at which I started). By measuring the time it takes to decelerate I can calculate the air and rolling resistance.

I want to use an arduino as the brain box. Once the frequency of the sensor signal drops below a given value the arduino should start measuring time. Once the frequency drops below a second given value the arduino should stop measuring. For example between 25Hz and 20Hz. I would like to change the Frequency (that corresponds to a speed) so i can test at different speeds. I would then want to log the time it took to my pc on the serial monitor. I plan on using an Arduino Mega 2560.

My knowledge of coding in C is very limited so I'm a bit stuck on how I would write such a program. Anybody got any ideas on how to program something by using interrupts? The vehicle on which I want to do this is an Urban Concept car for the Shell Eco-marathon efficiency race.

you can search on the web "Wheel Encoder Counter arduino" for example, you should see many results

There are lots of examples of frequency counting. The rest of your code can be a simple state machine:

enum states {WAITING_FOR_START, READY_FOR_START, RUNNING} State;

unsigned long ReadyFrequency = 30;  // Speed above start frequency where run can start
unsigned long StartFrequency = 25;  // Speed at which timing starts
unsigned long StopFrequency = 20;  // Speed at which timing stops

unsigned long StartTime, StopTime;

void setup() {
  Serial.begin(115200);
  while (!Serial);  // Wait for USB connection on Leonardo/Micro
  State = WAITING_FOR_START;
}

void loop() {
  switch (State) {
    case WAITING_FOR_START:
      if (Frequency() > ReadyFrequency) {
        Serial.println(F("; Ready to start a run."));
        State = READY_FOR_START;
      }
      break;

    case READY_FOR_START:
      if (Frequency() <= StartFrequency) {
        StartTime = millis();
        Serial.println(F("; RUN STARTED."));
        State = RUNNING;
      }
      break;

    case RUNNING:
      if (Frequency() <= StopFrequency) {
        StopTime = millis();
        Serial.println(F("; RUN FINISHED. Elapsed time in milliseconds:"));
        Serial.println(StopTime - StartTime);
        State = WAITING_FOR_START;
      }
      break;
  }
}

// Someone has to write this part
unsigned long Frequency() {
  return 0;
}

Thanks a lot for the replies!

@johnwasser that looks a lot like what we need! I see you say there's still a part missing that someone has to write. What function has this missing part?

What function has this missing part?

Frequency() will, when you write it.

Ok, let's start with Mr. johnwasser's program. Would this work if i write this extra piece about 'unsigned long Frequency()' or am i missing something? Thanks for thinking with me!

enum states {WAITING_FOR_START, READY_FOR_START, RUNNING} State;

int inpin =13;
unsigned long ReadyFrequency = 30;  // Speed above start frequency where run can start
unsigned long StartFrequency = 25;  // Speed at which timing starts
unsigned long StopFrequency = 20;  // Speed at which timing stops

unsigned long StartTime, StopTime;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  State = WAITING_FOR_START;
  pinMode(inpin, INPUT);
}

void loop() {
  switch (State) {
    case WAITING_FOR_START:
      if (Frequency() > ReadyFrequency) {
        Serial.println(F("; Ready to start a run."));
        State = READY_FOR_START;
      }
      break;

    case READY_FOR_START:
      if (Frequency() <= StartFrequency) {
        StartTime = millis();
        Serial.println(F("; RUN STARTED."));
        State = RUNNING;
      }
      break;

    case RUNNING:
      if (Frequency() <= StopFrequency) {
        StopTime = millis();
        Serial.println(F("; RUN FINISHED. Elapsed time in milliseconds:"));
        Serial.println(StopTime - StartTime);
        State = WAITING_FOR_START;
      }
      break;
  }
}


unsigned long Frequency() {
  unsigned long StartTime1=0, StartTime2=0,Time=0, Frequency;
  int counter1=1, counter2=0;
  while(counter1!=4)
  {
    while(~digitalRead(inpin))
    {
      if(counter1 == (counter2 +1))
      {
        if(StartTime2==0)
        {    
        StartTime2 =millis();    
        StartTime1 =millis();}
        else if(StartTime2!=0){
        StartTime2 = StartTime1;     
        StartTime1 =millis();}
      }
      counter2 = counter1;
    }
    if(counter1==counter2)
    {
      Time += (StartTime1-StartTime2);
      counter1 += 1;
    } 
  }
  Frequency = 1/(Time/1000);
  return Frequency;
}

Test_1_measure_between_frequencies.ino (1.72 KB)

Some notes:

unsigned long Frequency() {
  unsigned long StartTime1 = 0, StartTime2 = 0, Time = 0, Frequency;
  int counter1 = 1, counter2 = 0;
  while (counter1 != 4)
  {
    while (~digitalRead(inpin))  // WRONG OPERATOR!  Use '!' for logical NOT.
                                 // Note: This is NOT going to detect a pulse edge.  To do that you have to detect a CHANGE of state
    {
      if (counter1 == (counter2 + 1))
      {
        if (StartTime2 == 0)
        {
          StartTime2 = millis();
          StartTime1 = millis();
        }
        else if (StartTime2 != 0) {  // REDUNDANT!  StartTime2 is either == 0 or != 0.  No need to check both
          StartTime2 = StartTime1;
          StartTime1 = millis();
        }
      }
      counter2 = counter1;
    }

    if (counter1 == counter2)  // REDUNDANT!  This condition will always true
    {
      Time += (StartTime1 - StartTime2);  // Does this mean there are four pulses per revolution?
      counter1 += 1;
    }
  }
  Frequency = 1 / (Time / 1000);  // INTEGER DIVIDE BY ZERO! If less than 60 RPM (1 RPS)
                                  // ZERO RESULT!  If more than 120 RPM (2 RPS)
                                  // Try: Frequency = 1000 / Time;  Works from 60 RPM to 60000 RPM
  return Frequency;
}

Hi Johnwasser,

Im trying something different, what do you guys think of it? I'm getting the frequency on the serial monitor but not most of the time or not in combination with the total program ...

enum states {WAITING_FOR_START, READY_FOR_START, RUNNING} State;

int inpin =3;
unsigned long ReadyFrequency = 30;  // Speed above start frequency where run can start
unsigned long StartFrequency = 25;  // Speed at which timing starts
unsigned long StopFrequency = 20;  // Speed at which timing stops

unsigned long StartTime, StopTime,f;
int setFlank=0, counter=0;

void setup() {
  Serial.begin(9600);
  while (!Serial);
  State = WAITING_FOR_START;
  pinMode(inpin, INPUT);
  attachInterrupt(digitalPinToInterrupt(3), set, RISING);
}

void loop() {Serial.println(F("test1")); Frequency();
 switch (State) {
    case WAITING_FOR_START:
      f=Frequency();
      if (Frequency() > ReadyFrequency) {
        Serial.println(F("; Ready to start a run."));
        State = READY_FOR_START;
      }
      break;

    case READY_FOR_START:
      f=Frequency();
      if (Frequency() <= StartFrequency) {
        StartTime = millis();
        Serial.println(F("; RUN STARTED."));
        State = RUNNING;
      }
      break;

    case RUNNING:
      f=Frequency();
      if (Frequency() <= StopFrequency) {
        StopTime = millis();
        Serial.println(F("; RUN FINISHED. Elapsed time in milliseconds:"));
        Serial.println(StopTime - StartTime);
        State = WAITING_FOR_START;
      }
      break;
  }
  }


unsigned long Frequency() {
   unsigned long TimeNew=0, TimeOld=0,TimeTotal=0, Frequency=0;
   int counter=0;
  while(counter!=3)//measure time betweenv 3 flanks, 2 periods
  {
    if(setFlank)
    {
      setFlank = 0;
      if(TimeOld==0)//first cycle, old and new should be the same for correct calculation of total time
        { 
          TimeNew =millis();
          TimeOld = TimeNew;}
        else{
          TimeOld = TimeNew;     
          TimeNew =millis();}
        counter ++;
        TimeTotal += (TimeNew-TimeOld);
    }
  }     
  Frequency = 2000/TimeTotal;       //TimeTotal divided by 2 (2 periods) and 1000(milliseconds)
  Serial.println(Frequency);        
  return Frequency;
}

void set()
{
  setFlank =1;
}

Im trying something different, what do you guys think of it?

"we" probably think that with 4 post you are no longer a newbie and should post code correctly...

Please correct your post above and add code tags around your code:
[code]`` [color=blue]// your code is here[/color] ``[/code].

It should look like this:// your code is here
(Also press ctrl-T (PC) or cmd-T (Mac) in the IDE before copying to indent your code properly)

we also think setFlank should be a volatile boolean and not just an int (to start helping)

It would make more sense to capture the value of millis() in the ISR, instead of just setting a flag. Otherwise you have no need for interrupts, you can just poll a pin. The whole point of the interrupt is to avoid the loop() latency.

I work together with the OP on this project. We changed the program now!
Instead of calculating frequency we changed to working with the period between two pulses.
When a rising edge occurs, we take an interupt to calculate the period.
A switch case compares the value of period T against a set value, and calculates timing.

enum states {WAITING_FOR_START, READY_FOR_START, RUNNING} State;

unsigned long ReadyPeriod = 38;  // Speed above start frequency where run can start
unsigned long StartPeriod = 48;  // Speed at which timing starts
unsigned long StopPeriod = 77;  // Speed at which timing stops
unsigned long StartTime, StopTime;
const byte interruptPin = 2;
int counter1 = 0;
volatile unsigned long T, Toud, Tnew;

void setup()
{
  Serial.begin(9600);
  while (!Serial);
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), meting, RISING);
}

void loop()
{
  switch (State) {
    case WAITING_FOR_START:
      if (T < ReadyPeriod) {
        Serial.println(F("; Ready to start a run."));
        State = READY_FOR_START;
      }
      break;
    case READY_FOR_START:
      if (T >= StartPeriod) {
        StartTime = millis();
        Serial.println(F("; RUN STARTED."));
        State = RUNNING;
      }
      break;
    case RUNNING:
      if (T >= StopPeriod) {
        StopTime = millis();
        Serial.println(F("; RUN FINISHED. Elapsed time in milliseconds:"));
        Serial.println(StopTime - StartTime);
        State = WAITING_FOR_START;
      }
      break;
  }
}

void meting()
{
  Tnew = millis();
  T = Tnew - Toud;
  Toud = Tnew;
}

What do you guys think?

Also, now we measure the period between two pulses but to minimize the fault on our timing we would like to measure the period by taking the mean period of 4 pulses. Since everytime there is a rising edge the interupt runs, I don't see how this would work

an easy way would be to add another volatile variable counting where you are and making the variables in which you capture the timings an array instead of a single value. when the count in 4, in the loop you can calculate the average and reset the count. (whilst then count is at 4, then interrupt would do nothing for example)

J-M-L:
an easy way would be to add another volatile variable counting where you are and making the variables in which you capture the timings an array instead of a single value. when the count in 4, in the loop you can calculate the average and reset the count. (whilst then count is at 4, then interrupt would do nothing for example)

I tried it like this now:

enum states {WAITING_FOR_START, READY_FOR_START, RUNNING} State;

unsigned long ReadyPeriod = 38;  // Speed above start frequency where run can start
unsigned long StartPeriod = 48;  // Speed at which timing starts
unsigned long StopPeriod = 77;  // Speed at which timing stops
unsigned long StartTime, StopTime;
const byte interruptPin = 2;
int counter1 = 0;
volatile unsigned long T, Ta, Tb, Tc, Td, Te, Teen, Ttwee, Tdrie, Tvier;

void setup()
{
  Serial.begin(9600);
  while (!Serial);
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), meting, RISING);
}

void loop()
{
  T = (Teen + Ttwee + Tdrie + Tvier) / 4;
  switch (State) {
    case WAITING_FOR_START:
      if (T < ReadyPeriod) {
        Serial.println(F("; Ready to start a run."));
        State = READY_FOR_START;
      }
      break;
    case READY_FOR_START:
      if (T >= StartPeriod) {
        StartTime = millis();
        Serial.println(F("; RUN STARTED."));
        State = RUNNING;
      }
      break;
    case RUNNING:
      if (T >= StopPeriod) {
        StopTime = millis();
        Serial.println(F("; RUN FINISHED. Elapsed time in milliseconds:"));
        Serial.println(StopTime - StartTime);
        State = WAITING_FOR_START;
      }
      break;
  }
}

void meting()
{
  Ta = millis();
  Teen = Ta - Tb;
  Ttwee = Tb - Tc;
  Tdrie = Tc - Td;
  Tvier = Td - Te;
  Te = Td;
  Td = Tc;
  Tc = Tb;
  Tb = Ta;

}

And this seems to work. I put the calculation of the average in the loop, because I've heard dividing takes a lot of time and you should keep that away from your interupts.

Do you mean putting the T values in an array, instead of using Ta, Tb, Tc, etc?

void meting()
{
  Ta = millis();
  Teen = Ta - Tb;
  Ttwee = Tb - Tc;
  Tdrie = Tc - Td;
  Tvier = Td - Te;
  Te = Td;
  Td = Tc;
  Tc = Tb;
  Tb = Ta;
}

this has NO chance of delivering an average...

you need something that say "if it is the first time I arrive here, record in Ta, second time in Tb, third time in Tc and 4th time in Td... here you end up with all the values being equal.

--> an array and index in the array is what you need