IchigoDake + DakeCap で角度計を作ろうとしたが、惜しくも失敗した

[IchigoJam](https://ichigojam.net/) で動き、加速度センサを用いた角度計のプログラムを作成した。
[IchigoDake](http://dakeigai.shizentai.jp/) で動かしたかったが、失敗した。

※IchigoJamはjig.jpの登録商標です。

# ハードウェア

今回は、IchigoDake + [DakeCap](https://hello002.stores.jp/items/5cc11be94da8526afc2bbba3) で動くポータブルなガジェットを目指したので、加速度センサに加え小型液晶モジュールを搭載することにした。
そして、加速度センサや液晶などをまとめて IchigoJam に接続できるモジュールを作成した。

以下の部品を用いた。
入手元の「秋月」は[秋月電子通商](https://akizukidenshi.com/catalog/)を表す。

|部品|個数|入手元|
|---|:-:|---|
|片面ユニバーサル基板　Ｄタイプ（４７×３６ｍｍ）　ガラスコンポジット|1|秋月 P-08241|
|タクトスイッチ（黒色）|1|秋月 P-03647|
|ＫＸＴＣ９－２０５０使用３軸加速度センサモジュール|1|秋月 K-15232|
|Ｉ２Ｃ接続小型ＬＣＤモジュール（８×２行）ピッチ変換キット|1|秋月 K-06795|
|分割ロングピンソケット（細ピン用）　１×４０　（４０Ｐ）|1|秋月 C-10073|
|圧電スピーカー（圧電サウンダ）（１３ｍｍ）ＰＫＭ１３ＥＰＹＨ４０００－Ａ０|1|秋月 P-04118|
|★2.54★単列長足ピンソケット [PS254S-H8.5] 極数：1x14|2|[aitendo](https://www.aitendo.com/product/7088)|

ブザーは拡張性を考えてつけておいたが、今回のプログラムでは使用しなかった。
自作モジュールとLCDモジュールの接続のため、細ピン用のピンソケットを用いる。
自作モジュールと IchigoJam の接続には長足ピンソケットを用いるが、長すぎないものがよいと考え、aitendo のものを採用した。

配線は、スズメッキ線とハンダを用いて以下のように行った。
左が基板を上から見た図、右が下から見た図である。
青い線が基板裏面での配線を、赤い線が基板表面での配線を表す。

![配線図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c8326b44-9168-29ce-4930-709b3c889f9d.png)

ブザーは、SOUND 端子と GND 端子に繋がるように配線する。
タクトスイッチは、BTN 端子と GND 端子に繋がるように配線する。

LCDモジュールは、以下の端子に接続する。
また、プルアップのジャンパーをショートして有効化する。

|LCDモジュール|IchigoJam|
|:-:|:-:|
|VDD|VCC|
|RESET|VCC|
|SCL|SCL|
|SDA|SDA|
|GND|GND|

加速度センサは、以下の端子に接続する。

|加速度センサ|IchigoJam|
|:-:|:-:|
|+V|VCC|
|NC|-|
|ST|GND|
|EN|IN4|
|X|OUT1|
|Y|OUT2|
|Z|OUT3|
|GND|GND|

以下が完成したモジュールである。

![モジュール 表面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/1a335229-8cbd-dbae-6bba-d7624d0c7e4d.jpeg)
![モジュール 裏面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/2430a9bd-b164-5244-98b3-66d8865fad80.jpeg)

# ソフトウェア

## 仕様

加速度センサの出力を読み取り、そこから角度 (傾き) を計算してI2C経由で液晶画面に表示する。
読み取る角度は、スイッチ (BTN) を短時間押すことで以下から順に切り替えることができる。

* X軸を中心とする回転、Z軸が上を向いているとき 0° (上下矢印アイコンで表示)
* Y軸を中心とする回転、Z軸が上を向いているとき 0° (左右矢印アイコンで表示)
* Z軸を中心とする回転、Y軸が上を向いているとき 0° (丸矢印アイコンで表示)

スイッチを長押し (2秒以上) することで、液晶の表示をオフにしてスリープに入る。
再びスイッチを押すことで、(IchigoJam 側の仕様により) 復帰し、プログラムを再実行できる。

## プログラム

```
10 @ARUN:'ｶｸﾄﾞｹｲ
20 UART0,0:VIDEO0
30 OUT1,-1:OUT2,-1:OUT3,-1:OUT11,1:A=#3E:POKE#700,0,#39,#14,#70,#56,#6C:R=I2CW(A,#700,6):WAIT20:POKE#701,#38,#0C,#01:R=I2CW(A,#700,4)
40 POKE#700,#80,#48,#40,4,14,21,4,4,21,14,4,0,0,10,31,10,0,0,0,0,22,25,1,1,25,22,0:R=I2CW(A,#700,27):M=0:CLS:?CHR$(1):B=BTN():CLT
50 X=ANA(5)-512:Y=ANA(6)-512:Z=ANA(7)-512:IFM=0U=Z:V=YELSEIFM=1U=Z:V=XELSEU=Y:V=X
60 W=ABS(V):P=0:N=181
70 T=(P+N)/2:C=COS(T)/2:S=SIN(T)/2:IFW/2*C>=U/2*SP=TELSEN=T
80 IF P+1<NGOTO70
90 LC2,0:?DEC$(P,3);CHR$(#DF):?DEC$(U,4);DEC$(V,4)
100 POKE#700,#80,#80,#40:R=I2CW(A,#700,3,#900,8):POKE#701,#C0:R=I2CW(A,#700,3,#920,8)
110 IFBAND!BTN()M=(M+1)%3:LC0,0:?CHR$(M+1)
120 IFBTN()AND!BCLT
130 B=BTN():IF!BTN()ORTICK()<120GOTO50
140 POKE#700,0,#08,#39,#52,#64:R=I2CW(A,#700,5):OUT11,0
150 IFBTN()GOTO150
160 SLEEP:GOTO10
```

このプログラムは、[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja) でライセンスする。
このプログラム (改造したものを含む) を公開の場で利用する際は、出典を示していただけると嬉しい。
これは、Qiitaの利用規約に基づくプログラムの利用を禁止するものではない。

## 解説

### 10行目：自動実行指示・タイトル

> ```
> 10 @ARUN:'ｶｸﾄﾞｹｲ
> ```

`FILES` 用のタイトルに加え、起動時に自動実行させる `@ARUN` を置いている。

### 20行目：余計な出力の抑止

> ```
> 20 UART0,0:VIDEO0
> ```

今回は液晶表示を用いるポータブルなガジェットを目指したので、どうせ見ないはずのUARTと画面の出力を無効化する。
検証時に無効化を切りやすいよう、独立した行に配置した。

### 30～40行目：初期化

> ```
> 30 OUT1,-1:OUT2,-1:OUT3,-1:OUT11,1
> ```

加速度センサの出力に接続したポートを入力に、ENに出力したポートを出力に設定し、同時に加速度センサを動作させる (ENにHIGHを入力する)。

> ```
> A=#3E
> POKE#700,0,#39,#14,#70,#56,#6C:R=I2CW(A,#700,6)
> WAIT20
> POKE#701,#38,#0C,#01:R=I2CW(A,#700,4)
> ```

液晶の7bit I2Cアドレスを設定する。
液晶モジュールの取扱説明書に沿って初期設定を行う。

> ```
> 40 POKE#700,#80,#48,#40,4,14,21,4,4,21,14,4,0,0,10,31,10,0,0,0,0,22,25,1,1,25,22,0
> R=I2CW(A,#700,27)
> ```

液晶のCGRAMにデータを書き込み、モードを表すアイコンを表示する準備をする。

> ```
> M=0:CLS:?CHR$(1)
> ```

モードを初期化し、画面をクリアしてモードの表示用の文字を置く。

> ```
> B=BTN():CLT
> ```

ボタン入力 (押されているか、および押されている時間) の情報を初期化する。

### 50行目：加速度センサからのデータ取得

> ```
> 50 X=ANA(5)-512:Y=ANA(6)-512:Z=ANA(7)-512
> ```

加速度センサから各軸のデータを取得する。

> ```
> IFM=0U=Z:V=YELSEIFM=1U=Z:V=XELSEU=Y:V=X
> ```

モードに合わせ、加速度センサのデータを計算用の変数に代入する。

### 60～80行目：角度の計算

> ```
> 60 W=ABS(V):P=0:N=181
> ```

角度を0°～180°で求めるため、縦軸のデータが負の場合は反転させる。
[二分探索](https://qiita.com/drken/items/97e37dd6143e33a64c8c)を初期化する。

> ```
> 70 T=(P+N)/2:C=COS(T)/2:S=SIN(T)/2:IFW/2*C>=U/2*SP=TELSEN=T
> ```

[ベクトルの外積](https://atarimae.biz/archives/23716)の考え方を用いて現在の角度と求めたい角度の大小関係を求め、二分探索を進める。

ベクトルの外積は、2本の3次元ベクトルに対する演算であり、一方のベクトルの方向から他方のベクトルの方向に右ねじを回した時に進む向き、またはその逆の向き (座標系によって異なる) のベクトルを得ることができる。
今回は、2本の2次元ベクトル (U, W) および (C, S) をz成分が0の3次元ベクトル (U, W, 0) および (C, S, 0) と考えて外積を計算し、そのz成分の符号によってもとのベクトルの横軸正方向からの偏角の大小関係を求める。

今回の加速度センサの読みは -512～511、三角関数の値は -256～256 で表されるので、それぞれ2で割ってから計算を行なうことで、オーバーフローを起こす可能性を小さくしている。
(それぞれ2で割った値の掛け算の結果は、最大で -256 × -128 = 32768 となり、IchigoJam の変数で表せる範囲をオーバーしてしまうが、このような極端な入力になる可能性は少ないだろう)

> ```
> 80 IF P+1<NGOTO70
> ```

二分探索が完了していない場合、操作を繰り返す。

### 90～100行目：画面表示

> ```
> 90 LC2,0:?DEC$(P,3);CHR$(#DF):?DEC$(U,4);DEC$(V,4)
> ```

VRAMの1行目に求めた角度と「°」の記号を、2行目に角度を求めるのに用いた横軸方向と縦軸方向の加速度センサの読みを置く。
VRAMへの出力を用いることで、IchigoJam の標準機能による整数から文字列への変換を利用できる。

> ```
> 100 POKE#700,#80,#80,#40:R=I2CW(A,#700,3,#900,8)
> POKE#701,#C0:R=I2CW(A,#700,3,#920,8)
> ```

VRAMのデータを液晶に反映させる。

### 110～130行目：操作受付

> ```
> 110 IFBAND!BTN()M=(M+1)%3:LC0,0:?CHR$(M+1)
> ```

ボタンが離されたとき、モードを切り替え、画面上のアイコン表示を更新する。

> ```
> 120 IFBTN()AND!BCLT
> ```

ボタンが押されたとき、タイマーをクリアし、押されている時間の計測を開始する。

> ```
> 130 B=BTN():IF!BTN()ORTICK()<120GOTO50
> ```

ボタンの状態を変数 `B` に記録する。
ボタンが押されていない、または押されてから2秒経っていない場合は、角度計の動作を続ける。
(2秒連続してボタンが押されている場合は、次のスリープ処理に移る)

### 140～160行目：スリープ突入

> ```
> 140 POKE#700,0,#08,#39,#52,#64:R=I2CW(A,#700,5):OUT11,0
> ```

液晶の画面表示・フォロワー回路・ブースターをオフにする。
加速度センサのENをLOWにし、動作を停止させる。

> ```
> 150 IFBTN()GOTO150
> ```

ボタンが押されている間、待機する。

> ```
> 160 SLEEP:GOTO10
> ```

ボタンが離されたら、スリープに突入する。
スリープ解除後はリセットがかかるはずであるが、念のため最初に戻る処理も入れてある。

# 実行結果

[IchigoKamuy](https://www.ichigokamuy.net/) を用いたテストでは、3モードそれぞれについて傾きによって角度の表示が変わるのを確認でき、スリープの突入・復帰も成功した。

<iframe width="560" height="315" src="https://www.youtube.com/embed/UXrRA7WU5bc?si=UbxS7FrlTf_3CdOW" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

しかし、IchigoDake を用いた動作確認では、うまく動かなかった。
まず、液晶画面に表示が出なかった。
ビデオ出力の抑止を解除して確認すると、角度は計測できているときもあれば、加速度センサの出力がうまく読み取れず (もしくは、加速度センサを有効化できておらず) 読みの値がおかしくなっているとき、そしてI2C通信に失敗しているようでフリーズしているときもあった。
IchigoDake のピンソケットは高さの短い特殊なものであるため、今回作成したモジュールとの接触が悪く、連携がうまくいかなかったものと考えられる。

今回は作りやすさを考えて片面ユニバーサル基板・長足ピンソケットを用いたが、足が揃いやすいよう両面プリント基板・ロープロファイルピンヘッダを用いることで、成功する可能性を上げることができると予想できる。