/*
  * ARDUINO STATE MACHINE 2022 07N
  * 
  */




  #include <avr/sleep.h>.
  #include <avr/wdt.h>

  #define VERSION "ARDUINO STATE MACHINE 2022 01"

  #define DEBUG 0
  #if (DEBUG)
  #define DEBUGP(x) Serial.print(x)
  #define DEBUGN(x) Serial.println(x)
  #else
  #define DEBUGP(x)
  #define DEBUGN(x)
  #endif




  //HZ Control
  #define HZ_SETTING 20
  int mainLoop_count;
  unsigned long fast_loopTimer; // Time in miliseconds of main control loop
  const int hzCount = (1000 / HZ_SETTING) - 1;
  const int timeRemind = 1000 / HZ_SETTING;

  void (*resetFunc)(void) = 0; //declare reset function @ address 0 call resetFunc(); if you want to reset

  int secCount = 0;
  #define LED_PIN 13


  #define LED_TASK1   3
  #define LED_FAN     4


  ///  --------------TASK 1 用到的變數 ----------------

  #define TASK_STATE_INIT      0
  #define TASK_STATE_A         1
  #define TASK_STATE_B         2
  #define TASK_STATE_UNKNOW    255

  byte taskStateCurrent=TASK_STATE_INIT;
  byte taskStateLast=TASK_STATE_UNKNOW;

              
  unsigned long task1Counter =0;  // State A 裡面專用的計數器
  unsigned long task1Timer   =0;  // State A 裡面專用的計時器

  
  ///  --------------FAN 風扇模擬器 用到的變數 ----------------


  #define FAN_STATE_INIT      0
  #define FAN_STATE_OFF       1
  #define FAN_STATE_ON        2
  #define FAN_STATE_UNKNOW    255
  

  byte fanStateCurrent=FAN_STATE_INIT;
  byte fanStateLast=FAN_STATE_UNKNOW;

  unsigned long taskCounterFAN =0;  // State B裡面專用的計數器
  unsigned long taskTimerFAN   =0;  // State B 裡面專用的計時器

  ///  --------------TASK 1  的狀態實作  -----------------------------------------------
  
  void task1StateA(){

  

    /// 下面這區只有進來的時候會作一次 -------------------------------
    if(taskStateCurrent!=taskStateLast){
      
      /// 顯示一下目前的狀態 
      Serial.println("Hello I am task1 State A");

      /// 把狀態裡面的變數清空
      taskStateLast = taskStateCurrent;
      task1Counter=0;
      task1Timer =0;

    }


    task1Timer++; 

    // 狀態A 說要 每秒閃5 次,那就是每100ms 要變換一次燈號
    // 目前這個系統跑在20 HZ ( 請參考一下LINE #27 的MACRO 設定 ),所以每1/20 會被呼叫一次
    // 所以只要每2次進來執行這個State 1次 就變換一下LED燈的狀態,就可跑到5HZ 的閃滅
    
    if(task1Timer%2 ==0){
      
      digitalWrite(LED_TASK1,!digitalRead(LED_TASK1));

    }		


      //// 假設這個工作要做10 秒, 然後跳到 狀態B
    if(task1Timer>= (10*HZ_SETTING)){

      taskStateCurrent = TASK_STATE_B;
    }

  

  }

  
  void task1StateB(){

  

    /// 下面這區只有進來的時候會作一次 -------------------------------
    if(taskStateCurrent!=taskStateLast){
      
      /// 顯示一下目前的狀態 
      Serial.println("Hello I am task1 State B");

      /// 把狀態裡面的變數清空
      taskStateLast = taskStateCurrent;
      task1Counter=0;
      task1Timer =0;

      /// 叫風扇開始開啟散熱
      fanStateCurrent = FAN_STATE_ON; ///// 這樣就可以調用到其他狀態機的狀態


    }


    task1Timer++; 

    // 狀態B 說要 每秒閃1 次,那就是每500ms 要變換一次燈號
    // 目前這個系統跑在20 HZ ( 請參考一下LINE #27 的MACRO 設定 ),所以每1/20 會被呼叫一次
    // 所以只要每10次進來執行這個State 1次 就變換一下LED燈的狀態,就可跑到 1 HZ 的閃滅
    
    if(task1Timer%10 ==0){
      
      digitalWrite(LED_TASK1,!digitalRead(LED_TASK1));

    }		

      //// 假設這個工作要做10 秒, 然後跳到 狀態A
    if(task1Timer>= (10*HZ_SETTING)){

      taskStateCurrent = TASK_STATE_A;
    }
  

  }

  void task1StateInit(){

    Serial.println("Hello I am task1 State Init");
    taskStateCurrent = TASK_STATE_A;

  }


  void task1StateUnknow(){
    Serial.println("Hello I am task1 State Unknow");

  }


  ///  --------------FAN 風扇的狀態實作  -----------------------------------------------

    
  void fanStateOFF(){



    /// 下面這區只有進來的時候會作一次 -------------------------------
    if(fanStateCurrent!= fanStateLast){
      
      /// 顯示一下目前的狀態 
      Serial.println("Hello I am FAN State OFF ");

      fanStateLast = fanStateCurrent;
      /// 把狀態裡面的變數清空
      taskCounterFAN=0;
      taskTimerFAN =0;

      ///關閉風扇 (進來的時候關閉)
      digitalWrite(LED_FAN,LOW);


    }


    taskTimerFAN++; 
    
  
    /// 在這裡等待被TASK1呼叫


  }


  
  void fanStateON(){



  /// 下面這區只有進來的時候會作一次 -------------------------------
    if(fanStateCurrent!= fanStateLast){
      
      /// 顯示一下目前的狀態 
      Serial.println("Hello I am FAN State ON ~ ON~ ON ~ ");

      fanStateLast = fanStateCurrent;
      /// 把狀態裡面的變數清空
      taskCounterFAN=0;
      taskTimerFAN =0;

      ///開啟風扇 (進來的時候開啟)
      digitalWrite(LED_FAN,HIGH);


    }

    taskTimerFAN++; 

      ///打個5 秒鐘後,回去關閉狀態
    
    if(taskTimerFAN >= (15*HZ_SETTING) ){
      
      fanStateCurrent = FAN_STATE_OFF;
    }		


  }
  


  void fanStateInit(){

    Serial.println("Hello I am fanStateInit");
    fanStateCurrent = FAN_STATE_OFF;

  }


  void fanStateUnknow(){
    Serial.println("Hello I am fanStateUnknow");

  }




  void doSM_TASK(){

  switch(taskStateCurrent){

    case TASK_STATE_INIT:

      task1StateInit();

    break;


    case TASK_STATE_A :

      task1StateA(); 

    break;

    case TASK_STATE_B :

      task1StateB();

    break;  

  
    default:

      task1StateUnknow();

    break;

  }
    

  }

  void doSM_FAN(){

    switch(fanStateCurrent){

    case FAN_STATE_INIT:

      fanStateInit();

    break;


    case FAN_STATE_ON:

      fanStateON();

    break;

    case FAN_STATE_OFF :

      fanStateOFF();

    break;

    default :

      fanStateUnknow();

    break;

    



    }
    

  }
  


  void hw_init()
  {

    // put your setup code here, to run once:

    Serial.begin(115200);

    pinMode(LED_PIN, OUTPUT);
    pinMode(LED_TASK1, OUTPUT);
    pinMode(LED_FAN, OUTPUT);

    
  }

  
  void setup()
  {


    //Setup I/O

    hw_init();


    //setup WDT

    wdt_enable(WDTO_4S);
  }



  void loop()
  {
  

    // system Loop  HZ define in  #define HZ_SETTING   5

    if (millis() - fast_loopTimer > hzCount)
    {
      fast_loopTimer = millis();
      mainLoop_count++;
      wdt_reset(); // Reset WDT ...

      /// do things

    doSM_TASK();
    doSM_FAN();
  
    

      if (mainLoop_count % HZ_SETTING == 0)
      {

        secCount++;
        digitalWrite(LED_PIN,!digitalRead(LED_PIN));
        DEBUGP("Sec count : ");
        DEBUGN(secCount);

        
      }
      // Time Remind for this Loop ---------------------------------------------------------------
      DEBUGP("Time Remind  ms :");
      DEBUGN(timeRemind - (millis()-fast_loopTimer));
    }
  }