#include "AlarmTone.h"
#include <SevSeg.h>
const int ColonPin = 13; //!< 7セグコロンピン番号
const int SpeakerPin = A3; //!< スピーカーピン番号
const int MinuteButtonPin = A0; //!< 分ボタンピン番号
const int SecondButtonPin = A1; //!< 秒ボタンピン番号
const int StartButtonPin = A2; //!< 開始ボタンピン番号
const int ClearButtonPin = A4; //!< クリアボタンピン番号
const int holdButtonTime = 2000; //!< 2000秒ボタンの長押し
const int autoCountupTime = 200; //!< 200msごとにカウントアップの時間がインクリメント
/**
* @brief 状態種別
*/
enum KitchenTimer
{
NoneState, //!< なし
StopState, //!< 停止状態
CountdownState, //!< カウントダウン状態
PlayAlarmState, //!< アラーム音出力状態
};
bool singleReadButton(int pinNo);
/**
* @brief ボタン情報クラス
*/
class Button
{
public:
/**
* @brief コンストラクタ(各メンバーを初期化)
* @param [in] pinNo ピン番号
*/
Button(int pinNo) : state_(false), changed_(false), repeat_(0), lastState_(false), hold_(false), holdTime_(0), pinNo_(pinNo){};
/**
* @brief ボタンの状態を取得する(チャタリング除去あり)
* @retval true 押されている
* @retval false 押されていない
*/
void readButton()
{
changed_ = false;
bool state = singleReadButton(pinNo_);
if (lastState_ == state)
{
if (state_ != state)
{
repeat_++;
if (repeat_ >= 3)
{
state_ = state;
changed_ = true;
}
}
}
else
{
repeat_ = 1;
}
lastState_ = state;
hold_ = false;
if (state == true)
{
if (holdTime_ >= holdButtonTime)
{
hold_ = true;
}
else
{
holdTime_++;
}
}
else
{
holdTime_ = 0;
}
}
/**
* @brief ボタンを押したか判断する
* @param [in] button ボタン情報
* @retval true 押された
* @retval false 押されていない
*/
bool isPressedButton()
{
return changed_ && state_;
}
/**
* @brief 長押し状態を取得
* @return 長押し状態
*/
bool isHoldButton()
{
return hold_;
}
private:
bool state_; //!< 押下状態 (true:押下, false:解放)
bool changed_; //!< 押下状態の変化有無 (true:変化あり, false:変化なし)
int repeat_; //!< 押下状態繰り返し回数 (チャタリング除去情報)
bool lastState_; //!< 前回の押下状態 (チャタリング除去情報)
bool hold_; //!< 長押し状態 (true:長押し中, false:その他)
int holdTime_; //!< ボタン押下時間 [ms]
int pinNo_; //!< ピン番号
};
KitchenTimer currentState = StopState; //!< 現在の状態
Button minuteButton(MinuteButtonPin); //!< 分ボタン情報
Button secondButton(SecondButtonPin); //!< 秒ボタン情報
Button startButton(StartButtonPin); //!< 開始/停止ボタン情報
Button clearButton(ClearButtonPin); //!< クリアボタン情報
AlarmTone alarmTone; //!< アラーム音制御
SevSeg sevseg; //!< 7セグ制御
uint64_t minutes = 0; //!< 分
uint64_t seconds = 0; //!< 秒
uint64_t countdownTime = 0; //!< カウントダウン時間[ms]
uint64_t startedCountdownTime = 0; //!< 開始されたときのカウントダウン時間[ms]
bool displayColonState = true; //!<表示される7セグコロンの状態(true:点灯,false:消灯)
bool playAlarm = false; //!< アラーム音出力(true:出力,false:出力していない)
/**
* @brief アラーム音出力の開始/停止
* @param [in] on 出力開始(true)/出力停止(false)
*/
bool setAlarm(bool on)
{
if (on)
{
alarmTone.play();
}
else
{
alarmTone.stop();
}
}
/**
* @brief 7セグのコロンの点滅
* @param [in] blinkColonTime コロンの点滅時間
* @retval true 点灯
* @retval false 消灯
*/
bool blinkColon(uint64_t blinkColonTime)
{
static bool colon = true;
static uint64_t colonCnt = 0;
if (colonCnt >= blinkColonTime)
{
colonCnt = 0;
sevsegSetColon(colon);
return colon = !colon;
}
colonCnt++;
}
/**
* @brief 7セグ表示数値の設定
* @param [in] minute 分 (0~99)
* @param [in] second 秒 (0~59)
*/
void sevsegSetNumber(int minute, int second)
{
sevseg.setNumber(minute * 100 + second);
}
/**
* @brief 7セグのコロン表示
* @param [in] on 表示(true)/非表示(false)
*/
void sevsegSetColon(bool on)
{
digitalWrite(ColonPin, on ? LOW : HIGH);
}
/**
* @brief 時間をインクリメントするか判定
* @param [in] button ボタン情報
* @retval true インクリメントする
* @retval false インクリメントしない
*/
bool shouldIncrementTime(Button &button)
{
static unsigned int holdIncTime = 0;
if (button.isHoldButton())
{
holdIncTime++;
if (holdIncTime >= autoCountupTime)
{
holdIncTime = 0;
return true;
}
}
else if (button.isPressedButton())
{
holdIncTime = 0;
return true;
}
return false;
}
/**
* @brief 前回実行時から1ms以上経過しているか判断する
* @retval true 1ms以上経過している
* @retval false その他
*/
bool is1msElapsed()
{
static uint32_t lastTime = 0;
uint32_t now = millis();
if (lastTime == now)
{
return false;
}
lastTime = now;
return true;
}
/**
* @brief 停止状態の繰り返し処理
*/
KitchenTimer stopStateDo()
{
if (shouldIncrementTime(minuteButton))
{
minutes = (minutes + 1) % 100;
}
if (shouldIncrementTime(secondButton))
{
seconds = (seconds + 1) % 60;
}
if (clearButton.isPressedButton())
{
minutes = 0;
seconds = 0;
}
if (startButton.isPressedButton())
{
if ((minutes > 0) || (seconds > 0))
{
countdownTime = (minutes * 60 + seconds) * 1000;
startedCountdownTime = countdownTime;
return CountdownState;
}
}
displayColonState = true;
playAlarm = false;
return NoneState;
}
/**
* @brief カウントダウン状態の繰り返し処理
*/
KitchenTimer countdownStateDo()
{
if (countdownTime == 0)
{
return PlayAlarmState;
}
if (startButton.isPressedButton())
{
return StopState;
}
countdownTime--;
minutes = (countdownTime) / 1000 / 60;
seconds = (countdownTime) / 1000 % 60;
playAlarm = false;
displayColonState = blinkColon(500);
return NoneState;
}
/**
* @brief アラーム出力状態の繰り返し処理
*/
KitchenTimer playAlarmStateDo()
{
if (startButton.isPressedButton())
{
minutes = (startedCountdownTime) / 1000 / 60;
seconds = (startedCountdownTime) / 1000 % 60;
playAlarm = false;
return StopState;
}
displayColonState = blinkColon(250);
playAlarm = true;
return NoneState;
}
/**
* @brief ボタン状態を取得
* @param [in] pinNo ピン番号
* @retval true 押下中
* @retval false 解放中
*/
bool singleReadButton(int pinNo)
{
return digitalRead(pinNo) == LOW;
}
/**
* @brief 入力の更新
*/
void updateInput()
{
minuteButton.readButton();
secondButton.readButton();
startButton.readButton();
clearButton.readButton();
}
/**
* @brief 出力の更新
*/
void updateOutput()
{
// 7セグ表示
sevsegSetNumber(minutes, seconds);
sevsegSetColon(displayColonState);
sevseg.refreshDisplay();
//アラーム音出力
setAlarm(playAlarm);
}
/**
* @brief 初期化処理
*/
void setup()
{
Serial.begin(115200);
// ピン設定
pinMode(MinuteButtonPin, INPUT_PULLUP);
pinMode(SecondButtonPin, INPUT_PULLUP);
pinMode(StartButtonPin, INPUT_PULLUP);
pinMode(ClearButtonPin, INPUT_PULLUP);
pinMode(ColonPin, OUTPUT);
// アラーム初期化
alarmTone.begin(SpeakerPin);
// 7セグ初期化
byte digits = 4;
byte digitPins[] = {2, 3, 4, 5};
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12};
bool resistorsOnSegments = false;
bool updateWithDelays = false;
bool leadingZeros = true;
bool disableDecPoint = true;
sevseg.begin(COMMON_ANODE, digits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(90);
// 7セグ表示数値を0にする
sevsegSetNumber(0, 0);
}
/**
* @brief 繰り返し処理
*/
void loop()
{
// 1ms経過まで繰り返し処理をブロック
if (!is1msElapsed())
{
return;
}
updateInput();
KitchenTimer nextState = NoneState;
switch (currentState)
{
case StopState:
nextState = stopStateDo();
break;
case CountdownState:
nextState = countdownStateDo();
break;
case PlayAlarmState:
nextState = playAlarmStateDo();
break;
default:
break;
}
if (nextState != NoneState)
{
currentState = nextState;
}
updateOutput();
}