2

I'm having issues trying to convert my code to non-blocking. I was able to do so in some parts, but the rest aren't the same.

When I try to replace delay with an if statement like the spinning and sensors debounce it doesn't work at the right time, because the variable I subtract from cur_ms is not updated.

What I'm trying to do is to always switch_dir() and update_code(), and when second_btn.released() I want the other two functions to keep running but also to run a sequence of actions (those with the delay) without blocking.

I would love to get your help.

Thanks in advance :)

void loop()
{
    uint32_t cur_ms = millis();

    if (cur_ms - spin_ms >= SPINNING_MS) {
        switch_dir();
        spin_ms = cur_ms;
    }

    if (cur_ms - read_ms >= SENSORS_DEBOUNCE_MS) {
        update_code(get_sensors_state());
        read_ms = cur_ms;
    }

    if (second_btn.released()) {
        ball_servo.write(BALL_SERVO_MAX);   // drop the ball
        digitalWrite(BLOWER_PIN, LOW);  // blower on
        delay(BALL_DOOR_MS);
        ball_servo.write(BALL_SERVO_MIN);   // close ball door
        delay(BLOWER_MS);
        digitalWrite(BLOWER_PIN, HIGH); // blower off
    }
}
1
  • if (second_btn.released()) { should only run the first two commands, store ball_door_ms and set a state variable, for example state = 2........ new if statement if (state == 2 and cur_ms - ball_door_ms >= BALL_DOOR_MS) { would close ball door, store blower-ms and set state = 3 .... an so on
    – jsotola
    Nov 24, 2020 at 17:49

3 Answers 3

1

You had the right idea with your first few if statements! Just keep checking if time has passed, don't delay()

uint32_t releasedTime = 0;
if (second_btn.released() && releasedTime == 0) {
    releasedTime = cur_ms; // remember that the button was pressed now
    ball_servo.write(BALL_SERVO_MAX);   // drop the ball
    digitalWrite(BLOWER_PIN, LOW);  // blower on
} else if ((cur_ms - releasedTime > BALL_DOOR_MS) && (cur_ms - releasedTime < BALL_DOOR_MS + BLOWER_MS)) {
    ball_servo.write(BALL_SERVO_MIN);   // close ball door
} else {
    // must be after BALL_DOOR_MS + BLOWER_MS, time to run
    digitalWrite(BLOWER_PIN, HIGH); // blower off
    releasedTime = 0; // so this doesn't keep running
}
1
  • Re “releasedTime = 0; // so this doesn't keep running”: this won't work as promised by the comment. The line above (digitalWrite(BLOWER_PIN, HIGH);) will run over and over in the idle state. You would have to condition on if (releasedTime != 0) in order to avoid that, but note that 0 is a perfectly valid timestamp periodically returned by millis(). Also, the line ball_servo.write(BALL_SERVO_MIN); will run repeatedly during BLOWER_MS ms. May not be a big deal, but it slows down other tasks in the loop(). Both issues would be solved by using a proper “state” variable. Nov 25, 2020 at 16:52
0

When second_btn.released is true, its two delay() statements will block the whole loop. You can avoid this by rewriting so that the delay() statements are handled like you already handle spin_ms and cur_ms, which will involve some state-machine-type logic so that (for example) you set a state when you enter the BALL_DOOR_MS delay, then return to the main loop which will have a branch that is taken only if the BALL_DOOR_MS state is active. When that state ends it sets a new state that enables a separate branch of the main loop to do the BALL_SERVO_MIN and start the BLOWER_MS state, and so on. A switch/case chain may be the best way to handle this, replacing the present second_btn.released.

3
  • 1
    This link might be helpful: The Finite State Machine. Nov 24, 2020 at 15:11
  • Switch-case will block the rest of the loop, isn't it? Could you write a code sample to showcase your idea? Nov 24, 2020 at 15:17
  • 1
    The link was very helpful. It works. Thank you very much :) Nov 24, 2020 at 15:41
0

<To design in the millis() function, you have to think of it like using a stop watch. You record a time at which you started. Let's say you started at 10000mS. Then every so often, you check the current time. Let's say the current time is 10500mS. You then take the current time and subtract it by your start time to get the time elapsed since you started. This gives you 500mS. Let's say you're desired delay time is 1000mS. Since the elapsed time isn't 1000mS you keep doing other things until the Current time - Start time is greater than or equal to your delay time. Then you do what ever task you need to do, and then record a new start time and do the process all over again. It would look something like this.>

#define DELAY_TIME     1000   // the amount of time you want to delay `


unsigned long startTime = 0;
unsigned long currentTime = 0;

void setup()
{
   startTime = millis();  // Record starting time
   currentTime = startTime; // Set current time to start time so elapsed time would = 0
}

void loop()

{
  
currentTime = millis(); // update the current time value
  
if((currentTime - startTime) >= DELAY_TIME)) // check amount of time elapsed  
  {  
    // The amount of delay time has elapsed
    // Do what ever you need to do here that requires the delay

       startTime = millis(); // Reset the start time so that it waits another 1000ms
       currentTime = startTime; // Set the current time = to start time to avoid negatives
    }
  // Then put the rest of the code that you want to run continuously here 

}
1
  • What you advise here is exactly what the OP is already doing. He knows how to do this. His issue is about combining this technique with a state machine for handling the ball door and blower. Nov 25, 2020 at 16:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.