2025年3月31日月曜日

M5Stamp Picoを買っちゃった

M5StickC Plus2を弄ってみて、玩具などの改造など色々と使い道ありそうです。そこで、組み込みし易そうな、M5Stamp Pico Mateを購入してみました。このサイズであれば、ゴチゾウからはみ出さずに出来そうな気がしますが、バッテリーや液晶などを考えると難しいかな...

内容物は本体、端子引き出し用のジャンパ端子、シール、六角レンジとなっています。サクッとジャンパ端子を半田付けして使用しみます。

なお、ArduinoIDEはM5StickC Plus2で設定していますので、ライブラリインストールのみで使用できました

とりあえず、初めに行うのはLチカと勝手に決めてGPIOにLEDを接続して試します(普通はオンボードのLEDだとは主マスが、きっと使う機会は少ないのでGPIO経由で試します)。あと、LED 1個じゃつまらないのでナイトライダー風にします


// LED 制御(PWM)

#include <M5unified .h>
#define LED_NUM 6
#define LED_DLY 50
#define MAX_LED 256
#define LOW_LED 64
byte  leds   [LED_NUM] = {26, 18, 19, 21, 22, 25};
byte  pwmchs [LED_NUM] = {1, 2, 3, 4, 5, 6};

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  
  // set pin to OUTPUT & LED off
  for (byte i = 0 ; i < LED_NUM ; i++) {
    pinMode(leds[i], OUTPUT);
    ledcSetup(pwmchs[i], 12000, 8);       //周波数:12KHz、分解能:8ビット
    ledcAttachPin(leds[i], pwmchs[i]);
  }
}

void loop() {
  int y = 0;
  // 左から右
  for (int i = 0; i < LED_NUM*2-1; i++ ){
    if ((i % 2) == 0) {
      ledcWrite(pwmchs[y], MAX_LED);        //  100%(3.3V)
      delay(LED_DLY);
      ledcWrite(pwmchs[y], 0);              //  0%(0V)
    } else {
      ledcWrite(pwmchs[y], LOW_LED);        //  25%(0.9V)
      ledcWrite(pwmchs[y+1], LOW_LED);      //  25%(0.9V)
      delay(LED_DLY);
      ledcWrite(pwmchs[y], 0);              //  0%(0V)
      ledcWrite(pwmchs[y+1], 0);            //  0%(0V)
      y++;
    }
  }
  
  for (int i = LED_NUM*2-3; i >= 0; i-- ){
    if ((i % 2) == 0) {
      ledcWrite(pwmchs[y], MAX_LED);        //  100%(3.3V)
      delay(LED_DLY);
      ledcWrite(pwmchs[y], 0);              //  0%(0V)
    } else {
      ledcWrite(pwmchs[y], LOW_LED);        //  25%(0.9V)
      ledcWrite(pwmchs[y-1], LOW_LED);      //  25%(0.9V)
      delay(LED_DLY);
      ledcWrite(pwmchs[y], 0);              //  0%(0V)
      ledcWrite(pwmchs[y-1], 0);            //  0%(0V)
      y--;
    }
  }
}

なお、残像を残すようにするため、ループ変数 iから変数 yを作成しています(変数 iが偶数の時はLED 1個を最大輝度、基数の時はLED 2個を25%輝度)

書き込みにはStamp ISPを使用しています(ドライバはCH9102_VCP_SER_Windowsを使用)。なお、LED点灯だけであれば5Vが無くても動作するようです。とりあえず、無事に動作しました



PWM制御で作成していますが、本来はGPIOをアンログ出力(dacWrite)を使用すると思いますが、将来的にモーターを制御したいと思いますのでPWM制御にしています

2025年3月27日木曜日

mini MAG LITE(2AA)のLED化

作業机の引き出しを整理していたところ、mini MAG LITE用のLEDモジュールが出てきました。記憶が定かではありませんが、20年位前に友人からジャンクとして貰って、後回しで引き出しに放り込んでいたんだと思います

LEDの横が溶けており修理した後がありますが、よく見ると足が折れています。ま、ダメ元と言うことでいいやと思い、手持ちのLEDと変えてみました


何とかLED交換できましたが、パターンのランドもダメになっており半田で無理やり繋いでいます(LEDを取り外した際の写真は撮り忘れ...ランドが剥がれたので焦ってしまいました)。これで点くのかとドキドキしながら、装着してみたところ無事に点灯しました

やはり電球と比べると明るいです。写真では青っぽく見えますが、白色LEDを使用しています。これでしばらく使用しみます

ま、3Vで点灯するLEDもありますので、電球の代わりに付け替える方法もあるんですがね

2025年3月25日火曜日

デジタルゴチゾウを作ってみよう:ハードウェア編

前回の記事でソフトウェアの準備が出来ましたので、今度はハードウェア面を作成します

最初にヒダカヒロジさんの「デジタルゴチゾウをつくる | MUDAなことをしよう。」サイトを参考にしてか回路を作成します。当初は、サイズが小さい点からMCP4661を単体で購入して作成するつもりでしたが、部品サイトを検索しても見つからなかったため同じモジュールを使用することにしました。なので、ヒダカヒロジさんの紹介している回路と全く同じ構成になりますので、回路詳細は「デジタルゴチゾウをつくる | MUDAなことをしよう。」サイトをご覧ください

上側の6角形にした基板には、M5STICKC Plus2を接続するためのピンヘッダーを搭載しています。下側のピンヘッダはゴチゾウ側の基板、およびスイッチを接続します。配線が切れない様にホットボンドで固定してあります

さて、ケースですが我が家には3Dプリンタはありません。たとえ、合ったとしてもデータが作れないので、ゴチゾウを切った貼ったして加工しました。途中で、タイクーンはてれびくんの付録ゴチゾウだったことを思い出し、違うのを試用すればよかったと後悔することになりました

ゴチゾウ側の基板からの配線になります。こちらも断線防止のためにホットボンドで固定しています。ゴチゾウの下部スイッチは再利用して、中にマイクロスイッチ(uxcell 20pcs 4mm x 2mm SPST 瞬間押しボタン パネルPCB)を付けています。なお、株スイッチは奥まで押し込まれますので、それに合わせてください(スイッチを貼り付けるためにスイッチの出っ張りを切り落としています)


で、組み立てるとこんな感じになります。ホットボンドやレジンで補強しまくっています

ピンヘッダーに合わせて、M5STICKC Plus2を刺すことができます

この状態でガヴベルトに接続してテストを行いました。特に高さがありませので、動作テストとベルトとの干渉をチェックしています(少し余裕があるていどで使用できます)

ここで、我が家のちびちゃんにみつかりました。遊びたいオーラ出しまくりですが、M5STICKC Plus2が不安定なので渡せません。急いで上部を作成することにしました

いらない穴をふさいで、スクリーン用の穴を空けて、次は細かい傷を消そうというところで、ちびちゃんの玩具になりました。とりあえず、不安定では無いのでちびちゃんが遊んでも簡単には壊れないと思います

とりあえず、動作画面を載せておきます




レジェンドライダーを選択した時は、ライダーズクレストが表示されます

強奪されたので、これで完成にしました。気が向いたら細かい傷取りや塗装もしたいと思ってます...

2025年3月24日月曜日

デジタルゴチゾウを作ってみよう:ソフトウェア編

現在、我が家のちびちゃんは仮面ライダーガヴにはまっており、色々なゴチゾウに興味があります。一時期よりは手に入るようになりましたが、それでも手に入らない物もありますよね

そんな中、X(旧Twitter)を眺めていたところヒダカヒロジさんの投稿でデジタルゴチゾウが紹介されていました。早速、リンク先の「デジタルゴチゾウをつくる | MUDAなことをしよう。」を拝見したところ、スケッチ(ソースコードをこういう風に言うらしい)を含む制作過程がすべて紹介されていました。

M5STAMP製品どころかArduinoも使用したことありませんが、ここまで情報があればきっと作ることが出来ると見切り発車で~す

