M5Stack Glass Unitで「戦闘力たったの5か…ゴミめ…」ってやりたいのでとりあえず触ってみるの巻

# はじめに

[M5Stack Japan Creativity Contest 2024](https://www.switch-science.com/pages/m5stack-japan-creativity-contest-2024)に今年も参加したいなと思って、マルツに行ったら、少し前から買うか悩んでいた透明OLEDディスプレイが売っていたので購入。
いやぁ、透明な影響と言ったらスカウター。
スカウターと言ったら「戦闘力たったの5か…ゴミめ…」ですよね！
ちょうどコンテストのネタに困っていたのでそんな小さな夢の実現？に向けて頑張ります！

ちなみに、[GLASS2](https://www.marutsu.co.jp/pc/i/2764445/)もあるようです。
(売り切れていて店頭になかった)
今回はM5Capusuleを使います(が別にほかのでも問題はないと思う)

# コードと注意点

ちょっと情報に注意があったりしたので先に注意点を・・・

## UIFlow 2ではブロックエディタで扱えない
これが誤算でした。
おかげでコードで書いて実行するのを覚えられましたが・・・

UIFlow 1なら利用できるようですが、組み合わせるものによってはUIFlow2しか使えないので、現時点ではPythonやArduino IDEで頑張る(よく知らないけど)の2択かなと思います。

## サイトの情報が間違っているので注意
スイッチサイエンスさんのサイトを見るとi2cのアドレスが0x3Cになっていて、さらに販売元のページも0x3Cになっています。
しかし、[gitにあるCのソース](https://github.com/m5stack/M5Unit-GLASS/blob/4ef5cde4e0ca1cc164521682b12252cfc7e02a74/src/UNIT_GLASS.h#L7)を見ると0x3D。
実際に実機で確認したところ、0x3D。
皆さん注意してね！

## コードについて

全体像は以下の通り
今回はあまり大したことはやってないです。

```python
import os, sys, io
import M5
from M5 import *
import machine
import time

I2C_ADDR = 0x3D
i2C_SDA = 13
i2C_SCL = 15
i2C_FREQ = 400000

display = None

def setup():
  global display

  M5.begin()
  print("M5.begin() を呼び出しました")

  i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(13))

  print('I2Cスキャンを開始します...')
  devices = i2c.scan()

  if len(devices) == 0:
    print('I2Cデバイスが見つかりませんでした')
  else:
    print('I2Cデバイスが見つかりました：', len(devices))
    for device in devices:
        print("デバイスアドレス：", hex(device))

  try:
    display = M5.addDisplay({"unit_glass":{"enabled": True, "pin_sda": i2C_SDA, "pin_scl": i2C_SCL, "i2c_addr": I2C_ADDR, "i2c_freq": i2C_FREQ}}) 
    if not display:
      raise Exception("ディスプレイの初期化に失敗しました")
    print("ディスプレイが正常に初期化されました")  
  except Exception as e:
    print(f"ディスプレイの初期化エラー: {e}")

def loop():
  global display
  try:
    display.setTextSize(1)
    display.setCursor(4, 4)
    display.print("Battle Power\n ")
    display.setTextSize(2)
    display.setCursor(4, 12)
    display.print('     5')

    M5.update()
    time.sleep(1)

  except Exception as e:
    print(f"ループエラー: {e}")

if __name__ == '__main__':
  try:
    setup()
    while True:
      loop()
  except (Exception, KeyboardInterrupt) as e:
    try:
      from utility import print_error_msg
      print_error_msg(e)
    except ImportError:
      print("最新のファームウェアに更新してください")
    print(f"未処理のエラー: {e}")

```

実行結果はこんな感じ!
![P_20240608_223833.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277984/fa15d6d9-4ce8-8e8f-31bd-07097fbb5148.jpeg)




最初、認識全然できなくて困っていました。
知らな過ぎて、調べるの下手すぎるが原因。
という訳で、ポイントと、引っかかったところを書き出します。

### 処理のポイント

- M5.addDisplay()メソッドでGLASSを追加する
    - ここで失敗しても例外にならないので注意
- 描画はM5.addDisplay()メソッドで帰ってきたDisplayクラスのオブジェクトを利用する
    - もしかするとCanvasを作成してできるかもしれないので、試したら記事にまとめます。
- 画面のクリアの処理コストが高そうなので、変更したい箇所だけ更新するのがよさそう
    - 深く触ったわけではないですが、setupにクリア処理入れたときにloopの文字列表示に時間がかかったのでそんな印象
    - loopに画面クリアを入れると結構ちかちかしたのもその要因

### M5.addDisplayのpin_sda、pin_sclに何を設定すればいいのかわからなくてハマる
これはGLASSのD,CがどのPINに接続されているかを調べてそのPINの番号を指定すればOK。
このスクショの場合、GLASSのDがsda、Cがsclになり、ケーブルの色でM5Capsuleのどのポートに接続されているか確認ができる。
なので、今回はD=13、C=15になる。
（黄色の枠に注目)
![glass001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277984/ae4b37cb-aaef-b32c-fb17-1d647bd28a7c.png)




### i2c_addrが違っている問題
ChatGPTの力を借りて、アドレスを確認するコードを用意しました。
以下の部分です。
```python
  i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(13))

  print('I2Cスキャンを開始します...')
  devices = i2c.scan()

  if len(devices) == 0:
    print('I2Cデバイスが見つかりませんでした')
  else:
    print('I2Cデバイスが見つかりました：', len(devices))
    for device in devices:
        print("デバイスアドレス：", hex(device))
```

これで0x3Dと確定しました。
前も何か買ったときに、製造元の情報が間違っていたことがあったので、疑ってかからないとハマりますね・・・

### 出力にハマる

現時点でわかっていること
- setupでの画面クリアはいらない。
    ```
    #これやらなくていい
    #display.clear(0xffffff)
    ```


- 輝度の設定もおそらくいらない
    ```   
    #これやらなくていい
    #display.setBrightness(100)
    ```


    
- setTextSize()で指定できるサイズはピクセルではないので注意
    - 3や4でも結構文字が大きくなる

## その他のポイント
- 販売ページからリンクされている[STM32F030F4P6](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/STM32F030F4P6.PDF)をざっと見たんですが、Standard、Fast mode、Fast mode +の3つのモードがあるようです。違いは分かっていませんが・・・


- 昔セリアで買ったスカウター型のおもちゃのレンズに設置するのがちょっと難しそう
だって、こんな感じなんだもん

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">5はかろうじて読めた！ <a href="https://t.co/V6ERRLJyCo">pic.twitter.com/V6ERRLJyCo</a></p>&mdash; ᴛᴀᴋᴀ@浜松/ᴋʀᴏʜɪɢᴇᴡᴀɢᴍᴀ (@project_dev_tk) <a href="https://twitter.com/project_dev_tk/status/1799434380215189725?ref_src=twsrc%5Etfw">June 8, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

可能なら、おもちゃ側のレンズに穴をあけてネジで固定しようと思ったけどできない。
さて、どうするか・・・

# 最後に
わからないことが多すぎたのも今回ハマった原因だなぁと書きながら実感していました。
グラフィック周りもこれから調べていって、戦闘力を測るようなアニメーションをさせたいと思います。
あと、実装が間に合えば6/29の[Android Bazaar and Conference 2024 Summer](https://japan-android-group.connpass.com/event/301181/)にもっていって浜松支部のブースで展示してるかもしれません。
なので、ABC来てみてね！

# 参考
- [M5Stack用 1.5インチ透明OLEDディスプレイユニット](https://www.switch-science.com/products/8866)
- [m5-docs -Glass Unit- ](https://docs.m5stack.com/en/unit/Glass%20Unit)
- [M5Unit-GLASS](https://github.com/m5stack/M5Unit-GLASS/tree/main)
- [STM32F030F4P6](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/STM32F030F4P6.PDF)
- [Glass Unit](https://uiflow-micropython.readthedocs.io/en/latest/units/glass.html)