IchigoJamでキッチンタイマー

[こどもパソコン IchigoJam](https://ichigojam.net/)でキッチンタイマーを作ってみました。
※IchigoJamはjig.jpの登録商標です

# 要件定義

今回作る「キッチンタイマー」は、以下のようなプログラムとします。

* 最初に設定する時間の入力を受け付ける
* 1秒単位で残り時間のカウントダウンを開始する
* 設定した時間が経過したら、合図をする

# 先行研究

既存のIchigoJamを用いたタイマーとして、例えば以下のようなものが公開されています。

## 3分タイマー

[3分タイマー - イチゴジャム レシピ](https://15jamrecipe.jimdofree.com/basic/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0/3%E5%88%86%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC/)

設定時間はハードコードされ、分単位で設定できます。
このプログラムでは、9分までしか測れません。

## New タイマー

[New タイマー - イチゴジャム レシピ](https://15jamrecipe.jimdofree.com/basic/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0/new-%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC/)

ハードコードで秒単位まで設定できます。
「3分タイマー」と同様の仕組みなので、おそらく同じく9分程度までしか測れないでしょう。

## 教室タイマー

[教室タイマー - SHIROのIchigoJam日記](https://www.ichigojaman.jp/entry/20180121/1516476213)

時間を実行開始後の入力で設定でき、99分まで測ることができます。
ただし、誤差が大きいです。
「IchigoJam実機で、10分設定で1～2秒早く終わります。」
と主張していますが、実際に実験してみると1時間設定でカウントが約49秒遅く終わり、
誤差はおよそ8.2%となりました。
なお、実機ではそのままだとエラーが出たので、
`GSB`を`GOSUB`に、`RTN`を`RETURN`に、`CONT`を`GOTO (その行)`に書き換えて実験を行いました。

# 今回の作品

## シリアル出力版

最初に設定時間(時・分・秒)の入力を受け付け、残り時間を1秒ごとに単純に出力します。
本質でない部分を最低限にし、アルゴリズムのチェック用として作りました。
また、単純な出力のため、ビデオ出力が見られずシリアル通信だけで利用する状況でも見やすいです。

時間の測定には、`TICK()`を用います。
これは1秒あたり60増える時刻を返してくれるので、次にカウントダウンをする時刻を管理し、
その時刻に到達したらカウントダウンをします。
なお、この時刻は32767の次が32768の代わりに0になるようなので、それを考慮して判定をします。

2時間に設定して実験をしたところ、1時間経過した時点で11秒早く、2時間後には22秒早くカウントが終わりました。
誤差は約0.31%でした。
水晶を使っているにしては誤差が大きい気がしますが、まあ「キッチン」タイマーとしては使える程度かな。

```
10 'ｷｯﾁﾝﾀｲﾏｰ (ｼﾘｱﾙ)
20 INPUT "ﾅﾝｼﾞｶﾝ?",H:IF H<0 ?"ｾﾞﾛｲｼﾞｮｳ ﾆ ｼﾃﾈ":GOTO 20
30 INPUT "ﾅﾝﾌﾝ  ?",M:IF M<0 ?"ｾﾞﾛｲｼﾞｮｳ ﾆ ｼﾃﾈ":GOTO 30
40 INPUT "ﾅﾝﾋﾞｮｳ?",S:IF S<0 ?"ｾﾞﾛｲｼﾞｮｳ ﾆ ｼﾃﾈ":GOTO 40
50 IF H+M+S=0 ?"ｾﾞﾝﾌﾞ ｾﾞﾛ ｼﾞｬ ﾀﾞﾒﾀﾞﾖ":GOTO 20
60 ?"ﾅﾆｶ ｷｰｦ ｵｼﾃ ｽﾀｰﾄ"
70 IF INKEY()=0 GOTO 70
80 ?H;":";M;":";S:CLT:P=TICK()+60
90 T=TICK():IF T<P OR T-P>10000 GOTO 90
100 IF P<32708 P=P+60 ELSE P=P-32708
110 S=S-1
120 IF S<0 S=59:M=M-1
130 IF M<0 M=59:H=H-1
140 ?H;":";M;":";S
150 IF H+M+S>0 GOTO 90
160 ?"ｼﾞｶﾝ ﾃﾞｽ !"
```

## 本格版

設定時間(時・分・秒)の入力を受け付け、スタートすると、大きい文字で残り時間をカウントします。
設定時間が経過すると、キッチンタイマーらしく「ピピピピ」と鳴ります。
ついでにLEDも点滅します。このとき、キーを押すと止まります。

大きい文字の表示は、`#80`台のグラフィック文字と、内蔵フォントを用いて実装しました。
内蔵フォントはMSBに左のドット、LSBに右のドットが入っている形式なので、
表示する時に使いやすいように、最初に配列に格納してビット列を反転させます。
表示の更新は前回表示した数字を覚えておいて変わった時だけ出力することで、コストを抑えます。

誤差は、シリアル版と同じく1時間に設定して約11秒早く終わる程度でした。

```
10 'ｷｯﾁﾝﾀｲﾏｰ
20 FORI=0TO79:POKE#800+I,PEEK(#180+I):NEXT:FORI=0TO39:T=[I]:T=T>>1&#5555|T<<1&#AAAA:T=T>>2&#3333|T<<2&#CCCC:T=T>>4&#0F0F|T<<4&#F0F0:[I]=T:NEXT:GOTO50
30 IFN-[C][C]=N:FORI=0TO3:LCX,10+I:FORJ=0TO3:V=[N*4+I]>>(J*2):?CHR$(#80+V>>6&#C+V&3);:NEXT:NEXT
40 RETURN
50 INPUT"ﾅﾝｼﾞｶﾝ?",H:IFH<0ORH>99?"0 ｶﾗ 99 ﾆ ｼﾃ":GOTO50
60 INPUT"ﾅﾝﾌﾝ  ?",M:IFM<0ORM>59?"0 ｶﾗ 59 ﾆ ｼﾃ":GOTO60
70 INPUT"ﾅﾝﾋﾞｮｳ?",S:IFS<0ORS>59?"0 ｶﾗ 59 ﾆ ｼﾃ":GOTO70
80 IFH+M+S=0?"ｾﾞﾝﾌﾞ ｾﾞﾛ ﾊ ﾀﾞﾒ":GOTO50
90 LET[40],-1,-1,-1,-1,-1,-1:?"ﾅﾆｶ ｷｰｦ ｵｼﾃ ｽﾀｰﾄ":CLK
100 IF!INKEY()GOTO100
110 CLS:LC10,11:?CHR$(#81):LC10,12:?CHR$(#84):LC21,11:?CHR$(#81):LC21,12:?CHR$(#84):CLT:P=TICK()+60:GOTO160
120 T=TICK():IFT<PORT-P>9999GOTO120
130 IFP<32708P=P+60ELSEP=P-32708
140 S=S-1:IFS<0S=59:M=M-1
150 IFM<0M=59:H=H-1
160 N=H/10:C=40:X=1:GOSUB30:N=H%10:C=41:X=5:GOSUB30:N=M/10:C=42:X=12:GOSUB30:N=M%10:C=43:X=16:GOSUB30:N=S/10:C=44:X=23:GOSUB30:N=S%10:C=45:X=27:GOSUB30:IFH+M+S>0GOTO120
170 CLK:LC0,14
180 LED1:FORJ=0TO3:BEEP10,4:WAIT8:NEXT:LED0:WAIT30:IF!INKEY()GOTO180
```

### 実行結果

YouTube動画 (2:33)
[![IchigoJamでキッチンタイマー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/7621c848-9d93-4dd9-a21a-93f5186768be.png)
](https://www.youtube.com/watch?v=M4BL6OoS7QY)

# 結論

IchigoJamを用いて、長時間の設定ができて誤差もある程度抑えたキッチンタイマーを作ることができました。
本格版では表示の都合上99時間59分59秒までですが、
シリアル出力版では32767時間32767分32767秒 (約3.8年) まで大丈夫なはずです。