ヒダカヒロジさんは、ヴァレンバスターをターゲットにしていますので横向き使用となっておりますが、我が家ではガヴドライバでの使用を想定しています。そのため、ヒダカヒロジさんのソースコードから下記内容を変更しています

  1. 縦型表示
    • select_gochizou 関数内の画面サイズ、およびsetup 関数内のsetRotation()の設定値を変更
    • 各種描画位置、サイズを変更
  2. ゴチゾウのカテゴリ、名称の日本語表示
    ちびちゃんはまだ読めませんが、今後の勉強もかねて
    • M5Unified.h をインクルードし、setup 関数内でsetTextFontでgfxJapanGothic_8 フォントを設定
      縦型、日本語化に伴い、このサイズを選択しています
  3. カテゴリアイコンをゴチゾウ画像化
    ちびちゃんにはこちらの方が判りますので...
    • 85x85 の画像データを作成し、こちらのサイトからバイナリ化してimg_icon.hを作成しています
  4. ゴチゾウの目を丸型化
    横幅が狭いので変更。ま、こっちの方が好みなんですけど
    • ヒダカヒロジさんの瞳作成を参考にし、縦型変更に伴うサイズ、位置設定を変更してます
    • 瞳を閉じている目、初期状態の目も描画で作成したので、img_gochizou.hは使用してません
    • 白目の部分は黒めにしています(ゴチゾウのイメージカラーで白がいるため)
  5. デバッグモードを追加
    描画動作のデバッグの容易化のため
    • 4項のデバッグのため、ボタンBの設定を拡張スイッチと同様の内容に変更
    • デバッグモード時、I2C アクセスは行わない様になっています
  6. その他
    • 75x75でライダーズクレスト画像を作成し、img_crest.hに設定
    • サポートゴチゾウを増やしています(クレーンの丈さんの調査結果を参考にしました)
    • ブンブンジャー選択時もクレスト画像を表示っするようにしています

完成したソースコードは以下になります:

#include <M5StickCPlus2.h>
#include "M5Unified.h" // 日本語フォントを使用するために追加
#include "MCP4661_asukiaaa.hpp"
#include "img_icon.h"
#include "img_crest.h"
#include "Wire.h"

#define PIN_SW 25 // Extra SW
#define ON  LOW
#define OFF HIGH
uint8_t sw = OFF;
uint8_t prev_sw = OFF;

#define STATE_INIT     0
#define STATE_GOCHIZOU 1
#define STATE_CHANGE   2
#define STATE_FINISH   3
uint8_t state = STATE_INIT;
uint8_t prev_state = STATE_INIT;

#define STATE_FINISH_TO_CHANGE_TIME_MS 10000
unsigned long state_change_point_ms = 0;

// #define debug // I2C 通信オフ

void change_state(uint8_t new_state){
  state = new_state;
  Serial.print(F("State: "));
  switch(state){
  case STATE_INIT:     Serial.println(F("INIT"));     break;
  case STATE_GOCHIZOU: Serial.println(F("GOCHIZOU")); break;
  case STATE_CHANGE:   Serial.println(F("CHANGE"));   break;
  case STATE_FINISH:   Serial.println(F("FINISH"));   break;
  default: ;
  }
}

MCP4661_asukiaaa::Core potentio(&Wire, MCP4661_asukiaaa::DeviceAddress::A0H_A1H_A2H);

#define R_VALUE_MAX 256

int RrA_value = 0;
int RrB_value = 0;

// 257ステップで100kΩなので、1ステップあたり0.389Ω。
#define R_62K  162 // 145 ~ 179
#define R_47K  121 // 114 ~ 132
#define R_39K  102 //  95 ~ 108
#define R_30K   78 //  74 ~  82
#define R_24K   63 //  60 ~  65
#define R_20K   52 //  50 ~  54
#define R_16K   42 //  39 ~  44
#define R_12K   31 //  29 ~  32
#define R_10K   26 //  25 ~  27
#define R_8_2K  21 //  20 ~  22
#define R_6_8K  17 //  17 ~  18
#define R_5_6K  14 //  14 ~  15

void printLogAboutWrite(Stream* serial, uint8_t channel, uint16_t valWrote, int result) {
  serial->print("write " + String(valWrote) + " for wiper" + String(channel) +
                " ");
  serial->println(result == 0 ? "succeeded"
                              : "failed by error " + String(result));
}

#define CATEGORY_GUMMY         0
#define CATEGORY_SNACK         1
#define CATEGORY_CHOCOLATE     2
#define CATEGORY_MARSHMALLOW   3
#define CATEGORY_CANDY         4
#define CATEGORY_RAMUNE        5
#define CATEGORY_POPCORN       6
#define CATEGORY_RICE_CRACKER  7
#define CATEGORY_JELLY         8
#define CATEGORY_MACARON       9
#define CATEGORY_DONUT        10
#define CATEGORY_CARAMEL      11
#define CATEGORY_COOKIE       12
#define CATEGORY_PUDDING      13
#define CATEGORY_CAKE         14
#define CATEGORY_SPECIAL      15
#define CATEGORY_LEGEND_RIDER 16
#define CATEGORY_SUPER_SENTAI 17
#define CATEGORY_SWEETS       18

typedef struct {
  char* name_1; // 全角 8文字 
  char* name_2; // 全角 8文字 
  uint32_t color; // 0x000000Uの形で表現
  uint16_t r_A;
  uint16_t r_B;
} gochizou;

#define C_BLACK       0x000000U
#define C_NAVY        0x000080U
#define C_DARKGREEN   0x008000U
#define C_DARKCYAN    0x008080U
#define C_MAROON      0x800000U
#define C_PURPLE      0x800080U
#define C_OLIVE       0x808000U
#define C_LIGHTGREY   0xC0C0C0U
#define C_DARKGREY    0x808080U
#define C_BLUE        0x0000FFU
#define C_GREEN       0x00FF00U
#define C_CYAN        0x00FFFFU
#define C_RED         0xFF0000U
#define C_MAGENTA     0xFF00FFU
#define C_YELLOW      0xFFFF00U
#define C_WHITE       0xFFFFFFU
#define C_ORANGE      0xFFA500U
#define C_GREENYELLOW 0xADFF2FU
#define C_PINK        0xFFC0CBU
#define C_BROWN       0x964B00U
#define C_GOLD        0xFFD700U
#define C_SILVER      0xC0C0C0U
#define C_SKYBLUE     0x87CEEBU
#define C_VIOLET      0xB42EE2U

gochizou selected_gochizou = {"ポッピンググミ", NULL, C_PURPLE, R_62K, R_62K};

gochizou gochizou_gummy[] = {
  {"ポッピンググミ",   NULL, C_PURPLE,   R_62K, R_62K}, //017
  {"キッキンググミ",   NULL, C_ORANGE,   R_62K, R_47K}, //018
  {"パンチンググミ",   NULL, C_SKYBLUE,  R_62K, R_39K}, //019
  {"フィッシンググミ", NULL, C_CYAN,     R_62K, R_30K}, //020
  {"アメージンググミ", NULL, C_PURPLE,    R_62K, R_24K}, //021
  {"スパーキンググミ", NULL, C_DARKGREY, R_62K, R_20K}  //022
};

gochizou gochizou_snack[] = {
  {"ザクザクチップス", NULL,     C_YELLOW, R_62K, R_16K},  //023
  {"ヒリヒリチップス", NULL,     C_RED,    R_62K, R_8_2K}, //026
  {"ショットスナック", NULL,     C_YELLOW,  R_24K, R_10K},  //073
  {"スパイクスナック", NULL,     C_YELLOW, R_24K, R_8_2K}, //074
  {"ショットスナック", "技:シュート", C_YELLOW,  R_20K, R_47K},  //078
  {"スパイクスナック", "技:ニードル", C_YELLOW, R_20K, R_39K},  //079
  {"ポテトチェーン",   NULL,     C_YELLOW, R_20K, R_16K},  //083
  {"シールドスナック", NULL,     C_YELLOW, R_20K, R_12K},  //084
  {"ポテトチェーン",   "技:ストーム",  C_YELLOW, R_20K, R_10K},  //085
  {"シールドスナック", "技:アタック", C_YELLOW, R_20K, R_8_2K}, //086
};

