スクラッチ&スクラップ

簡単なプログラミングや電子工作など。しょうもない工作の記録。

Arduino と NeoPixel で作る ミニ電光掲示板

f:id:macrochelys99:20180209223332j:plain:w600

手のひらサイズの電光掲示板

Arduino + NeoPixelシールドで作るミニ電光掲示板
よく見たら「&」が鏡文字になってた....。 まぁいいや。

NeoPixel 楽しいです

NeoPixelはコントローラの入ったフルカラーLEDです。信号線は数珠つなぎにすれば良いので使用するマイコンのポートは1本のみ。一直線に連なったテープ状のもの、マトリックス状に並べたディスプレーのようなもの、リング状のもあります。もちろん1個ずつの単品も。今回使う部品はマイコンボード Arduino UNO とそれに簡単に接続できる NeoPixelシールド。40個の NeoPixel が 5×8のマトリックスに並んでいます。

改良の余地

PCのシリアルコンソールから入力した文字列をリピート表示できたら、ちょっとは使い道ありそうでしょうか。 アルファベット大文字小文字・数字・記号 だけならできるかも。

スケッチ

#include <math.h>
#include <Adafruit_NeoPixel.h>

#define PIN_PIXELS    6
#define NUM_PIXELS    40
#define NUM_PIXELS_H  5
#define NUM_PIXELS_W  8

#define PIXELS_BRIGHT 16    // NEOPIXELを点灯させる明るさ 0~255

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, PIN_PIXELS, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
}

void loop() {
  show_message();
  show_count(0, 99);
}

void show_message() {
  int sh = 5;   // 表示内容の高さピクセル
  int sw = 62;  // 表示内容の幅ピクセル
  int dh = NUM_PIXELS_H;   // ディスプレーの高さピクセル
  int dw = NUM_PIXELS_W;   // ディスプレーの幅ピクセル
  int r, g, b;  // red green blue
  boolean str[sh][sw] = {
    {0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,1,0,0},
    {0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0},
    {0,0,1,1,0,0,0,0,0,1,0,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,1,1,0,1,0,1,0,1,0,0,0,0,1,0,1,0,0},
    {0,1,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0},
    {0,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0}
  };

  for (int c = 0; c < sw + dw; c++) {  // スクロール
    for (int h = 0; h < dh; h++) {  // 上の行から
      for (int w = 0; w < dw; w++) {  // 左の列から
        if ((c + w < dw) || (c + w >= dw + sw)) { // 表示範囲外
          continue;
        } else if (str[h][w - dw +  c] == 1) { // 表示位置の データが 1 のとき
          r = PIXELS_BRIGHT; g = PIXELS_BRIGHT; b = PIXELS_BRIGHT;
        } else {  // 表示位置のデータが 0 のとき
          r = 0; g = 0; b = 0;
        }
        pixels.setPixelColor(h * dw + w, pixels.Color(r, g, b));
      }
    }
    pixels.show();
    delay(100);  // スクロール速度の設定
  }
}

void show_count(int from, int to) {
  int chr_h = 5;
  int chr_w = 3;

 boolean chr[10][chr_h][chr_w] = {  // 5x3で数字を描く
    // 0
    0, 1, 0,
    1, 0, 1,
    1, 0, 1,
    1, 0, 1,
    0, 1, 0,

    // 1
    0, 1, 0,
    1, 1, 0,
    0, 1, 0,
    0, 1, 0,
    0, 1, 0,

    // 2
    1, 1, 0,
    0, 0, 1,
    0, 1, 1,
    1, 0, 0,
    1, 1, 1,

    // 3
    1, 1, 1,
    0, 0, 1,
    1, 1, 1,
    0, 0, 1,
    1, 1, 1,

    // 4
    1, 0, 1,
    1, 0, 1,
    1, 1, 1,
    0, 0, 1,
    0, 0, 1,

    // 5
    1, 1, 1,
    1, 0, 0,
    1, 1, 1,
    0, 0, 1,
    1, 1, 1,

    // 6
    1, 0, 0,
    1, 0, 0,
    1, 1, 1,
    1, 0, 1,
    1, 1, 1,

    // 7
    1, 1, 1,
    0, 0, 1,
    0, 0, 1,
    0, 1, 0,
    0, 1, 0,

    // 8
    1, 1, 1,
    1, 0, 1,
    1, 1, 1,
    1, 0, 1,
    1, 1, 1,

    // 9
    1, 1, 1,
    1, 0, 1,
    1, 1, 1,
    0, 0, 1,
    1, 1, 1,
  };

  for (int c = from; c <= to; c++) {  // c: count
    pixels_clear();
    int pos;
    int rgbhsv[] = {c * 5, 255, PIXELS_BRIGHT};
    hsv_to_rgb(rgbhsv);
    if (c < 10) { // 1桁のみ表示
      pos = 3;
      for (int h = 0; h < chr_h; h++) {
        for (int w = 0; w < chr_w; w++) {
          if (chr[c][h][w] == 1) {
            pixels.setPixelColor(NUM_PIXELS_W * h + w + pos, pixels.Color(rgbhsv[0], rgbhsv[1], rgbhsv[2]));
          }
        }
      }
    } else { // 2桁以上の表示
      pos = 1; // 10の位
      for (int h = 0; h < chr_h; h++) {
        for (int w = 0; w < chr_w; w++) {
          if (chr[c / 10][h][w] == 1) {
            pixels.setPixelColor(NUM_PIXELS_W * h + w + pos, pixels.Color(rgbhsv[0], rgbhsv[1], rgbhsv[2]));
          }
        }
      }
      pos = 5; // 10の位
      for (int h = 0; h < chr_h; h++) {
        for (int w = 0; w < chr_w; w++) {
          if (chr[c % 10][h][w] == 1) {
            pixels.setPixelColor(NUM_PIXELS_W * h + w + pos, pixels.Color(rgbhsv[0], rgbhsv[1], rgbhsv[2]));
          }
        }
      }
    }
    pixels.show();
    delay(200);
  }
}

