While with rising pulse detection: how to?

A while needs to be executed until a sensor rising flank is detected, but with the caveat that the sensor already is high at the start of the while.

For example: an optical detector senses a rotating disc with one open slot. A motor needs to turn this disc one revolution starting with the slot being detected, until after one rotation the slot is detected again.

How to best write this condition?

Either an ISR or a state change detector inside the loop().

Have a look at ISR set on RISING...

 attachInterrupt(digitalPinToInterrupt(PIN), myIsr, RISING);

I should have said that both available interrupts already are in use.

Use a state machine. In the first state, check for the input to be LOW, in the next state wait for the input to be HIGH. Do the action in the third state.

1 Like

Thanks sterretje, but is it not reverse: first HIGH, then LOW, then move from LOW to HIGH? And can this be written in the while condition?

Meanwhile I have been thinking of an alternative: first execute the while with condition (readSensor) which would be high, then while running the sensor turns low so a second while with condition (!readSensor).

The code source (or at least a portion of it) should have helped a lot to better understand your environment...
Anyway, first question is does one of the two current interrupts is attached to the same pin you need to detect for RISING? In this case, you could distinguish between rise and fall on the ISR iself, and set a global variable accordingly.
If you already have two interrupts allocated for other purposes, you must decide if one of them can be moved to loop (typically the one that doesn't need a prompt interrupt response), and switch it with the new one.
Otherwise, you can only do the work inside the loop(), providing you don't have much delays or other blocking statements. But without the code, I can't say how.

You want to detect a rising edge. If the input is HIGH in the first state, you wait till it goes LOW before going to the next state. If it's already LOW, you can move straight to the second state.

In the second state you wait for the input to be HIGH.

bool detectRising()
{
  enum class STATES: uint8_t
  {
    WAITLOW,
    WAITHIGH,
  };

  static STATES state;

  bool rb = false;

  switch(state)
  {
    case STATES::WAITLOW:
      if(digitalRead(somePin == LOW)
      {
        state = STATES::WAITHIGH;
      }
      break;
    case STATES::WAITHIGH:
      if(digitalRead(somePin == HIGH)
      {
        // for the next time that you want to detect the rising edge
        state = STATES::WAITLOW;
        // set return value;
      }
      break;
  }
  return rb;
}

You can call this from a while-loop; when the function returns true, a rising edge was detected. Be aware that you will introduce blocking code if you use the above in a while-loop so it's probably not what you want.

while-loops basically don't belong in Arduino code !!

Note:
Typed on a mobike phone so code only to give you the idea.

In that case, wait for a low, then start the task.

while (sensor() == HIGH); //do nothing
while (sensor() == LOW) do_task();

Why not sterretje?

Because they block. Same applies for for-loops. There are a few exceptions; e.g. setting elements of an array or reading an array.

Assume that you want to blink a LED and want to detect a button press using the simple blink example. Add a button between pin and GND.

const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
  
  Serial.begin(115200);
}

void loop()
{
  digitalWrite(ledPin, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(1000);                 // wait for a second
  digitalWrite(ledPin, LOW);   // turn the LED off by making the voltage LOW
  delay(1000);                 // wait for a second

  // wait till button is pressed
  while (digitalRead(btnPin) == HIGH) {}
  
  // continue the code
  Serial.println(F("Button was pressed"));
}

The effect is that you will only see one blink and next the code waits for the button to be pressed. The Serial.print is just to demonstrate some action that you want to take.

Now replace the while with an if. If the button is not pressed, the code will keep blinking the LED.

const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);

  Serial.begin(115200);
}

void loop()
{
  digitalWrite(ledPin, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(1000);                 // wait for a second
  digitalWrite(ledPin, LOW);   // turn the LED off by making the voltage LOW
  delay(1000);                 // wait for a second

  // if button is pressed
  if (digitalRead(btnPin) == LOW)
  {
    Serial.println(F("Button is pressed"));
  }
  
  // continue the code
}

Now this is still not perfect because the button will only be read every 2 seconds. To solve that, you have to use a none-blocking blink as demonstrated in blink-without-delay. Below is a way to achieve it

const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);

  Serial.begin(115200);
}

void loop()
{
  // remember the state of the LED
  static uint8_t ledState = LOW;
  // remember the last time that the LED was updated
  static uint32_t delayStarttime;

  // if it's time to update the LED
  if (millis() - delayStarttime >= 1000)
  {
    // remember the time that we did update the LED
    delayStarttime = millis();
    // toggle the LED's state
    ledState = !ledState;
    // update the LED
    digitalWrite(ledPin, ledState);
  }

  // if button is pressed
  if (digitalRead(btnPin) == LOW)
  {
    Serial.println(F("Button was pressed"));
  }
  // continue the code
}

And in the next step you can place the blink functionality in a function

const uint8_t btnPin = 2;
const uint8_t ledPin = LED_BUILTIN;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);

  Serial.begin(115200);
}

// the loop function runs over and over again forever
void loop()
{
  // blink the LED
  blink();
  
  // wait till button is pressed
  if (digitalRead(btnPin) == LOW)
  {
    Serial.println(F("Button was pressed"));
  }
  // continue the code
}

/*
  non-blocking blink
*/
void blink()
{
  // remember the state of the LED
  static uint8_t ledState = LOW;
  // remember the last time that the LED was updated
  static uint32_t delayStarttime;

  // if it's time to update the LED
  if (millis() - delayStarttime >= 1000)
  {
    // remember the time that we did update the LED
    delayStarttime = millis();
    // toggle the LED's state
    ledState = !ledState;
    // update the LED
    digitalWrite(ledPin, ledState);
  }
}

Only thing to optimise in the above code is the magic number 1000.

I prefer to use static variables. They are remembered between the different calls of the function (like global variables are) but are local to the function (like normal variables); the advantage of the latter is that you can use the same variable name in different functions without them interfering with each other.

I hope this clarifies it a bit.

1 Like

That is very clear, thank you for that extensiove answer sterretje.
But what if a certain part of the code needs to be blocking, for example waiting for a keypad entry while all the rest can wait?

Example: a steppermotor (using Accelstepper.h) needs to be positioned depending on a keypad entry while the rest of the loop should not be executed because there are instructions to disable the driver outputs.

If I understand you well then the instructions (in the loop) to disable outputs should be placed in an if-loop to prevent being executed while waiting for this keypad entry, which now is in an if-loop intead of a while?

Obviously in this case you can do it with a while() loop. But on "normal" coding it's often a bad habit for the reasons @sterretje has already explained to you, so I always avoid to have while loops in my cose, even if it could be used like the exception you said, to let me expand the code functionalities to perform other tasks also. I just use non blocking code, with either a flag or, better, a finite state machine.

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