gochizou gochizou_chocolate[] = {
  {"チョコダン",  NULL,           C_RED,       R_62K, R_6_8K}, //027
  {"チョコドン",  "技:バースト", C_BROWN,      R_62K, R_5_6K}, //028
  {"チョコT",     NULL,           C_DARKGREEN, R_47K, R_47K},  //030
  {"チョコホネ" , NULL,           C_DARKGREEN, R_47K, R_39K},  //031
  {"チョコルド",  NULL,           C_DARKGREY,  R_47K, R_30K},  //032
  {"チョコルド",  "技:アタック", C_DARKGREY,   R_47K, R_24K},  //033
  {"チョコスタ" , NULL,           C_BROWN,     R_20K, R_62K},  //077
  {"チョコホネ",  "技:ウォール", C_DARKGREEN,  R_20K, R_30K},  //080
  {"チョコスタ",  "技:シュート", C_BROWN,      R_20K, R_20K}   //082
};

gochizou gochizou_marshmallow[] = {
  {"フワマロ", NULL, C_CYAN, R_47K, R_20K},  //034
  {"マルマロ", NULL, C_PINK, R_47K, R_16K}   //035
};

gochizou gochizou_candy[] = {
  {"ブルキャン", NULL,               C_MAGENTA, R_47K, R_12K},  //036
  {"グルキャン", NULL,               C_SKYBLUE, R_47K, R_10K},  //037
  {"バクキャン", NULL,               C_YELLOW,  R_47K, R_8_2K}, //038
  {"もふパチ",   NULL,               C_PURPLE,  R_39K, R_30K},  //044
  {"もふパチ",   "技:スパーキング", C_PURPLE,  R_30K, R_16K}   //059
};

gochizou gochizou_ramune[] = {
  {"バブルラムネ",  "技:ショック", C_SKYBLUE, R_47K, R_6_8K}, //039
  {"バブルラムネ",  NULL,           C_SKYBLUE, R_47K, R_5_6K}  //040
};

gochizou gochizou_popcorn[] = {
  {"ポップバーン", NULL,         C_SKYBLUE, R_39K, R_16K}, //047
  {"ポップバーン", "技:バーン", C_SKYBLUE, R_30K, R_8_2K} //062
};

gochizou gochizou_rice_cracker[] = {
  {"ビュンベイ", NULL,             C_RED, R_39K, R_10K}, //049
  {"ビュンベイ", "技:ローリング", C_RED, R_30K, R_5_6K} //064
};

gochizou gochizou_jelly[] = {
  {"ぷるゼリー", NULL,            C_MAGENTA, R_39K, R_8_2K}, //050
  {"ぷるゼリー", "技:ショット",  C_MAGENTA, R_24K, R_62K}   //065
};

gochizou gochizou_macaron[] = {
  {"エレガンマカロン", NULL,           C_PINK, R_39K, R_6_8K}, //051
  {"エレガンマカロン", "技:アタック", C_PINK, R_24K, R_47K},  //066
};

gochizou gochizou_donut[] = {
  {"ドーマル", NULL,           C_SKYBLUE, R_39K, R_5_6K}, //052
  {"ドーマル", "技:ショット", C_SKYBLUE, R_24K, R_39K}   //067
};

gochizou gochizou_caramel[] = {
  {"キャラメルメル", NULL,           C_GOLD, R_30K, R_62K}, //053
  {"キャラメルメル", "技:マジック", C_GOLD, R_24K, R_30K}  //068
};

gochizou gochizou_cookie[] = {
  {"クッキッキー", NULL,           C_BLUE, R_30K, R_47K}, //054
  {"クッキッキー", "技:ラッシュ", C_BLUE, R_24K, R_24K}  //069
};

gochizou gochizou_pudding[] = {
  {"どっプリン", NULL,             C_ORANGE, R_30K, R_30K}, //056
  {"どっプリン", "技:インパクト", C_ORANGE, R_24K, R_16K}  //071
};

gochizou gochizou_cake[] = {
  {"ケーキング", NULL,      C_WHITE, R_5_6K, R_39K}, //151
  {"ブッシュ",  "ドノエル", C_GREEN, R_5_6K, R_30K}  //152
};

gochizou gochizou_special[] = {
  {"スペシャル",   NULL,            C_WHITE, R_16K, R_62K}, //089
  {"おめでとう",   NULL,            C_WHITE, R_16K, R_47K}, //090
  {"クリスマス",   NULL,            C_GREEN, R_16K, R_39K}, //091
  {"ハッピー",     "ニューイヤー",  C_RED,   R_16K, R_30K}, //092
  {"ミュージック", NULL,            C_WHITE, R_16K, R_24K}, //093
  {"おめでとう",   "誕生日Ver.",    C_WHITE, R_16K, R_20K}  //094
};

gochizou gochizou_legend_rider[] = {
  {"クウガ",      NULL,       C_RED,     R_16K, R_16K},  //095
  {"アギト",      NULL,       C_GOLD,    R_16K, R_12K},  //096
  {"竜騎",        NULL,       C_RED,     R_16K, R_10K},  //097
  {"ファイズ",    NULL,       C_RED,     R_16K, R_8_2K}, //098
  {"ブレード",    NULL,       C_BLUE,    R_16K, R_6_8K}, //099
  {"響",          NULL,       C_PURPLE,  R_16K, R_5_6K}, //100
  {"カブト",      NULL,       C_RED,     R_12K, R_62K},  //101
  {"電王",        NULL,       C_RED,     R_12K, R_47K},  //102
  {"キバ",        NULL,       C_RED,     R_12K, R_39K},  //103
  {"ディケード",  NULL,       C_MAGENTA, R_12K, R_30K},  //104
  {"ダブル",      NULL,       C_PURPLE,   R_12K, R_24K}, //105
  {"オーズ",      NULL,       C_GREEN,   R_12K, R_20K},  //106
  {"フォーゼ",    NULL,       C_WHITE,   R_12K, R_16K},  //107
  {"ウィザード",  NULL,       C_RED,     R_12K, R_12K},  //108
  {"鎧武",        NULL,       C_ORANGE,  R_12K, R_10K},  //109
  {"ドライブ",    NULL,       C_RED,     R_12K, R_8_2K}, //110
  {"ゴースト",    NULL,       C_ORANGE,  R_12K, R_6_8K}, //111
  {"エグゼイド",  NULL,       C_PURPLE,  R_12K, R_5_6K}, //112
  {"ビルド",      NULL,       C_RED,     R_10K, R_62K},  //113
  {"ジオウ"  ,    NULL,       C_PURPLE,  R_10K, R_47K},  //114
  {"ゼロワン",    NULL,       C_YELLOW,  R_10K, R_39K},  //115
  {"セイバー",    NULL,       C_RED,     R_10K, R_30K},  //116
  {"リバイ",      "REVICE",   C_PINK,    R_10K, R_24K},  //117
  {"バイス",      "REVICE",   C_PINK,    R_10K, R_20K},  //118
  {"ギーツ",      NULL,       C_WHITE,   R_10K, R_16K},  //119
  {"タイクーン",  "GEATS",    C_GREEN,   R_10K, R_12K},  //120
  {"バッファ",    "GEATS",    C_PURPLE,  R_10K, R_10K},  //121
  {"ナーゴ",      "GEATS",    C_GOLD,    R_10K, R_8_2K}, //122
  {"ガッチャード", NULL,       C_SKYBLUE, R_10K, R_6_8K}, //123
  {"レジェンド",   "GOTCHARD", C_GOLD,    R_10K, R_5_6K}, //124
  {"マジェード",   "GOTCHARD", C_WHITE,   R_8_2K, R_62K}, //125
  {"ジーン",       "GEATS",    C_BLUE,    R_8_2K, R_47K}, //126
  {"ヴァルバラド", "GOTCHARD", C_PURPLE,  R_8_2K, R_39K}, //127
  {"1号",         NULL,       C_GREEN,   R_8_2K, R_30K}, //128
  {"バルカン",     "ZERO-ONE", C_BLUE,    R_8_2K, R_24K}, //129
  {"ブレイズ",     "SABER",    C_BLUE,    R_8_2K, R_20K}, //130
  {"ライブ",       "REVICE",   C_GOLD,    R_8_2K, R_16K}, //131
  {"ガヴ",         NULL,       C_PURPLE,  R_5_6K, R_24K}, //153
  {"Nextライダー", NULL,       C_GREEN,   R_5_6K, R_20K}, //154
  {"Newヒーロー",  NULL,       C_GREEN,   R_5_6K, R_16K}, //155
  {"仮面ライダー", NULL,       C_GREEN,   R_5_6K, R_12K}  //156
};

