Replacing delay () for non-blocking delays in the Arduino IDE

    The first thing a newbie mastering Arduino encounters is the unpleasant property of the delay () function - blocking program execution. Many examples on the Internet use this function, but practical application hints somehow that it is better to do without it.

    As befitting a beginner, I invented a bike made its implementation of non-blocking delay. The task was as follows:

    • Ensure pseudo-multitasking so that different events occur in due time, at their own intervals and do not block each other.
    • It was convenient to use it.
    • It could be designed as a library and easily included in other projects without copy-paste.

    Having seen that most of the Arduino libraries are made using OOP, I also decided not to work out and wrote the SmartDelay class , which can be obtained from the github as a zip to add to the Arduino IDE or make git clone in ~ / Arduino / libraries /

    The result is this.

    #include 
    SmartDelay foo(1000000UL); // в микросекундах
    void loop () {
      if (foo.Now()) {
        // Код здесь выполняется каждый интервал в микросекундах, указанный в конструкторе выше.
      }
      //Прочий код
    }
    

    The Now () method returns true if the interval has passed. In this case, the countdown starts again at the same interval. That is, Now () each time "recharges" automatically.

    Classic LED flashing can be complicated right away until two blink. For example, the bulbs are connected to the legs 12 and 11, should blink at intervals of 1s and 777ms, respectively.

    #include 
    SmartDelay led12(1000000UL); 
    SmartDelay led11(777000UL);
    setup () {
      pinMode(12,OUTPUT);
      pinMode(11,OUTPUT);
    }
    byte led12state=0;
    byte led11state=0;
    void loop () {
      if (led12.Now()) {
          digitalWrite(12,led12state);
          led12state=!led12state;
      }
      if (led11.Now()) {
          digitalWrite(11,led11state);
          led11state=!led11state;
      }
    }
    

    In the cycle, you can do something else, blinking LEDs will not block the execution of this code.

    It is clear that this is not a complete replacement for delay (), which stops the flow for a given time, you must always write the program as an MCA (state machine mechanism). That is, store the state and, depending on it, go to the right place in the code.

    Old version:

    ...
    action1();
    delay(1000);
    action2();
    delay(500);
    action3();
    ...
    

    New option:

    byte state=0;
    SmartDelay d();
    ...
    switch (state) {
    case 0: 
      action1(); 
      d.Set(1000000UL);
      state=1;
      break;
    case 1:
      if (d.Now()) {
        action2();
        d.Set(500000UL);
        state=2;
      }
      break;
    case 2:
      if (d.Now()) {
        action3();
        d.Stop();
        state=0;
      }
      break;
    }
    ...
    

    The Set method (interval) sets a new interval and returns the old one. You can just look at the interval using the Get () method;

    Stop () stops processing and Now () always returns false.

    Start () resumes work and Now () starts to work as usual.

    If you need to slow down the time count, but do not stop at all, that is, the Wait () method. For example, if LED 12 blinks and does not blink when a button is pressed, just add this code to loop () in the example with two diodes above:

    ...
    if (digitalRead(9)) led12.Wait();
    ...
    

    So, at a high signal level on the 9th leg, the diode for 12 will not blink and will continue when 0. appears there.

    When such a timer is drawn on the screen, for example, and the buttons are processed in parallel, it may be necessary to redraw the screen or part immediately after clicking on button, and not wait for the end of the interval. To do this, use the Reset () method, after which the next call to Now () will return true. For instance:

    SmartDelay display(1000000UL);
    void loop() {
      if (btClick()) display.Reset(); // ткнул в кнопку, надо отрисовать экранчик.
      if (display.Now()) screenRedraw(); // отрисовка экранчика.
    }
    

    From the bugs, I see only that the overflow of the microsecond counter is not taken into account, but otherwise, yes, you need to clean the code. I don’t like how Reset () is done while I think.

    I liked the object approach, it allows you to hide all the code in a library, which you can never look into later. Now this little library lives in all my projects :)

    Project on GitHub

    Only registered users can participate in the survey. Please come in.

    Did it make sense?

    • 42.5% Useful. 115
    • 25.5% Good try. 69
    • 3.3% Bullshit. 9
    • 11.4% Meaningless govnokod. 31
    • 0.7% I did better! 2
    • 11.1% Everything was invented before us! thirty
    • 5.1% What is delay ()? 14

    Also popular now: