M5StickCPlus2で美咲フォント8x8NeoPixel表示

M5StickCPlus2で美咲フォント8x8NeoPixel表示

これ

https://x.com/tatmos/status/1761395019980951697?s=20

を作った時のメモ

# 苦労した点
### 意図したように動かない時がある
色によってはstrip.getPixelColorがうまく取得できない時があるみたいで、
数行しか表示できないことがあった。（未解決）
setBrightnessで 50だと数行しかでなくて
setBrightnessで 150とかだと数行正しくて、途中から色が変わってしまったりとか
NeoPixelの長さが長いと途中で信号がうまくとれなくなるのかな・・・


### SPIがわからなかった
NeoPixelの制御にSPIというライブラリを使った例があったけど、自分にはよくわからないものだった
・・・もしかして、SPI使って制御とかしないと同期がとれないとか意図したように動かないのとかあるのかもしれない。　あるいは更新頻度とかもっと効率よくできるのかもしれない。

# 参考にしたもの

NeoPixelと文字表示検索でヒットした
https://gist.github.com/Tamakichi/379f79f0861eb15f21bc90080d5c580a

ほとんどこれベースに、
ChatGPTにSPI使わないコードをお願いして一緒に作った感じ

# コード

``` GamingBrazenBlaze.ino.java
#include <Adafruit_NeoPixel.h>
#include <misakiUTF16.h>
#include <M5StickCPlus2.h>

// Neopixelの設定
#define NUMPIXELS 64  // Neopixel ピクセル数(LED数)
#define PIN       26  // Neopixel 制御用ピン番号

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
    M5.begin(); // M5StickC Plusの初期化
  M5.Lcd.fillScreen(BLACK); // 画面を黒色でクリア
  Serial.begin(9600);

  strip.begin();          // Neopixel ストリップを初期化
  strip.show();           // 全ての LED をオフにする（初期化時）
  strip.setBrightness(255); // 明るさを設定 (適宜調整してください)
  // 他の必要な初期化処理があればここに追加
}

void loop() {
  NeoMsg("ブレイゼンブレイズ　Brazen Blaze ", 0, 16, 0, 25);
  NeoMsg("ブレイゼンブレイズ　Brazen Blaze  ", 0, 8, 8, 25);
  NeoMsg("ブレイゼンブレイズ　Brazen Blaze   ", 8, 4, 4, 25);
  //delay(1000); // 次のメッセージ表示までの間隔
}

// Neopixelの表示クリア
void NeoCLS(bool flgUpdate = true) {
  for (int i = 0; i < NUMPIXELS; i++) {
    strip.setPixelColor(i, 0); // 全てのピクセルをオフにする
  }
  if (flgUpdate) strip.show();
}

// 指定したピクセルの色を設定
void NeoSetRGB(uint8_t no, uint8_t R, uint8_t G, uint8_t B, bool flgUpdate = false) {
  if (no < NUMPIXELS) {
    strip.setPixelColor(no, strip.Color(R, G, B)); // RGB順に設定
  } 
  if (flgUpdate) strip.show();
}

// ドットマトリックス 左スクロール
void NeoScroll(bool flgUpdate = false) {
  for (int i = 0; i < 8; i++) { // 8行すべてに対して実行
    for (int j = 0; j < 7; j++) { // 各行について7ピクセル分をシフト
      uint32_t color = strip.getPixelColor(i * 8 + j + 1);
      strip.setPixelColor(i * 8 + j, color);
    }
    strip.setPixelColor(i * 8 + 7, 0); // 行の最後をクリア
  }
  if (flgUpdate) strip.show();

  // LCDにNeoPixelの色を表示
  for (int i = 0; i < NUMPIXELS; i++) {
    // NeoPixelから色を取得
    uint32_t color = strip.getPixelColor(i);
    // RGB成分に分解
    uint8_t r = (uint8_t)(color >> 16),
            g = (uint8_t)(color >> 8),
            b = (uint8_t)color;

    // LCD上の対応する位置に点を描画
    // 注: ここではNeoPixelとLCDのマッピングやスケールを適宜調整してください
    int x = (i % 8) * 20;  // 例: x位置を20ピクセルごとにスケールアップ
    int y = (i / 8) * 20;  // 例: y位置を20ピクセルごとにスケールアップ
    M5.Lcd.fillCircle(x, y, 10, M5.Lcd.color565(r, g, b));  // 半径10の円を描画
  }
}


// 8x8ドットマトリックス 指定座標ピクセル番号変換
inline uint8_t XYtoNo(uint8_t x, uint8_t y) {
  return y * 8 + x; // マトリックスの座標をピクセル番号に変換
}

// 1文字左スクロール挿入
void NeoScrollInChar(uint8_t *fnt, uint8_t R, uint8_t G, uint8_t B, uint16_t tm) {
  for (int i = 0; i < 8; i++) {
    NeoScroll(false); // 左に1ドットスクロール

    // フォントパターン1列分をセット
    int x = 7;
    // for(int x = 0;x< 8; x++)
    {

      for (int j = 0; j < 8; j++) {
        if (fnt[j] & (0x80 >> i)) {
          NeoSetRGB(XYtoNo(x, j), R, G, B, false);
        } else {
          NeoSetRGB(XYtoNo(x, j), 0, 0, 0, false);
        }
      }
    }
    strip.show();
    delay(tm); // 指定された時間だけ待つ
  }
}

// メッセージの表示
void NeoMsg(char* msg, uint8_t R, uint8_t G, uint8_t B, uint16_t tm) {
  uint8_t fnt[8]; // フォントデータを格納するバッファ

  NeoCLS(false); // ディスプレイをクリア
  char* str = msg;
  while (*str) {
    if (!(str = getFontData(fnt, str))) {
      Serial.println("Error");
      break;
    }
    //NeoScrollInChar(fnt, R, G, B, tm);
    NeoScrollInChar(fnt, random(255), random(255), random(255), tm);
  }
  strip.show();
}
```


# 32x8バージョン

https://x.com/tatmos/status/1761480626543419536?s=20

- あんまり明るいとM5StickPlus2がフリーズしてしまうようなので、控えめの明るさに設定
- NeoPixelの配置がヘビ型

https://gist.github.com/tatmos/765add4633588ce90a71dabc0fdb11c5