gochizou gochizou_super_sentai[] = {
  {"戦隊",     NULL,            C_RED, R_8_2K, R_12K},  //132
  {"ヒーロー", NULL,            C_RED, R_8_2K, R_10K},  //133
  {"爆上戦隊", "ブンブンジャー", C_RED, R_8_2K, R_8_2K}  //134
};

gochizou gochizou_sweets[] = {
  {"おやつ", NULL, C_WHITE, R_24K,  R_6_8K}, // 078
  {"おかし", NULL, C_WHITE, R_24K,  R_5_6K}, // 079
  {"??????", NULL, C_WHITE, R_20K,  R_24K},
  {"??????", NULL, C_WHITE, R_8_2K, R_6_8K},
  {"??????", NULL, C_WHITE, R_8_2K, R_5_6K},
  {"??????", NULL, C_WHITE, R_6_8K, R_62K},
  {"??????", NULL, C_WHITE, R_6_8K, R_47K},
  {"??????", NULL, C_WHITE, R_6_8K, R_39K},
  {"??????", NULL, C_WHITE, R_6_8K, R_30K},
  {"??????", NULL, C_WHITE, R_6_8K, R_24K},
  {"??????", NULL, C_WHITE, R_6_8K, R_20K},
  {"??????", NULL, C_WHITE, R_6_8K, R_16K},
  {"??????", NULL, C_WHITE, R_6_8K, R_12K},
  {"??????", NULL, C_WHITE, R_6_8K, R_10K},
  {"??????", NULL, C_WHITE, R_6_8K, R_8_2K},
  {"??????", NULL, C_WHITE, R_6_8K, R_6_8K},
  {"??????", NULL, C_WHITE, R_6_8K, R_5_6K},
  {"??????", NULL, C_WHITE, R_6_8K, R_62K},
  {"??????", NULL, C_WHITE, R_6_8K, R_47K},
  {"??????", NULL, C_WHITE, R_6_8K, R_39K},
  {"??????", NULL, C_WHITE, R_6_8K, R_30K},
  {"??????", NULL, C_WHITE, R_6_8K, R_24K},
  {"??????", NULL, C_WHITE, R_6_8K, R_20K},
  {"??????", NULL, C_WHITE, R_6_8K, R_16K},
  {"??????", NULL, C_WHITE, R_6_8K, R_12K},
  {"??????", NULL, C_WHITE, R_6_8K, R_10K},
  {"??????", NULL, C_WHITE, R_6_8K, R_8_2K},
  {"??????", NULL, C_WHITE, R_6_8K, R_6_8K},
  {"??????", NULL, C_WHITE, R_6_8K, R_5_6K},
  {"??????", NULL, C_WHITE, R_5_6K, R_62K},
  {"??????", NULL, C_WHITE, R_5_6K, R_47K}
};

int8_t  gochizou_index = 0;
uint8_t gochizou_category = CATEGORY_GUMMY;
uint8_t gochizou_category_size = sizeof(gochizou_gummy) / sizeof(gochizou);

void change_gochizou_category(){
  // カテゴリー順を編集しやすいよう、この形にする
  switch(gochizou_category){
  case CATEGORY_GUMMY:
    gochizou_category = CATEGORY_SNACK;
    gochizou_category_size = sizeof(gochizou_snack) / sizeof(gochizou);
    break;
  case CATEGORY_SNACK:
    gochizou_category = CATEGORY_CHOCOLATE;
    gochizou_category_size = sizeof(gochizou_chocolate) / sizeof(gochizou);
    break;
  case CATEGORY_CHOCOLATE:
    gochizou_category = CATEGORY_MARSHMALLOW;
    gochizou_category_size = sizeof(gochizou_marshmallow) / sizeof(gochizou);
    break;
  case CATEGORY_MARSHMALLOW:
    gochizou_category = CATEGORY_CANDY;
    gochizou_category_size = sizeof(gochizou_candy) / sizeof(gochizou);
    break;
  case CATEGORY_CANDY:
    gochizou_category = CATEGORY_RAMUNE;
    gochizou_category_size = sizeof(gochizou_ramune) / sizeof(gochizou);
    break;
  case CATEGORY_RAMUNE:
    gochizou_category = CATEGORY_POPCORN;
    gochizou_category_size = sizeof(gochizou_popcorn) / sizeof(gochizou);
    break;
  case CATEGORY_POPCORN:
    gochizou_category = CATEGORY_RICE_CRACKER;
    gochizou_category_size = sizeof(gochizou_rice_cracker) / sizeof(gochizou);
    break;
  case CATEGORY_RICE_CRACKER:
    gochizou_category = CATEGORY_JELLY;
    gochizou_category_size = sizeof(gochizou_jelly) / sizeof(gochizou);
    break;
  case CATEGORY_JELLY:
    gochizou_category = CATEGORY_MACARON;
    gochizou_category_size = sizeof(gochizou_macaron) / sizeof(gochizou);
    break;
  case CATEGORY_MACARON:
    gochizou_category = CATEGORY_DONUT;
    gochizou_category_size = sizeof(gochizou_donut) / sizeof(gochizou);
    break;
  case CATEGORY_DONUT:
    gochizou_category = CATEGORY_CARAMEL;
    gochizou_category_size = sizeof(gochizou_caramel) / sizeof(gochizou);
    break;
  case CATEGORY_CARAMEL:
    gochizou_category = CATEGORY_COOKIE;
    gochizou_category_size = sizeof(gochizou_cookie) / sizeof(gochizou);
    break;
  case CATEGORY_COOKIE:
    gochizou_category = CATEGORY_PUDDING;
    gochizou_category_size = sizeof(gochizou_pudding) / sizeof(gochizou);
    break;
  case CATEGORY_PUDDING:
    gochizou_category = CATEGORY_CAKE;
    gochizou_category_size = sizeof(gochizou_cake) / sizeof(gochizou);
    break;
  case CATEGORY_CAKE:
    gochizou_category = CATEGORY_SPECIAL;
    gochizou_category_size = sizeof(gochizou_special) / sizeof(gochizou);
    break;
  case CATEGORY_SPECIAL:
    gochizou_category = CATEGORY_LEGEND_RIDER;
    gochizou_category_size = sizeof(gochizou_legend_rider) / sizeof(gochizou);
    break;
  case CATEGORY_LEGEND_RIDER:
    gochizou_category = CATEGORY_SUPER_SENTAI;
    gochizou_category_size = sizeof(gochizou_super_sentai) / sizeof(gochizou);
    break;
  case CATEGORY_SUPER_SENTAI:
    //gochizou_category = CATEGORY_SWEETS;
    //gochizou_category_size = sizeof(gochizou_sweets) / sizeof(gochizou);
    gochizou_category = CATEGORY_GUMMY;
    gochizou_category_size = sizeof(gochizou_gummy) / sizeof(gochizou);
    break;
  /*  
  case CATEGORY_SWEETS:
    gochizou_category = CATEGORY_GUMMY;
    gochizou_category_size = sizeof(gochizou_gummy) / sizeof(gochizou);
    break;
  */
  default:
    ;
  }

  gochizou_index = 0;
}

