Time BETWEEN 2 high pulses from same pin

Hi,

I'm trying to measure the time between two LOW events on a digital input pin. I found this thread which provided valuable insight: https://forum.arduino.cc/t/measuring-time-between-button-presses/431063.

But it records the duration the pin is high before going low or vice versa depending on how it's coded.

Whereas, I would like to measure the time between: the first event of the pin going low and the second event of the pin going low. Forgetting the high period in-between.

I'm trying the code below:

pRotate = digitalRead(pulseRotate);
    if (pRotate != inRpm1) { 
    if (pRotate == 0) {
      StartTimeRpm = millis();  
      inRpmTimer = 1;
      } else { inRpmTimer = 0; }
    }
    
      inRpm1 = pRotate;

    if (inRpmTimer1 == 1 && pRotate == 0) {
      StopTimeRpm = millis();            
    }

  duration = StartTimeRpm - StopTimeRpm
  intRPM = 60 / duration

The second IF will never work as inRpmTimer1 and pRotate can be TRUE during the first event. Therefore the StopTimeRpm will be the same as the start?

Look at the change in state in examples.

Look for a LOW change, record the time.

Look for the next change to LOW, record the time, subtract the first time from the second time.

Here an example using a (debounced) button:

const byte inPin = 3;
unsigned long timeDifference = 0;
unsigned long lastHigh = 0;
unsigned long actHigh;
byte lastState = HIGH;
byte state;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(inPin, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
 state = debounced();
 if (lastState != state) {
   if (state) {
     actHigh = millis();
     if (lastHigh > 0) {
       timeDifference = actHigh - lastHigh;
       Serial.println(timeDifference);
     }
     lastHigh = actHigh;
   }
   lastState = state;
 } 
}

byte debounced(){
   static unsigned long lastTime = 0;
   static byte lastS = HIGH;
   static byte ReturnState = HIGH;
   byte actState = digitalRead(inPin);
   if (actState != lastS){
     lastS = actState;
     lastTime = millis();
   }
   if (millis()-lastTime > 30) {
     ReturnState = actState;
   }
   return ReturnState;
}

The simple hardware setup:

image

To be tested here:

https://wokwi.com/projects/335918253739082324

There will surely be more advanced/simple solutions (e.g. also by using an interrupt with FALLING edge detection).

Made it this way because you may not require the debouncing part of it ...

That's "cycles per milli-minute". You probably want:
intRPM = 60000 / duration;
(cycles per minute)

Such a scenario has a minimum recovery time as you

  • idle at a high
  • begin timing on a low
  • continue timing while pin is high
  • stop timing when pin is low

Stray capacitance and inductance may play a role if the low-to-low period is small. As the input pin is high-Z, you will need pull-up or pull-down resistance depending on the nature of the single-pin signal (internal, weak pull up/down may not be satisfactory.)

Thus, I have marked the 1st transition if I understand what you are wishing to accomplish:

Thanks guys,

Ec2021, so I could run your code without the de-bounce function?


state = actstate();
 if (lastState != state) {
   if (state) {
     actHigh = millis();
     if (lastHigh > 0) {
       timeDifference = actHigh - lastHigh;
       Serial.println(timeDifference);
     }
     lastHigh = actHigh;
   }
   lastState = state;
 }

Mrburnette, yep, start counting at the first low, idle at high, stop counting at the next low. Idle at high, start counting at next low etc. The input pin is already defined as as INPUT_PULLUP.

Im trying to calculate RPM by detecting the time between pulses (low when active, high when not) of a magnetic proximity sensor.

At minimum rotation speed the pin is LOW for probably 2 seconds, HIGH for 20 seconds. Max rotation pin LOW for estimated 300ms, HIGH for 2.5 seconds.

I want to keep the code as simple as possible and without the use of interrupts, as I don’t think the speeds involved above warrant it.

Look at this example.

Do you understand what is being done ?



//********************************************^************************************************
#define PUSHED                       LOW
#define RELEASED                     HIGH

#define CLOSED                       LOW
#define OPEN                         HIGH

#define ENABLED                      true
#define DISABLED                     false

#define LEDon                        HIGH
#define LEDoff                       LOW

//********************************************^************************************************
const byte buttonPin               = 2;

const byte ledPin                  = 12;
const byte heartbeatLED            = 13;

byte lastButtonPinState            = RELEASED;

//timing stuff
unsigned long heartbeatMillis;
unsigned long switchMillis;

unsigned long switchLowMillis;


//                                       s e t u p ( )
//********************************************^************************************************
void setup()
{
  Serial.begin(115200);
  
  pinMode(buttonPin, INPUT_PULLUP);

  pinMode(heartbeatLED, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LEDoff);

} //END of    setup()


//                                       l o o p ( )
//********************************************^************************************************
void loop()
{
  //*********************************                             h e a r t b e a t   T I M E R
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatMillis >= 500ul)
  {
    //restart this TIMER
    heartbeatMillis = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*********************************                      c h e c k   s w i t c h   T I M E R
  //is it time to check the switches ?
  if (millis() - switchMillis >= 50ul)
  {
    //restart this TIMER
    switchMillis = millis();

    checkSwitches();
  }

} //END of    loop()


//                              c h e c k S w i t c h e s ( )
//********************************************^************************************************
//this function runs every 50ms; this effectively debounces your inputs
void checkSwitches()
{
  byte currentState;

  //************************************************                      b u t t o n P i n
  currentState = digitalRead(buttonPin);

  //has this switch changed state ?
  if (lastButtonPinState != currentState)
  {
    //update to the new switch state
    lastButtonPinState = currentState;

    //*************************
    //has this switch been pushed ?
    if (currentState == PUSHED)
    {
      //**********
      //1st push of this switch
      //is the output currently OFF ?
      if (digitalRead(ledPin) == LEDoff)
      {
        //save the time the switch was first pushed
        switchLowMillis = millis();
        Serial.println("1st Push Detected");

        digitalWrite(ledPin, LEDon);
      }

      //**********
      //2nd push of this switch
      else
      {
        digitalWrite(ledPin, LEDoff);

        Serial.print(millis() - switchLowMillis);
        Serial.println(" ms \n");
      }
    }

  } //END of   this switch


  //*********************************
  // Future switches go here
  //*********************************

} //END of   checkSwitches()

That should work depending on what your actState() function returns.

The debouncing is required if the physical switch is not providing "clean" state changes but e.g. spikes before the state settles. Bouncing leads to multiple unwanted changes in short time.

The function in loop() checks for a change of the state. If a change to HIGH was detected

  • the difference between the last LOW to HIGH (lastHigh) change is calculated and printed
  • the recent time of the change is then stored in the global variable
  • the function waits for the next LOW to HIGH change.

The "if (lastHigh > 0)" avoids printing the first time a change to HIGH is detected (as 0 is not a valid detected time).

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.