Arduinoで作るデジタル腕時計 - 有限状態マシンによる実装
必要な部品と材料
| Arduino UNO(またはLCDキーパッドシールド互換ボード) | ×1 |
| LCDキーパッドシールド | ×1 |
使用するアプリとオンラインサービス
- YAKINDU Statechart Tools(非商用利用無料)
- Eclipse C++ IDE for Arduino
プロジェクト概要
YAKINDU Statechart Toolsを使って状態機械モデルからC++コードを生成し、LCDキーパッドシールド搭載のArduinoで動くデジタル腕時計を作ります。
元のモデルはDavid Harelの論文「従来の状態機械・状態図形式の広範な拡張」で例示されていたデジタルウォッチです。これをYAKINDUで再構築し、Arduino上で動作させました。
デジタルウォッチの仕様
90年代に流行った多機能デジタルウォッチを想像してください。ストップウォッチ、複数アラーム、毎正時のビープ音など。
入力ボタン: On/Off、Mode、Set、さらにLight On/Off。
Modeボタンで以下のモードを切替:
- 時刻表示(Clock)
- 日付表示(Date)
- アラーム設定(Alarm 1, Alarm 2)
- チャイム(毎正時ビープ)有効/無効(Set Chime)
- ストップウォッチ(Stop Watch)
メニュー内ではOn/Offボタンで設定変更、Setボタンで時刻/アラーム設定。ストップウォッチはLight On/Offボタンでスタート/ストップ、ラップ機能も搭載。チャイムとバックライト制御も内蔵(初期版ではArduino未接続)。
状態機械モデル






モデルはグラフィカル(状態図)とテキスト(定義部)の2部構成。定義部でイベント、変数、操作を定義し、状態図で論理を記述。
内部イベントとして100msごとにtime変数をインクリメント:
every 100 ms / time += 1
この100msステップから現在時刻をHH:MM:SS形式で計算:
display.first = (time / 36000) % 24; // 時
display.second = (time / 600) % 60; // 分
display.third = (time / 10) % 60; // 秒
状態機械呼び出し毎にupdateLCD操作で表示更新:
display.updateLCD(display.first, display.second, display.third, display.text)
合成状態、履歴、サブダイアグラム、出口ノード等のモデリング要素を活用し、可読性を確保。詳細はユーザーガイド参照。
LCDキーパッドシールド
シンプルなHMI(人間-機械インターフェース)に最適。5つのユーザーボタン+リセットボタンを装備。5ボタンはすべてA0ピンに接続され、分圧回路で各ボタンの電圧が異なるため、analogRead(0)で判別可能。
メーカーによって値は異なります。以下は実測値:
- 未押下: 1023
- Select: 640
- Left: 411
- Down: 257
- Up: 100
- Right: 0

閾値判定コード:
#define NONE 0
#define SELECT 1
#define LEFT 2
#define DOWN 3
#define UP 4
#define RIGHT 5
static int readButton() {
int result = analogRead(0);
if (result < 50) return RIGHT;
if (result < 150) return UP;
if (result < 300) return DOWN;
if (result < 550) return LEFT;
if (result < 850) return SELECT;
return NONE;
}
状態機械とのインターフェース
生成されたC++コードが提供するインターフェースを実装して状態機械を制御します。ボタン入力をイベントに変換する際、チャタリング除去(デバウンス)が必須。でないとイベントが複数回発火し、予測不能な動作になります。
ここでは立下りエッジ(ボタン離し)検出方式でデバウンス:
int oldState = NONE;
static void raiseEvents() {
int buttonPressed = readButton();
delay(80); // 80ms待機(50msより安定)
oldState = buttonPressed;
if (oldState != NONE && readButton() == NONE) {
switch (oldState) {
case SELECT: stateMachine->getSCI_Button()->raise_mode(); break;
case LEFT: stateMachine->getSCI_Button()->raise_set(); break;
case DOWN: stateMachine->getSCI_Button()->raise_light(); break;
case UP: stateMachine->getSCI_Button()->raise_light_r(); break;
case RIGHT: stateMachine->getSCI_Button()->raise_onoff(); break;
default: break;
}
}
}
全体の構成
メインプログラムは3要素で構成:
- 状態機械(
DigitalWatch) - タイマー(
CPPTimerInterface) - 表示ハンドラ(
DisplayHandler、通常のlcd.print()等)
DigitalWatch* stateMachine = new DigitalWatch();
CPPTimerInterface* timer_sct = new CPPTimerInterface();
DisplayHandler* displayHandler = new DisplayHandler();
setup()
void setup() {
stateMachine->setSCI_Display_OCB(displayHandler);
stateMachine->setTimer(timer_sct);
stateMachine->init();
stateMachine->enter();
}
loop()
long current_time = 0;
long last_cycle_time = 0;
void loop() {
raiseEvents(); // 入力イベント発火
last_cycle_time = current_time;
current_time = millis();
timer_sct->updateActiveTimer(stateMachine, // 経過時間でタイマー更新
current_time - last_cycle_time);
stateMachine->runCycle(); // 状態機械1サイクル実行
}
例題の入手
YAKINDU Statechart Tools IDEに付属の例題として入手可能:
File → New → Example → YAKINDU Statechart Examples → Next → Arduino - Digital Watch (C++)
IDEはこちらからダウンロード。30日トライアル後、非商用利用は無料ライセンス取得可能!
製造プロセス
- Arduinoで作るモニター用Ambilightシステム:没入感アップの自作ガイド
- Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
- Arduinoで超音波レベーション(浮遊)装置を自作:ステップバイステップガイド
- Arduinoとスマホで作るスマート電圧計 – 簡単DIYプロジェクト
- ArduinoとMAX30100で作るリアルタイムIoT心拍数モニター
- Arduino UNO WiFi Webサーバー:ブラウザからLEDをON/OFF制御
- Arduinoで作る自作デジタルペット「Tamaguino」─ 3Dプリンター製ケース付き完全ガイド
- DIY Arduino スマートウォッチ — Apple Watch 風腕時計を自作
- Arduinoで自動化するChrome恐竜ゲーム – LDRとPythonで障害物を検知してジャンプ
- Arduino NanoとRDA5807Mで作るコンパクトFMラジオ
- 有限ステートマシン設計:コンセプトから実装まで