void select_gochizou(){
  if(gochizou_index >= gochizou_category_size){
    gochizou_index = 0;
  }else if(gochizou_index < 0){
    gochizou_index = gochizou_category_size - 1;
  }

  switch(gochizou_category){
  case CATEGORY_GUMMY:        selected_gochizou = gochizou_gummy[gochizou_index];        break;
  case CATEGORY_SNACK:        selected_gochizou = gochizou_snack[gochizou_index];        break;
  case CATEGORY_CHOCOLATE:    selected_gochizou = gochizou_chocolate[gochizou_index];    break;
  case CATEGORY_MARSHMALLOW:  selected_gochizou = gochizou_marshmallow[gochizou_index];  break;
  case CATEGORY_CANDY:        selected_gochizou = gochizou_candy[gochizou_index];        break;
  case CATEGORY_RAMUNE:       selected_gochizou = gochizou_ramune[gochizou_index];       break;
  case CATEGORY_POPCORN:      selected_gochizou = gochizou_popcorn[gochizou_index];      break;
  case CATEGORY_RICE_CRACKER: selected_gochizou = gochizou_rice_cracker[gochizou_index]; break;
  case CATEGORY_JELLY:        selected_gochizou = gochizou_jelly[gochizou_index];        break;
  case CATEGORY_MACARON:      selected_gochizou = gochizou_macaron[gochizou_index];      break;
  case CATEGORY_DONUT:        selected_gochizou = gochizou_donut[gochizou_index];        break;
  case CATEGORY_CARAMEL:      selected_gochizou = gochizou_caramel[gochizou_index];      break;
  case CATEGORY_COOKIE:       selected_gochizou = gochizou_cookie[gochizou_index];       break;
  case CATEGORY_PUDDING:      selected_gochizou = gochizou_pudding[gochizou_index];      break;
  case CATEGORY_CAKE:         selected_gochizou = gochizou_cake[gochizou_index];         break;
  case CATEGORY_SPECIAL:      selected_gochizou = gochizou_special[gochizou_index];      break;
  case CATEGORY_LEGEND_RIDER: selected_gochizou = gochizou_legend_rider[gochizou_index]; break;
  case CATEGORY_SUPER_SENTAI: selected_gochizou = gochizou_super_sentai[gochizou_index]; break;
  case CATEGORY_SWEETS:       selected_gochizou = gochizou_sweets[gochizou_index];       break;
  default: ;
  }
}

M5Canvas canvas_0(&StickCP2.Display); // スプライト1
M5Canvas canvas_1(&StickCP2.Display); // スプライト2

#define DISP_WIDTH  135
#define DISP_HEIGHT 240
#define SWEETS_ICON_WIDTH  85
#define SWEETS_ICON_HEIGHT 85

#define EYE_X 35
#define EYE_Y 140

void update_disp_gochizou_name(){
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    // ダブルとビルドのみ、上下で帯の色を変える
    switch(gochizou_index){
    case 10: StickCP2.Display.fillRect(0, 0, DISP_WIDTH, 25, C_GREEN); break; // W
    case 18: StickCP2.Display.fillRect(0, 0, DISP_WIDTH, 25, C_BLUE);  break; // Build
    default: StickCP2.Display.fillRect(0, 0, DISP_WIDTH, 25, selected_gochizou.color);
    }
  }else{
    StickCP2.Display.fillRect(0, 0, DISP_WIDTH, 25, selected_gochizou.color);
  }
  StickCP2.Display.fillRect(0, 215, DISP_WIDTH, 25, selected_gochizou.color);

  StickCP2.Display.fillRect(0, 130, DISP_WIDTH, 85, TFT_BLACK);
  if(selected_gochizou.name_2 == NULL){
    StickCP2.Display.setCursor(0,170);
    StickCP2.Display.print(selected_gochizou.name_1);
  }else{
    StickCP2.Display.setCursor(0,150);
    StickCP2.Display.print(selected_gochizou.name_1);
    StickCP2.Display.setCursor(0,185);
    StickCP2.Display.print(selected_gochizou.name_2);
  }
}

void update_disp_gochizou_category(){
  StickCP2.Display.fillRect(0, 25, DISP_WIDTH, 105, C_BLACK);
  switch(gochizou_category){
  case CATEGORY_GUMMY:        StickCP2.Display.drawPng((std::uint8_t*)gummy,        12425, 25, 35); break;
  case CATEGORY_SNACK:        StickCP2.Display.drawPng((std::uint8_t*)snack,        14633, 25, 35); break;
  case CATEGORY_CHOCOLATE:    StickCP2.Display.drawPng((std::uint8_t*)chocolate,    12294, 25, 35); break;
  case CATEGORY_MARSHMALLOW:  StickCP2.Display.drawPng((std::uint8_t*)marshmallow,  10978, 25, 35); break;
  case CATEGORY_CANDY:        StickCP2.Display.drawPng((std::uint8_t*)candy,        11478, 25, 35); break;
  case CATEGORY_RAMUNE:       StickCP2.Display.drawPng((std::uint8_t*)ramune,       14219, 25, 35); break;
  case CATEGORY_POPCORN:      StickCP2.Display.drawPng((std::uint8_t*)popcorn,      13037, 25, 35); break;
  case CATEGORY_RICE_CRACKER: StickCP2.Display.drawPng((std::uint8_t*)rice_cracker, 12937, 25, 35); break;
  case CATEGORY_JELLY:        StickCP2.Display.drawPng((std::uint8_t*)jelly,        15142, 25, 35); break;
  case CATEGORY_MACARON:      StickCP2.Display.drawPng((std::uint8_t*)macaron,      12466, 25, 35); break;
  case CATEGORY_DONUT:        StickCP2.Display.drawPng((std::uint8_t*)donut,        15886, 25, 35); break;
  case CATEGORY_CARAMEL:      StickCP2.Display.drawPng((std::uint8_t*)caramel,      12346, 25, 35); break;
  case CATEGORY_COOKIE:       StickCP2.Display.drawPng((std::uint8_t*)cookie,       12419, 25, 35); break;
  case CATEGORY_PUDDING:      StickCP2.Display.drawPng((std::uint8_t*)pudding,      15329, 25, 35); break;
  case CATEGORY_CAKE:         StickCP2.Display.drawPng((std::uint8_t*)cake,          9209, 25, 35); break;
  case CATEGORY_SPECIAL:      StickCP2.Display.drawPng((std::uint8_t*)special,      12475, 25, 35); break;
  case CATEGORY_LEGEND_RIDER: StickCP2.Display.drawPng((std::uint8_t*)rider,         2467, 25, 35); break;
  case CATEGORY_SUPER_SENTAI: StickCP2.Display.drawPng((std::uint8_t*)sentai,        5287, 25, 35); break;
  //case CATEGORY_SWEETS:
  //  break;
  default: ;
  } 
}


void show_gochizou_face_init(){
  canvas_0.fillSprite(C_WHITE);
  canvas_1.fillSprite(C_WHITE);
  // 右目
  canvas_0.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(EYE_X, EYE_Y, 21, C_SILVER);
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    // ダブルとビルドのみ、左右で目の色を変える
    switch(gochizou_index){
    case 10: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_GREEN); break;
    case 18: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_BLUE);  break;
    default: canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);
    }
  }else{
    canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);;
  }
  // 左目
  canvas_0.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 21, C_SILVER);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 15, selected_gochizou.color);
  // 両ハイライト
  canvas_0.fillCircle(EYE_X+7, EYE_Y-7,  5, C_WHITE);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X-7, EYE_Y-7,  5, C_WHITE);
  canvas_0.pushSprite(0,0);
  
  // 目を閉じる方は準備だけ
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 26, C_WHITE);
  // 左目
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 26, C_WHITE);
}

