アニメーションと連動したM5Stackトグルスイッチ



以前作成した「[Stateパターンでボタンの状態遷移](https://qiita.com/taront/items/af14f778a32a7ec6c8ef)」を修正して、アニメーションと連動して操作可能なM5Stack用のトグルスイッチを作成しました。ボタンの位置や大きさを指定して表示させることができます。
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/264410/c856276c-1b4d-b8ff-e4cb-d3c19609cf26.gif)

動画
https://www.youtube.com/watch?v=hHPt8RRSGxY
GitHub
https://github.com/toritamantaro/m5_switch_panel

### **取り組んだ内容**

* Obserberパターンによるイベント通知
* スイッチのアニメーション
* 画像の明滅方法
* 感想


## **Obserberパターンによるイベント通知**
アニメーションに必要な状態変化を通知する手段として、Observer(Listener)パターンを利用してみました。ソースコードは[こちら](https://github.com/toritamantaro/m5_switch_panel)です。

また、自分の頭の中を整理するために、クラス図も書いてみました。正直なところクラス図の書き方に不慣れなもので適切でない部分もあるかもしれませんが、雰囲気だけでもお伝えしたいということでご了承ください。

![M5_panel_switch_uml_02.png](https://qiita-image-store.s3.amazonaws.com/0/264410/0af66434-33de-8d60-b227-eae2b8f2ad72.png)


## **スイッチのアニメーション**
スイッチをアニメーションをさせる方法については全く見当がつかなかったので、meganetaaanさんの[m5stack-avatar](https://github.com/meganetaaan/m5stack-avatar)を参考にさせて貰いました。
上級者の方のコードを読むことは本当に有益な学習の機会だと思います。大変勉強になりました。この場を借りてお礼申し上げます。


## **画像の明滅方法**
ボタンを点滅させるときにαチャンネルみたいな値を使って徐々に変化させる処理を行いたかったのですが、M5stackの標準機能で透過量を調整する方法を調べきれませんでした。
なので、ちょっと強引ですが次のような関数を作って疑似的に色調を変化させて誤魔化すことにしました。`GetGradationColor()`に2つの色とその比率を0.0~1.0の間で指定すれば、それっぽい色を取得できます。

```c++:ColorPalette.cpp
/*
input
red, green, blue : 0 to 255
*/
uint16_t ColorPalette::RGB2Color(uint8_t red, uint8_t green, uint8_t blue)
{
    // r , b : 0 to 31
    // g : 0 to 63
    uint8_t r = map(red, 0, UINT8_MAX, 0, 31);
    uint8_t g = map(green, 0, UINT8_MAX, 0, 63);
    uint8_t b = map(blue, 0, UINT8_MAX, 0, 31);
    return (r << 11) | (g << 5) | b;
}

/*
output
rgb[0] (red)   : 0 to 255
rgb[1] (green) : 0 to 255
rgb[2] (blue)  : 0 to 255
*/
void ColorPalette::Color2RGB(uint16_t color, uint8_t rgb[])
{
    uint8_t r = color >> 11;         // valid lower 5 bit
    uint8_t g = (color >> 5) & 0x3F; // valid lower 6 bit
    uint8_t b = color & 0x1F;        // valid lower 5 bit

    rgb[0] = map(r, 0, 31, 0, UINT8_MAX);
    rgb[1] = map(g, 0, 63, 0, UINT8_MAX);
    rgb[2] = map(b, 0, 31, 0, UINT8_MAX);
}

uint16_t ColorPalette::GetGradationColor(uint16_t base_color, uint16_t target_color, float_t ratio)
{
    /* ratio : 0.0 to 1.0 */
    ratio = (ratio < 0) ? 0.0 : _min(1.0, ratio);

    uint8_t rgb_base[3];
    uint8_t rgb_targ[3];

    Color2RGB(base_color, rgb_base);
    Color2RGB(target_color, rgb_targ);

    float_t red = (float_t)(rgb_targ[0] - rgb_base[0]) * ratio + (float_t)rgb_base[0];
    float_t green = (float_t)(rgb_targ[1] - rgb_base[1]) * ratio + (float_t)rgb_base[1];
    float_t blue = (float_t)(rgb_targ[2] - rgb_base[2]) * ratio + (float_t)rgb_base[2];

    return RGB2Color((uint8_t)red, (uint8_t)green, (uint8_t)blue);
}

```

この方法で、白vs赤や白vs青などの単純な色調であればそれなりに上手くいくのですが、色の組み合わせによっては変化の途中で意図しない色調が生成されてしまいます。
RGBではなくてHSV色空間などに変換すれば上手くいくのでしょうが、ちょっと根気がないので放っておきます。

## **感想**
せっかくAdvent Calendarにお誘いいただいたので、今までやったことがないことに取り組もうと、張り切っていろんな初挑戦（動画の投稿やクラス図の作成なども含めて・・・）をしてみたのですが、デザインパターンやスマートポインタなど、前提知識として勉強しなけばならないことが膨大すぎてやばかったです。主に週末に作業していたのですが、先月の休みは殆どこれに潰れてしましました。今月は嫁さんのご機嫌取りに注力します。

とはいえ、今回の勉強はM5Stackがあったからこそ大変捗りました。やはり作成したプログラムを手元で実際に動かしながら作るとモチベーションの維持に繋がりますね。たとえ成果物がしょぼくとも。

今度、時間があるときにワンボタンで操作できるスライドバーのようなものも作ってみたいと考えています。来年のAdvent Calendarには間に合わせるつもりです（笑）



## **参考にさせて頂きました**

* [OBSERVERの骸骨](http://www002.upp.so-net.ne.jp/ys_oota/mdp/Observer/index.htm)
* [C++とUMLのクラス図](http://d.hatena.ne.jp/l1o0/20100911/1284213655)
* [draw.io](https://www.draw.io/)（クラス図作成に利用）
* [C++11スマートポインタ入門](https://qiita.com/hmito/items/db3b14917120b285112f
)