Nonblocking IO สำหรับ Arduino

เวลาเขียนโปรแกรมใน arduino เพื่อรอรับค่าจาก I/O หรืออะไรสักอย่าง ถ้าเราไม่ใช้ INTERRUPT
เราสามารใช้คำสั่ง delay ในการวนลูปรอจนกว่าจะจับสัญญาณได้ แต่เราจะเจอปัญหาว่าในระหว่างที่รอ Arduino จะไม่สามารถไปทำงานอื่นได้เลย เรียกว่ามันถูก Blocking

เพื่อไม่ให้ระหว่างที่รอแล้ว Arduino ไม่ทำงานอะไรเลย เราจะใช้วิธีกำหนดช่วงเวลาหนึ่งๆเอาไว้ แล้วให้ Arduino กระโดดออกไปทำอะไรบางอย่างก่อน เสร็จแล้วก็กระโดดกลับมาทำงานหลักก่อน จากนั้นพอถึงเวลาที่กำหนดก็กระโดดออกไปอีกรอบ จนกว่าจะได้สัญญาณที่ต้องการ จึงค่อยทำงานที่ต้องการได้ การที่มันไม่ block loop เอาไว้ จึงเรียกว่า non-blocking โดยเหมือนมันจะทำงาน multi task ได้ แต่จริงๆแล้วมันแค่กระโดดกลับไปกลับมาเฉยๆ

#include <Arduino.h>
class TimerTask {
  private:
    unsigned long previousMillis;
    unsigned long interval;
  public:
    TimerTask(unsigned long _interval);
    bool isReady();
};
#include <Arduino.h>
#include "TimerTask.h"


TimerTask::TimerTask(unsigned long _interval) {
  interval = _interval;
  previousMillis = 0;
}

bool TimerTask::isReady() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    return true;
  }
  return false;
}
#include "TimerTask.h"

TimerTask checkInTask(3600000); // 6000 = 6sec, 60000 = 1 minute, 3600000 = 1 Hr
TimerTask commandInQTask(60000);
//TimerTask rtcTask(500);      // 1   sec
TimerTask displayTimeOnLcdTask(1000);      // 1.2 sec
//TimerTask cmpTimeTask(5000);  // 5   second
TimerTask updateValveStatus(6000);


void loop() {
  if (checkInTask.isReady()) {
    checkInConfirm("REGULAR_CHECKIN");
  }

  if (displayTimeOnLcdTask.isReady()) {
    printCurrentDateOnLcd();
  }

  if (commandInQTask.isReady()) {
    getArrayOfCommandInQueue();
  }


  /*

  ########### MAIN FLOW IS LYING HERE - AND IT IS NOT BLOCKING BY ABOVE COMMAND/FUNCTION ########

  */

}

หลักการคือ Object TimerTask จะเก็บค่าเวลาไว้สองค่า คือ
previousMilli – ช่วงเวลาที่ทำงานไปครั้งล่าสุด
interval – ช่วงเวลารอ

isReady จะเช็คว่า currentMilli (เวลาปัจจุบัน) – previousMilli (เวลาที่ทำงานครั้งก่อน) มากกว่า interval หรือยัง

ถ้ายัง ก็ไปทำงานหลักต่อ

ถ้าเป็น TRUE แสดงว่าได้เวลาทำงานแล้ว ก็กระโดดออกไปก่อน เช่นไปอ่าน sensor ดูว่า sensor เป็น On หรือยัง ถ้ายังก็กลับ ไม่ต้องรอ ถ้า On แล้ว ก็ทำงานได้

จริงๆ เราสามารถเขียนโดยไม่ต้องสร้าง TimerTask ก็ได้แหล่ะ แต่มันคือ code จะดูยาวๆ ไม่สวย แล้วมันกำหนดไม่ได้ว่าจะให้ทำงานทุกๆกี่นาที กี่วินาที มันจะทำงานทุกครั้งที่วนลูป
ซึ่งการทำ TimerTask มันจะทำให้ code เราดูง่ายขึ้น แล้วกำหนดเวลาได้ชัดเจน ไม่อ่าน sensor หรือ api บ่อยจนเกินไป