void show_gochizou_face_change(){
  canvas_0.fillSprite(C_WHITE);
  canvas_1.fillSprite(C_WHITE);
  // 右目
  canvas_0.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(EYE_X, EYE_Y, 21, C_SILVER);
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    // ダブルとビルドのみ、左右で目の色を変える
    switch(gochizou_index){
    case 10: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_GREEN); break;
    case 18: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_BLUE);  break;
    default: canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);
    }
  }else{
    canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);;
  }
  canvas_0.fillCircle(EYE_X, EYE_Y, 6, C_SILVER);
  canvas_0.fillCircle(EYE_X, EYE_Y,  2, C_BLACK);
  // 左目
  canvas_0.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 21, C_SILVER);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 15, selected_gochizou.color);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y,  6, C_SILVER);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y,  2, C_BLACK);
  // 両まぶた
  canvas_0.fillTriangle(EYE_X-24, EYE_Y-26, EYE_X+26, EYE_Y+5, EYE_X+26, EYE_Y-26, C_WHITE);
  canvas_0.fillTriangle(DISP_WIDTH - EYE_X+24, EYE_Y-26, DISP_WIDTH - EYE_X-26, EYE_Y+5, DISP_WIDTH - EYE_X-26, EYE_Y-26, C_WHITE);
  // 両ハイライト
  canvas_0.fillCircle(EYE_X-7, EYE_Y+9,  5, C_WHITE);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X+7, EYE_Y+9,  5, C_WHITE);

 // canvas_0.drawPng((std::uint8_t*)gochizou_init, 548, 0, 0);
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    switch(gochizou_index){
    case 0:  canvas_0.drawPng((std::uint8_t*)kuuga,    2136, 30, 50); break;
    case 1:  canvas_0.drawPng((std::uint8_t*)agito,    1897, 30, 50); break;
    case 2:  canvas_0.drawPng((std::uint8_t*)ryuki,    2100, 30, 50); break;
    case 3:  canvas_0.drawPng((std::uint8_t*)faiz,     1884, 30, 50); break;
    case 4:  canvas_0.drawPng((std::uint8_t*)blade,    1357, 30, 50); break;
    case 5:  canvas_0.drawPng((std::uint8_t*)hibiki,   2128, 30, 50); break;
    case 6:  canvas_0.drawPng((std::uint8_t*)kabuto,   2654, 30, 50); break;
    case 7:  canvas_0.drawPng((std::uint8_t*)deno,     3238, 30, 50); break;
    case 8:  canvas_0.drawPng((std::uint8_t*)kiva,     2509, 30, 50); break;
    case 9:  canvas_0.drawPng((std::uint8_t*)decade,   1612, 30, 50); break;
    case 10: canvas_0.drawPng((std::uint8_t*)w,        1535, 30, 50); break;
    case 11: canvas_0.drawPng((std::uint8_t*)ooo,      4088, 30, 50); break;
    case 12: canvas_0.drawPng((std::uint8_t*)fourze,   4203, 30, 50); break;
    case 13: canvas_0.drawPng((std::uint8_t*)wizard,   4437, 30, 50); break;
    case 14: canvas_0.drawPng((std::uint8_t*)gaim,     2921, 30, 50); break;
    case 15: canvas_0.drawPng((std::uint8_t*)drive,    1850, 30, 50); break;
    case 16: canvas_0.drawPng((std::uint8_t*)ghost,    2158, 30, 50); break;
    case 17: canvas_0.drawPng((std::uint8_t*)exaid,    2450, 30, 50); break;
    case 18: canvas_0.drawPng((std::uint8_t*)build,    1963, 30, 50); break;
    case 19: canvas_0.drawPng((std::uint8_t*)zio,      2045, 30, 50); break;
    case 20: canvas_0.drawPng((std::uint8_t*)zeroone,  2182, 30, 50); break;
    case 21: canvas_0.drawPng((std::uint8_t*)saber,    2461, 30, 50); break;
    case 22: canvas_0.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    case 23: canvas_0.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    case 24: canvas_0.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    /*
    case 25: canvas_0.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 26: canvas_0.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 27: canvas_0.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    */
    case 28: canvas_0.drawPng((std::uint8_t*)gotchard, 2453, 30, 50); break;
    case 29: canvas_0.drawPng((std::uint8_t*)legend,   3035, 30, 50); break;
    case 30: canvas_0.drawPng((std::uint8_t*)majede,   2743, 30, 50); break;
    // case 31: canvas_0.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 32: canvas_0.drawPng((std::uint8_t*)valvarad, 2630, 30, 50); break;
    case 33: canvas_0.drawPng((std::uint8_t*)ichigo,   3302, 30, 50); break;
    /*
    case 34: canvas_0.drawPng((std::uint8_t*)zeroone,  2182, 30, 50); break;
    case 35: canvas_0.drawPng((std::uint8_t*)saber,    2461, 30, 50); break;
    case 36: canvas_0.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    */
    case 37: canvas_0.drawPng((std::uint8_t*)gavv,     2121, 30, 50); break;
    /*
    case 38: canvas_0.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    case 39: canvas_0.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    case 40: canvas_0.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    */
    default: ;
    }
  } else if(gochizou_category == CATEGORY_SUPER_SENTAI){
    switch(gochizou_index){
    //case 0:  canvas_0.drawPng((std::uint8_t*)sentai,    5287, 30, 50); break;
    //case 1:  canvas_0.drawPng((std::uint8_t*)sentai,    5287, 30, 50); break;
    case 2:  canvas_0.drawPng((std::uint8_t*)boonboom,    3597, 30, 50); break;
    default: ;
    }
  }
  canvas_0.pushSprite(0,0);

  // 目を閉じる方は準備だけ
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 26, C_WHITE);
  // 左目
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 26, C_WHITE);
 // canvas_1.drawPng((std::uint8_t*)gochizou_blink, 1070, 0, 0);
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    switch(gochizou_index){
    case 0:  canvas_1.drawPng((std::uint8_t*)kuuga,    2136, 30, 50); break;
    case 1:  canvas_1.drawPng((std::uint8_t*)agito,    1897, 30, 50); break;
    case 2:  canvas_1.drawPng((std::uint8_t*)ryuki,    2100, 30, 50); break;
    case 3:  canvas_1.drawPng((std::uint8_t*)faiz,     1884, 30, 50); break;
    case 4:  canvas_1.drawPng((std::uint8_t*)blade,    1357, 30, 50); break;
    case 5:  canvas_1.drawPng((std::uint8_t*)hibiki,   2128, 30, 50); break;
    case 6:  canvas_1.drawPng((std::uint8_t*)kabuto,   2654, 30, 50); break;
    case 7:  canvas_1.drawPng((std::uint8_t*)deno,     3238, 30, 50); break;
    case 8:  canvas_1.drawPng((std::uint8_t*)kiva,     2509, 30, 50); break;
    case 9:  canvas_1.drawPng((std::uint8_t*)decade,   1612, 30, 50); break;
    case 10: canvas_1.drawPng((std::uint8_t*)w,        1535, 30, 50); break;
    case 11: canvas_1.drawPng((std::uint8_t*)ooo,      4088, 30, 50); break;
    case 12: canvas_1.drawPng((std::uint8_t*)fourze,   4203, 30, 50); break;
    case 13: canvas_1.drawPng((std::uint8_t*)wizard,   4437, 30, 50); break;
    case 14: canvas_1.drawPng((std::uint8_t*)gaim,     2921, 30, 50); break;
    case 15: canvas_1.drawPng((std::uint8_t*)drive,    1850, 30, 50); break;
    case 16: canvas_1.drawPng((std::uint8_t*)ghost,    2158, 30, 50); break;
    case 17: canvas_1.drawPng((std::uint8_t*)exaid,    2450, 30, 50); break;
    case 18: canvas_1.drawPng((std::uint8_t*)build,    1963, 30, 50); break;
    case 19: canvas_1.drawPng((std::uint8_t*)zio,      2045, 30, 50); break;
    case 20: canvas_1.drawPng((std::uint8_t*)zeroone,  2182, 30, 50); break;
    case 21: canvas_1.drawPng((std::uint8_t*)saber,    2461, 30, 50); break;
    case 22: canvas_1.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    case 23: canvas_1.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    case 24: canvas_1.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    /*
    case 25: canvas_1.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 26: canvas_1.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 27: canvas_1.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    */
    case 28: canvas_1.drawPng((std::uint8_t*)gotchard, 2453, 30, 50); break;
    case 29: canvas_1.drawPng((std::uint8_t*)legend,   3035, 30, 50); break;
    case 30: canvas_1.drawPng((std::uint8_t*)majede,   2743, 30, 50); break;
    // case 31: canvas_1.drawPng((std::uint8_t*)geats,    2640, 30, 50); break;
    case 32: canvas_1.drawPng((std::uint8_t*)valvarad, 2630, 30, 50); break;
    case 33: canvas_1.drawPng((std::uint8_t*)ichigo,   3302, 30, 50); break;
    /*
    case 34: canvas_1.drawPng((std::uint8_t*)zeroone,  2182, 30, 50); break;
    case 35: canvas_1.drawPng((std::uint8_t*)saber,    2461, 30, 50); break;
    case 36: canvas_1.drawPng((std::uint8_t*)revice,   2183, 30, 50); break;
    */
    case 37: canvas_1.drawPng((std::uint8_t*)gavv,     2121, 30, 50); break;
    /*
    case 38: canvas_1.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    case 39: canvas_1.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    case 40: canvas_1.drawPng((std::uint8_t*)raider,   2467, 30, 50); break;
    */
    default: ;
    }
  } else if (gochizou_category == CATEGORY_SUPER_SENTAI){
    switch(gochizou_index){
    //case 0:  canvas_1.drawPng((std::uint8_t*)sentai,    5287, 30, 50); break;
    //case 1:  canvas_1.drawPng((std::uint8_t*)sentai,    5287, 30, 50); break;
    case 2:  canvas_1.drawPng((std::uint8_t*)boonboom,    3597, 30, 50); break;
    default: ;
    }
  }
}

