/*
* ARDUINO STATE MACHINE 2022 07N
* 使用CLI 切換狀態
*/
#include "SerialCommand.h"
#include <avr/sleep.h>.
#include <avr/wdt.h>
#define VERSION "ARDUINO STATE MACHINE 2022 CLI + State Change "
#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
SerialCommand SCmd; // The demo SerialCommand object
//HZ Control
#define HZ_SETTING 10
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 BTN_INPUT 8
/// --------------TASK 1 用到的變數 ----------------
#define TASK_STATE_INIT 0
#define TASK_STATE_A 1
#define TASK_STATE_B 2
#define TASK_STATE_C 3
#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 裡面專用的計時器
/// --------------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));
}
if(digitalRead(BTN_INPUT)==0){
task1Counter+=1;
Serial.print("!");
}else{
task1Counter =0;
}
if(task1Counter>=1) {
taskStateCurrent=TASK_STATE_B;
Serial.println("!");
}
}
void task1StateB(){
/// 下面這區只有進來的時候會作一次 -------------------------------
if(taskStateCurrent!=taskStateLast){
/// 顯示一下目前的狀態
Serial.println("Hello I am task1 State B");
/// 把狀態裡面的變數清空
taskStateLast = taskStateCurrent;
task1Counter=0;
task1Timer =0;
}
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));
}
//// 等待timeout 10 秒,如果沒有在感測到任何輸入,就跳回狀態A
if(task1Timer>= (10*HZ_SETTING)){
taskStateCurrent = TASK_STATE_A;
}
if(digitalRead(BTN_INPUT)==0){
task1Counter+=1;
Serial.print("!");
}else{
task1Counter =0;
}
//// 如果按鈕連續被壓下5 秒
if(task1Counter>=5*HZ_SETTING) {
taskStateCurrent=TASK_STATE_C;
Serial.println("!");
}
}
void task1StateC(){
/// 下面這區只有進來的時候會作一次 -------------------------------
if(taskStateCurrent!=taskStateLast){
/// 顯示一下目前的狀態
Serial.println("Hello I am task1 State C");
/// 把狀態裡面的變數清空
taskStateLast = taskStateCurrent;
task1Counter=0;
task1Timer =0;
}
task1Timer++;
// 狀態B 說要 每2 秒閃1 次,那就是每500ms 要變換一次燈號
// 目前這個系統跑在20 HZ ( 請參考一下LINE #27 的MACRO 設定 ),所以每1/20 會被呼叫一次
// 所以只要每10次進來執行這個State 1次 就變換一下LED燈的狀態,就可跑到 1 HZ 的閃滅
if(task1Timer% 20 ==0){
digitalWrite(LED_TASK1,!digitalRead(LED_TASK1));
}
//// 假設這個工作要做30 秒, 然後跳到 狀態A
if(task1Timer>= (5*HZ_SETTING)){
taskStateCurrent = TASK_STATE_A;
}
}
void task1StateInit(){
Serial.println("Hello I am task1 State Init");
taskStateLast = taskStateCurrent;
taskStateCurrent = TASK_STATE_A;
}
void task1StateUnknow(){
Serial.println("Hello I am task1 State Unknow 冏RZ ");
taskStateLast = taskStateCurrent;
taskStateCurrent = TASK_STATE_A;
}
void doSM_TASK(){
switch(taskStateCurrent){
case TASK_STATE_INIT:
task1StateInit();
break;
case TASK_STATE_A :
task1StateA();
break;
case TASK_STATE_B :
task1StateB();
break;
case TASK_STATE_C :
task1StateC();
break;
default:
task1StateUnknow();
break;
}
}
void process_command()
{
int aNumber, bNumber;
char *arg;
Serial.println("We're in process_command");
arg = SCmd.next();
if (arg != NULL)
{
aNumber = atoi(arg); // Converts a char string to an integer
Serial.print("First argument was: ");
Serial.println(aNumber);
}
else
{
Serial.println("No arguments");
aNumber = 999;
}
arg = SCmd.next();
if (arg != NULL)
{
bNumber = atol(arg);
Serial.print("Second argument was: ");
Serial.println(bNumber);
}
else
{
Serial.println("No second argument");
bNumber = 999;
}
switch (aNumber)
{
case 0:
Serial.print("stateCurrent =");
Serial.println(taskStateCurrent);
break;
case 1:
//// 如果使用者沒有輸入參數,直接跳走
if(bNumber==999){
return;
}
taskStateCurrent = bNumber;
Serial.print("stateCueent =");
Serial.println(taskStateCurrent);
break;
case 2:
Serial.println("2");
break;
case 104:
Serial.println(VERSION);
Serial.println("本範例說明Timeout 的機制");
Serial.println("系統啟動後,會跳到狀態A等待使用者輸入,如果有偵測到狀態改變就會到狀態B");
Serial.println("在固定的時間內去確定是否有持續輸入,如果有就跳去狀態C");
Serial.println("如果狀態沒有持續,時間到(timeout) 就跳回狀態A");
break;
default:
break;
}
}
// This gets set as the default handler, and gets called when no other command matches.
void unrecognized()
{
Serial.println("What?");
}
void hw_init()
{
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(LED_TASK1, OUTPUT);
pinMode(BTN_INPUT, INPUT_PULLUP);
}
void setup()
{
//Setup I/O
hw_init();
//setup WDT
///wdt_enable(WDTO_4S);
///
SCmd.addCommand("P", process_command); // Converts two arguments to integers and echos them back
SCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
/// 使用提示
Serial.println("本範例說明Timeout 的機制");
Serial.println("系統啟動後,會跳到狀態A等待使用者輸入,如果有偵測到狀態改變就會到狀態B");
Serial.println("在固定的時間內去確定是否有持續輸入,如果有就跳去狀態C");
Serial.println("如果狀態沒有持續,時間到(timeout) 就跳回狀態A");
}
void loop()
{
SCmd.readSerial();
// 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();
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));
}
}