/*
2022 0427
紅綠燈控制器最終版
https://wokwi.com/projects/330455834471432786
*/
#define VERSION "小姐姐講嵌入式系統設計 紅綠燈系統 最終回"
///////////////////////// Library 引入 //////////////////
#include "SerialCommand.h"
///////////////////////// 這個是常用的除錯傾印開關 //////////////////
//////////////////////// 0 不印 , 1 要印 ////////////////////////
#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 2 /// 讓系統一秒跑5次即可
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;
/////////////////////////硬體腳位相關的定義 //////////////////
#define LED_Rabbit_RED 11
#define LED_Rabbit_YELLOW 12
#define LED_Rabbit_GREEN 7
#define LED_Tortoise_RED 8
#define LED_Tortoise_YELLOW 9
#define LED_Tortoise_GREEN 10
#define LED_PIN 13
#define SW_MODE A0
#define SW_LIGHT_CONTROL A1
#define KNOB_TIME_Setting A2
/////////////////////////Golbal 變數 //////////////////////////////////////////////////////
int secCount = 0;
byte flagShow=0;
/////////////////////////CLI 相關 //////////////////////////////////////////////////////
SerialCommand SCmd; // The demo SerialCommand object
void process_command()
{
int aNumber, bNumber, cNumber;
char *arg;
Serial.println("We're in process_command");
/// Para 1
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 1 st arguments");
aNumber= 9999;
}
/// Para 2
arg = SCmd.next();
if (arg != NULL)
{
bNumber = atoi(arg); // Converts a char string to an integer
Serial.print("2 nd argument was: ");
Serial.println(bNumber);
}
else
{
Serial.println("No 2nd arguments");
bNumber= 9999;
}
/// Para 3
arg = SCmd.next();
if (arg != NULL)
{
cNumber = atoi(arg); // Converts a char string to an integer
Serial.print("3rd argument was: ");
Serial.println(cNumber);
}
else
{
Serial.println("No 3rd arguments");
cNumber= 9999;
}
switch(aNumber){
case 0:
Serial.println("CMD 0");
break;
case 4:
if(bNumber==9999 ){
return;
}
flagShow= bNumber;
break;
case 104:
Serial.println(VERSION);
default:
Serial.println("Default");
break;
}
}
void unrecognized(const char *command)
{
Serial.println("What?");
}
///////////////////////// 紅綠燈的秒數宣告 ///////////////////////////////////////////////////
const int GREEN_LIGHT = 12; // Green light time in seconds
const int AMBER_LIGHT = 3; // Amber light time in seconds
const int DEAD_LIGHT = 2; // Dead light time in seconds
int trafficLightTimeTable[6] = {GREEN_LIGHT * HZ_SETTING,
AMBER_LIGHT *HZ_SETTING,
DEAD_LIGHT *HZ_SETTING,
(GREEN_LIGHT / 3) * HZ_SETTING,
AMBER_LIGHT *HZ_SETTING,
DEAD_LIGHT *HZ_SETTING};
///////////////////////// 狀態機宣告 ///////////////////////////////////////////////////
#define STATE_RABBIT_G_TORTO_R 0
#define STATE_RABBIT_Y_TORTO_R 1
#define STATE_RABBIT_R_TORTO_R 2
#define STATE_RABBIT_R_TORTO_G 3
#define STATE_RABBIT_R_TORTO_Y 4
#define STATE_RABBIT_R2_TORTO_R2 5
#define STATE_MANUAL_RABBIT_G 10
#define STATE_MANUAL_RABBIT_R 11
#define STATE_REMOTE_RABBIT_G 20
#define STATE_REMOTE_RABBIT_R 21
#define STATE_REMOTE_FLASH_MODE 22
int state = 0;
int lastState = -1;
int timeCounter;
void showState()
{
switch (state)
{
case STATE_RABBIT_G_TORTO_R:
Serial.println("STATE_RABBIT_G_TORTO_R");
break;
case STATE_RABBIT_Y_TORTO_R:
Serial.println("STATE_RABBIT_Y_TORTO_R");
break;
case STATE_RABBIT_R_TORTO_R:
Serial.println("STATE_RABBIT_R_TORTO_R");
break;
case STATE_RABBIT_R_TORTO_G:
Serial.println("STATE_RABBIT_R_TORTO_G");
break;
case STATE_RABBIT_R_TORTO_Y:
Serial.println("STATE_RABBIT_R_TORTO_Y");
break;
case STATE_RABBIT_R2_TORTO_R2:
Serial.println("STATE_RABBIT_R2_TORTO_R2");
break;
case STATE_MANUAL_RABBIT_G:
Serial.println("STATE_MANUAL_RABBIT_G");
break;
case STATE_MANUAL_RABBIT_R:
Serial.println("STATE_MANUAL_RABBIT_R");
break;
}
}
void doTrafficLightTask()
{
switch (state)
{
case STATE_RABBIT_G_TORTO_R:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[state];
secCount = 0;
digitalWrite(LED_Rabbit_RED, LOW);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, HIGH);
digitalWrite(LED_Tortoise_RED, HIGH);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
timeCounter = timeCounter - 1;
if (timeCounter == 0)
{
state = 1;
return;
}
break;
case STATE_RABBIT_Y_TORTO_R:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[STATE_RABBIT_Y_TORTO_R];
secCount = 0;
digitalWrite(LED_Rabbit_RED, LOW);
digitalWrite(LED_Rabbit_YELLOW, HIGH);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, HIGH);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
digitalWrite(LED_Rabbit_YELLOW, !digitalRead(LED_Rabbit_YELLOW));
timeCounter = timeCounter - 1;
if (timeCounter == 0)
{
state = 2;
}
break;
case STATE_RABBIT_R_TORTO_R:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[STATE_RABBIT_R_TORTO_R];
secCount = 0;
digitalWrite(LED_Rabbit_RED, HIGH);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, HIGH);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
timeCounter = timeCounter - 1;
if (timeCounter == 0)
{
state = 3;
}
break;
case STATE_RABBIT_R_TORTO_G:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[state];
secCount = 0;
digitalWrite(LED_Rabbit_RED, HIGH);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, LOW);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, HIGH);
}
timeCounter = timeCounter - 1;
if (timeCounter == 0)
{
state = 4;
}
break;
case STATE_RABBIT_R_TORTO_Y:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[state];
secCount = 0;
digitalWrite(LED_Rabbit_RED, HIGH);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, LOW);
digitalWrite(LED_Tortoise_YELLOW, HIGH);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
timeCounter = timeCounter - 1;
digitalWrite(LED_Tortoise_YELLOW, !digitalRead(LED_Tortoise_YELLOW));
if (timeCounter == 0)
{
state = 5;
}
break;
case STATE_RABBIT_R2_TORTO_R2:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
timeCounter = trafficLightTimeTable[state];
secCount = 0;
digitalWrite(LED_Rabbit_RED, HIGH);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, HIGH);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
timeCounter = timeCounter - 1;
if (timeCounter == 0)
{
state = STATE_RABBIT_G_TORTO_R;
}
break;
case STATE_MANUAL_RABBIT_G:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
secCount = 0;
digitalWrite(LED_Rabbit_RED, LOW);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, HIGH);
digitalWrite(LED_Tortoise_RED, HIGH);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
}
break;
case STATE_MANUAL_RABBIT_R:
if (lastState != state)
{
Serial.print("State Change to ");
Serial.println(state);
showState();
lastState = state;
secCount = 0;
digitalWrite(LED_Rabbit_RED, HIGH);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, LOW);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, HIGH);
}
break;
}
}
void hwInit()
{
Serial.begin(115200);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_Rabbit_RED, OUTPUT);
pinMode(LED_Rabbit_YELLOW, OUTPUT);
pinMode(LED_Rabbit_GREEN, OUTPUT);
pinMode(LED_Tortoise_RED, OUTPUT);
pinMode(LED_Tortoise_YELLOW, OUTPUT);
pinMode(LED_Tortoise_GREEN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_Rabbit_RED, LOW);
digitalWrite(LED_Rabbit_YELLOW, LOW);
digitalWrite(LED_Rabbit_GREEN, LOW);
digitalWrite(LED_Tortoise_RED, LOW);
digitalWrite(LED_Tortoise_YELLOW, LOW);
digitalWrite(LED_Tortoise_GREEN, LOW);
Serial.println(VERSION);
}
int adcReading = 0;
int adcReadingLast = -1;
byte modeSw = 0;
byte mmodeSwLast = 10;
//// 這邊的使用者輸入接收方法,僅供模擬用,實際不要這樣寫,會出問題。
void doGetUserInput()
{
/// 讀取時間設定旋鈕
adcReading = map(analogRead(KNOB_TIME_Setting), 0, 1023, 1, 5);
if (adcReading != adcReadingLast)
{
Serial.print(" ------------ 時間間隔調整 X ");
Serial.println(adcReading);
delay(1000); // 停下來一下,不然看不到
adcReadingLast = adcReading;
trafficLightTimeTable[0] = GREEN_LIGHT * HZ_SETTING * adcReading;
trafficLightTimeTable[1] = AMBER_LIGHT * HZ_SETTING * adcReading;
trafficLightTimeTable[2] = DEAD_LIGHT * HZ_SETTING * adcReading;
trafficLightTimeTable[3] = (GREEN_LIGHT / 3) * HZ_SETTING * adcReading;
trafficLightTimeTable[4] = AMBER_LIGHT * HZ_SETTING * adcReading;
trafficLightTimeTable[5] = DEAD_LIGHT * HZ_SETTING * adcReading;
}
////讀取自動,手動切換開關
modeSw = digitalRead(SW_MODE);
if (modeSw == 1)
{
if (mmodeSwLast != modeSw)
{
mmodeSwLast = modeSw;
Serial.println("Change to Manual Mode");
delay(1000); // 方便我們觀察狀態變換
}
if (digitalRead(SW_LIGHT_CONTROL) == HIGH)
{
state = STATE_MANUAL_RABBIT_G;
}
else
{
state = STATE_MANUAL_RABBIT_R;
}
}
else if (modeSw == 0)
{
if (mmodeSwLast != modeSw)
{
mmodeSwLast = modeSw;
Serial.println("Change to Auto Mode");
delay(1000); // 方便我們觀察狀態變換
state = STATE_RABBIT_G_TORTO_R; /// 從新來過 ...
}
}
}
////////////////////////////////// 開始工作了 //////////////////////////////////////////////////////////////
void setup()
{
hwInit();
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?")
}
// the loop function runs over and over again forever
void loop()
{
/// 放在這邊會有最快的回應
SCmd.readSerial();
/// 利用這個機制來管理時間,我們可以掌握到的時間間隔就是 1/HZ_SETTING (秒)
/// 以這個例子來說, HZ_SETTING = 5 , 所以每1/5 秒 會進入到這個環圈1次
if (millis() - fast_loopTimer > hzCount)
{
fast_loopTimer = millis();
mainLoop_count++;
doGetUserInput();
doTrafficLightTask();
//// 這個是方便我們使用的一個判斷,因為我們是設計系統每 1/5 秒進入迴圈一次,
//// 所以每進來5次就是 1 秒
if (mainLoop_count % HZ_SETTING == 0)
{
secCount++;
mainLoop_count = 0;
if(flagShow&0x1 ==1 ){
Serial.print(" S :");
Serial.println(secCount);
}
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
/// 這裡我們故意浪費100ms ,看看下面的時間計算是否正確??
/// delay(100);
}
// 在這裡我們來計算一次,這次跑進來總共用了多少時間,
/// 然後我們還有多少時間可以用
if(flagShow & 0x2 ==0x2){
Serial.print("一個迴圈是");
Serial.print(timeRemind);
Serial.print("ms,我還有剩下這麼多時間喔 :");
Serial.print(timeRemind - (millis() - fast_loopTimer));
Serial.print(" ms ");
}
}
}