void show_gochizou_face_finish(){
  // 天使の輪を描く
  canvas_0.fillSprite(C_WHITE);
  canvas_1.fillSprite(C_WHITE);
  // 右目
  canvas_0.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(EYE_X, EYE_Y, 21, C_SILVER);
  if(gochizou_category == CATEGORY_LEGEND_RIDER){
    // ダブルとビルドのみ、左右で目の色を変える
    switch(gochizou_index){
    case 10: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_GREEN); break;
    case 18: canvas_0.fillCircle(EYE_X, EYE_Y, 15, C_BLUE);  break;
    default: canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);
    }
  }else{
    canvas_0.fillCircle(EYE_X, EYE_Y, 15, selected_gochizou.color);;
  }
  // 左目
  canvas_0.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 21, C_SILVER);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X, EYE_Y, 15, selected_gochizou.color);
  // 両ハイライト
  canvas_0.fillCircle(EYE_X+7, EYE_Y-7,  5, C_WHITE);
  canvas_0.fillCircle(DISP_WIDTH - EYE_X-7, EYE_Y-7,  5, C_WHITE);
  canvas_0.fillEllipse(DISP_WIDTH/2, 70, 50, 30, C_GOLD);
  canvas_0.fillEllipse(DISP_WIDTH/2, 67, 40, 20, C_WHITE);
  canvas_0.pushSprite(0,0);
  
  // 目を閉じる方は準備だけ
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(EYE_X-4, EYE_Y+5, 26, C_WHITE);
  // 左目
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 28, C_BLACK);
  canvas_1.fillCircle(DISP_WIDTH - EYE_X+4, EYE_Y+5, 26, C_WHITE);
  canvas_1.fillEllipse(DISP_WIDTH/2, 70, 50, 30, C_GOLD);
  canvas_1.fillEllipse(DISP_WIDTH/2, 67, 40, 20, C_WHITE);
}

uint8_t blink_state = 0;
unsigned long prev_eye_change_point_ms = 0;