void pixels_clear() {  // 全ピクセル消灯
  for (int n = 0; n < NUM_PIXELS; n++) {
    pixels.setPixelColor(n, pixels.Color(0, 0, 0));
  }
}

/*----------------------------------------
    COLOR FUNCTIONS
  ----------------------------------------*/

void rgb_to_hsv(int rgbhsv[3]) {  // RGB を HSV に変換
  float r = rgbhsv[0], g = rgbhsv[1], b = rgbhsv[2];
  float h, s, v;
  float ma = max(r, g);
  ma = max(ma, b);
  float mi = min(r, g);
  mi = min(mi, b);

  if (r == g && r == b) {
    h = 0;
  } else if (r >= g && r >= b) {
    h = 60.0 * (g - b) / (ma - mi);
  } else if (g >= r && g >= b) {
    h = 60.0 * (b - r) / (ma - mi) + 120.0;
  } else if (b >= r && b >= g) {
    h = 60.0 * (r - g) / (ma - mi) + 240.0;
  }

  if (h < 0) h = h + 360.0;
  if (h >= 360) h = h - 360.0;

  s = (ma - mi) / ma * 255.0;
  v = ma;

  rgbhsv[0] = (int)(h + 0.5);
  rgbhsv[1] = (int)(s + 0.5);
  rgbhsv[2] = (int)(v + 0.5);
}

void hsv_to_rgb(int rgbhsv[3]) {  // HSV を RGB に変換
  float h = (float)rgbhsv[0], s = (float)rgbhsv[1], v = (float)rgbhsv[2];
  float ma = v;
  float mi = ma - (s / 255.0 * ma);

  float r, g, b;
  h = fmod(h, 360.0);
  switch ((int)h / 60) {
    case 0:
      r = ma;
      g = (h / 60.0) * (ma - mi) + mi;
      b = mi;
      break;
    case 1:
      r = ((120.0 - h) / 60.0) * (ma - mi) + mi;
      g = ma;
      b = mi;
      break;
    case 2:
      r = mi;
      g = ma;
      b = ((h - 120.0) / 60.0) * (ma - mi) + mi;
      break;
    case 3:
      r = mi;
      g = ((240.0 - h) / 60.0) * (ma - mi) + mi;
      b = ma;
      break;
    case 4:
      r = ((h - 240.0) / 60.0) * (ma - mi) + mi;
      g = mi;
      b = ma;
      break;
    case 5:
      r = ma;
      g = mi;
      b = ((360.0 - h) / 60.0) * (ma - mi) + mi;
      break;
  }

  rgbhsv[0] = (int)(r + 0.5);
  rgbhsv[1] = (int)(g + 0.5);
  rgbhsv[2] = (int)(b + 0.5);
}

1 と 0 で点灯するピクセルを指定しています。 f:id:macrochelys99:20180121043454p:plain
テキストエディタから 1, を検索してこんな感じにマーカーを付ければ出来上がりが見やすいし、上書きモードで書き換えていけば編集も楽です。