void update_face(unsigned long now_ms){
  // 状態遷移時
  switch(prev_state){
  case STATE_INIT:
    if(state == STATE_GOCHIZOU){
      show_gochizou_face_init();
    }
    break;
  case STATE_GOCHIZOU:
    if(state == STATE_INIT){
      update_disp_gochizou_name();
      update_disp_gochizou_category();
    }else if(state == STATE_CHANGE){
      show_gochizou_face_change();
    }
    break;
  case STATE_CHANGE:
    if(state == STATE_INIT){
      update_disp_gochizou_name();
      update_disp_gochizou_category();
    }else if(state == STATE_FINISH){
      show_gochizou_face_change();
    }
    break;
  case STATE_FINISH:
    if(state == STATE_INIT){
      update_disp_gochizou_name();
      update_disp_gochizou_category();
    }else if(state == STATE_CHANGE){
      show_gochizou_face_finish();
    }
    break;
  default:
    ;
  }

  // 時間経過時
  if(state == STATE_GOCHIZOU || state == STATE_CHANGE){
    // まばたき動作
    switch(blink_state){
    case 0:
      if(now_ms - prev_eye_change_point_ms >= 3000){
        canvas_1.pushSprite(0,0); // 目を閉じる
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 1:
      if(now_ms - prev_eye_change_point_ms >= 100){
        canvas_0.pushSprite(0,0); // 目を開く
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 2:
      if(now_ms - prev_eye_change_point_ms >= 5000){
        canvas_1.pushSprite(0,0); // 目を閉じる
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 3:
      if(now_ms - prev_eye_change_point_ms >= 100){
        canvas_0.pushSprite(0,0); // 目を開く
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 4:
      if(now_ms - prev_eye_change_point_ms >= 7000){
        canvas_1.pushSprite(0,0); // 目を閉じる
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 5:
      if(now_ms - prev_eye_change_point_ms >= 100){
        canvas_0.pushSprite(0,0); // 目を開く
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 6:
      if(now_ms - prev_eye_change_point_ms >= 100){
        canvas_1.pushSprite(0,0); // 目を閉じる
        blink_state++;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    case 7:
      if(now_ms - prev_eye_change_point_ms >= 100){
        canvas_0.pushSprite(0,0); // 目を開く
        blink_state = 0;
        prev_eye_change_point_ms = now_ms;
      }
      break;
    default:
      ;
    }
  }
}

void setup(void)
{
  auto cfg = M5.config();
  StickCP2.begin(cfg);

  Wire.end();
  Wire.begin(0, 26); //G0. G26 I2C port

  Serial.begin(115200);

  // 省電力化のため、CPU周波数を少し落とす場合に有効化。最大240MHz。
  setCpuFrequencyMhz(160);

  pinMode(PIN_SW, INPUT_PULLUP);  // 入力として使用する
  gpio_pulldown_dis(GPIO_NUM_36); // 使用しないのでフローティング入力設定
  gpio_pullup_dis(GPIO_NUM_36);   // 使用しないのでフローティング入力設定

  StickCP2.Display.setBrightness(5);
  StickCP2.Display.setRotation(2);  // 画面向き
  StickCP2.Display.setTextSize(2);  // フォントサイズ
  StickCP2.Display.setTextFont(&fonts::lgfxJapanGothic_8);  // フォント
  update_disp_gochizou_name();
  update_disp_gochizou_category();

  // ゴチゾウの表情用スプライトの準備
  canvas_0.createSprite(DISP_WIDTH, DISP_HEIGHT);
  canvas_1.createSprite(DISP_WIDTH, DISP_HEIGHT);
}

void loop(void)
{
  StickCP2.update();
  sw = digitalRead(PIN_SW);

  unsigned long now_ms = millis();

  if(prev_sw == OFF && sw == ON){
    switch(state){
    case STATE_INIT:
      StickCP2.Speaker.tone(5000, 50);
      change_gochizou_category();
      select_gochizou();
      update_disp_gochizou_category();
      update_disp_gochizou_name();
      break;
    case STATE_GOCHIZOU:
      change_state(STATE_CHANGE);
      break;
    case STATE_CHANGE:
      change_state(STATE_FINISH);
      state_change_point_ms = now_ms;
      break;
    case STATE_FINISH:
      break;
    default:
      ;
    }
  }

  if(StickCP2.BtnA.wasClicked()){
    StickCP2.Speaker.tone(5000, 50);
    switch(state){
    case STATE_INIT:
      gochizou_index++;
      select_gochizou();
      update_disp_gochizou_name();
      break;
    case STATE_GOCHIZOU:
      break;
    case STATE_CHANGE:
    case STATE_FINISH:
    default:
      ;
    }
  }

  if(StickCP2.BtnB.wasClicked()){
    switch(state){
    case STATE_INIT:
      StickCP2.Speaker.tone(5000, 50);
      change_gochizou_category();
      select_gochizou();
      update_disp_gochizou_category();
      update_disp_gochizou_name();
      break;
    case STATE_GOCHIZOU:
      change_state(STATE_CHANGE);
      break;
    case STATE_CHANGE:
      change_state(STATE_FINISH);
      state_change_point_ms = now_ms;
      break;
    case STATE_FINISH:
      break;
    default:
      ;
    }
  }


  if(StickCP2.BtnA.wasHold()){
    switch(state){
    case STATE_INIT:
      // 抵抗値をセットする
      int result_a, result_b;

      #ifndef debug
        result_a = potentio.writeWiperRAM(0, selected_gochizou.r_A);
        printLogAboutWrite(&Serial, 0, selected_gochizou.r_A, result_a);
        result_b = potentio.writeWiperRAM(1, selected_gochizou.r_B);
        printLogAboutWrite(&Serial, 1, selected_gochizou.r_B, result_b);
      #else
        result_a = 0;
        result_b = 0;
      #endif

      if(result_a == 0 && result_b == 0){
        StickCP2.Speaker.tone(4000, 50);
        delay(50);
        StickCP2.Speaker.tone(6000, 50);
        change_state(STATE_GOCHIZOU);
      }
      break;
    case STATE_GOCHIZOU:
    case STATE_CHANGE:
    case STATE_FINISH:
      change_state(STATE_INIT);
      break;
    default:
      ;
    }
  }

  // 時間経過処理
  if(state == STATE_FINISH && now_ms - state_change_point_ms >= STATE_FINISH_TO_CHANGE_TIME_MS){
    change_state(STATE_CHANGE);
  }

  update_face(now_ms);

  prev_sw = sw;
  prev_state = state;

  delay(10);
}

完成したスケッチをArduino IDEでコンパイルと検証を行って見たいと思います。その前に準備として、MCP4661_asukiaaaをArduino IDEにインストールしました(関連ライブラリとして、wire_asukiaaa 1.1.13もインストールされました)

コンパイルしたところ、エラーが発生しました

IIn file included from c:\Users\********\Documents\Arduino\libraries\wire_asukiaaa\src/wire_asukiaaa.h:3,
                 from c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:3,
                 from C:\Tools\M5StickCPlus2\gochizou\gochizou.ino:3:
c:\Users\********\Documents\Arduino\libraries\wire_asukiaaa\src/wire_asukiaaa.hpp:8:35: error: 'TwoWire' was not declared in this scope
 typedef PeripheralHandlerTemplate<TwoWire> PeripheralHandler;
                                   ^~~~~~~
c:\Users\********\Documents\Arduino\libraries\wire_asukiaaa\src/wire_asukiaaa.hpp:8:35: note: suggested alternative: 'TwoWire_h'
 typedef PeripheralHandlerTemplate<TwoWire> PeripheralHandler;
                                   ^~~~~~~
                                   TwoWire_h
c:\Users\********\Documents\Arduino\libraries\wire_asukiaaa\src/wire_asukiaaa.hpp:8:42: error: template argument 1 is invalid
 typedef PeripheralHandlerTemplate<TwoWire> PeripheralHandler;
                                          ^
In file included from C:\Tools\M5StickCPlus2\gochizou\gochizou.ino:3:
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:131:3: error: 'TwoWire' does not name a type; did you mean 'TwoWire_h'?
   TwoWire* wire;
   ^~~~~~~
   TwoWire_h
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:133:15: error: expected ')' before '*' token
   Core(TwoWire* wire, uint8_t address) : wire(wire), address(address) {}
       ~       ^
               )
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp: In member function 'int MCP4661_asukiaaa::Core::writeToRegister(uint8_t, uint16_t, uint8_t)':
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:189:5: error: 'wire' was not declared in this scope
     wire->beginTransmission(address);
     ^~~~
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:189:5: note: suggested alternative: 'Core'
     wire->beginTransmission(address);
     ^~~~
     Core
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp: In member function 'int MCP4661_asukiaaa::Core::readFromRegister(uint8_t, uint16_t*, uint8_t)':
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:199:9: error: 'wire' was not declared in this scope
         wire, address, buildFirstByte(registerAddress, command, 0), bytes, 2);
         ^~~~
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:199:9: note: suggested alternative: 'Core'
         wire, address, buildFirstByte(registerAddress, command, 0), bytes, 2);
         ^~~~
         Core
C:\Tools\M5StickCPlus2\gochizou\gochizou.ino: At global scope:
C:\Tools\M5StickCPlus2\gochizou\gochizou.ino:38:84: error: no matching function for call to 'MCP4661_asukiaaa::Core::Core(TwoWire*, const uint8_t&)'
 MCP4661_asukiaaa::Core potentio(&Wire, MCP4661_asukiaaa::DeviceAddress::A0H_A1H_A2H);
                                                                                    ^
In file included from C:\Tools\M5StickCPlus2\gochizou\gochizou.ino:3:
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:129:7: note: candidate: 'constexpr MCP4661_asukiaaa::Core::Core(const MCP4661_asukiaaa::Core&)'
 class Core {
       ^~~~
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:129:7: note:   candidate expects 1 argument, 2 provided
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:129:7: note: candidate: 'constexpr MCP4661_asukiaaa::Core::Core(MCP4661_asukiaaa::Core&&)'
c:\Users\********\Documents\Arduino\libraries\MCP4661_asukiaaa\src/MCP4661_asukiaaa.hpp:129:7: note:   candidate expects 1 argument, 2 provided

exit status 1

Compilation error: no matching function for call to 'MCP4661_asukiaaa::Core::Core(TwoWire*, const uint8_t&)'
エラーメッセージから検索しましたが、さっぱり判りません。そもそもToWireってM5STAMPの標準関数では無いの?!っという疑問しかわきませんでした。記述内容をチェックしても原因が判りません

問題の切り分けのためにMCP4661_asukiaaa.hppにM5StickCPlus2.hのインクルード文を追加したところ同じエラーが発生、さらにwire_asukiaaa.hppにも追加してもエラーが発生しました。
この現象から、ToWireが定義されているwire.hが参照されていないのではなりました。

慣れている人であれば対策を知っているかと思いますが、初めて使用した私からするとこれまたサッパリです。なので、全てのヘッダファイルをローカルにコピーして、インクルード文をローカル参照にすることでコンパイルが正常に終了しました。簡単に書いていますが、一晩悩みましたよ

2025年3月18日火曜日

M5StickC Plus2の開発環境準備

先日、あるものを作りたくなり、その準備のためにM5StickC Plus2を購入しました


この製品は、M5Stackテクノロジー社のマイコンボードで、CPU:ESP32-PICO-V3-02、フラッシュメモリ:8MB、さらにバッテリーを搭載ています

これの開発環境としてはいくつかありますが、今回は手軽そうなArduinoIDEを使用することにしました

最初にArduinoIDEをダウンロードします。ArduinoIDEのサイトに移動して、Download Optionから使用しているOSにあったファイルをダウンロードします。今回は「Windows win 10 and newer, 64bit」を選択しました


ダウンロードは、寄付または無償でダウンロードすることが出来ます。無償で行う場合は、「JUST DOWNLOAD」をクリックします

ダウンロードが完了したらインストーラを起動し、指示に従ってインストールします

次にArduinoIDEをM5StickC Plus2に合わせて設定します

ファイルメニューの基本設定を開きます(最初は英語設定なので、File → Preferences)


下記設定を行います

  • エディタ言語(Language):日本語
  • 配色テーマ(Theme):Dark(お好みで)
  • 追加のボードマネージョーのURL(Additional boards Maneger URLs)に下記サイトを追加
    https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
ボードマネージャーを開き、M5Stackをインストールします

ボードマネージャーのインストールが完了すると、ターゲットボードの設定にM5StickC Plus2が表示されます


M5StickC Plus2に必要な下記ライブラリをライブラリマネージャーでインストールします
  • M5StickCPlus2
  • M5Unified


これで、ArduinoIDEの設定は完了になります

最後にM5StickC Plus2に書き込むためのUSBドライバをインストールします。インストーラーは、M5Stackのサイトからリンクされています


M5StickC Plus2の場合、「FTDI Drivers」になります。リンク先サイトから赤丸箇所をクリックするとダウンロードできます

ドライバのインストールを行えば、準備完了になります

2025年3月14日金曜日

iOS 18.3.2リリース

iOS 18.3.2がリリースされました

対象デバイスは、iPhone XS以降, iPad(7代目), iPad mini(5代目以降), iPad Air(3代目), iPad Pro(13インチ、12.9インチ 3代目以降、11インチ 初代以降)になります

アップデート内容は、以下の様になっています

このアップデートには重要なバグ修正とセキュリティアップデートが含まれ、一部のストリーミングコンテンツを再生できなくなることがある問題に対応しています。

Apple セキュリティアップデートを確認すると以下のセキュリティアップデート内容が記載されています

WebKit

WebKit Bugzilla: 285858 

CVE-2025-24201: Apple

なおアップデートファイルの大きさは、iOS 18.3.1をインストールしているiPhone 11 proで433.6MB、iPhone 11で418.7MBになります


スマホ・携帯ランキング