Arduino
電子工作

3x3x3のLEDキューブを作ってみた

余ったLEDで作る3x3x3のLEDキューブを作ってみました。

Honeyview_IMG_1456.jpg

作り方は、

まるまるこれです。

1層ごとにカソードを束ねて、縦にアノードの配線が重なるようにして、1層ずつダイナミック点灯します。

材料は

  • LED 27個
  • ユニバーサル基盤 6cm x 8cm ぐらいの
  • Arduino Uno
  • ジャンプワイヤ 12本
  • ジャンプワイヤ針金 10cm 6本
  • ポリウレタンエナメルワイヤー
  • はんだ

です。道具としては

  • はんだごて
  • ニッパー
  • ラジオペンチ

でおこないました。ひたすらラジオペンチでLEDの足を曲げる作業をやる感じです。これがいい感じに設計するのが非常に難しいです。

きれいに作ってみようと思って、一応段ボールで冶具を作ってみたのですが、あんまりうまくいきませんでした。

Honeyview_IMG_1428.jpg

もっときれいに整列させる方法が合ったら学びたいです。

なお3x3x3のLEDキューブであればArduino Unoだけで、カソードx3とアノードx9 で12ピンしか使わず、既存ピンだけであまりも出るうえ、ダイナミック点灯するため抵抗も不要で作れて結構よい感じです。

なお4x4x4以上になると4+16で20ピン必要となりシフトレジスタを使わなくては厳しそうですね。

裏の配線はこんな感じです。

IMG_1459.JPG

各層からまとめたカソードをおろしてくるところはこんな感じ。

IMG_1458.JPG

なおコードはこんな感じ。

// 階層ごとのカソード
// ↑ 上側
// 3
// 2
// 1
// ↓ 基盤側
int fl1 = 4; 
int fl2 = 3; 
int fl3 = 2; 

// セグメントごとのアノード
// ポジション
// ABC → ワイヤー側
// DEF → ワイヤー側
// GHI → ワイヤー側
int segA = 5; 
int segB = 6; 
int segC = 7;
int segD = 8;
int segE = 9;
int segF = 10;
int segG = 11;
int segH = 12;
int segI = 13;

int dinamicDelay = 1; // ダイナミック点灯時のディレイ 6回実行される (1の場合6ms)
int loopDinamicCount = 10; // ループで実行するダイナミック点灯の回数 (100だとx6, 600msごとの遷移)

// パターンは 
// フロア1 ABCDEFGHI, フロア2 ABCDEFGHI, フロア3 ABCDEFGHI
// の順に1か0で表現、1がオン、0がオフ
// 配列の長さが25までのようなのでパターンの限界は25

// テスト
//int patternLength = 1;
//String patterns[] = {
//  "100000000000000000000000000"
//};

// 下から上昇
//int patternLength = 3;
//String patterns[] = {
//  "111111111000000000000000000",
//  "000000000111111111000000000",
//  "000000000000000000111111111"
//};

// うずまき
int patternLength = 26;
String patterns[] = {
  "110000000000000000000000000",
  "011000000000000000000000000",
  "001001000000000000000000000",
  "000001001000000000000000000",
  "000000011000000000000000000",
  "000000110000000000000000000",
  "000100100000000000000000000",
  "000110000000000000000000000",
  "000010000000010000000000000",
  "000000000000011000000000000",
  "000000000000001001000000000",
  "000000000000000011000000000",
  "000000000000000110000000000",
  "000000000000100100000000000",
  "000000000100100000000000000",
  "000000000110000000000000000",
  "000000000011000000000000000",
  "000000000001000000001000000",
  "000000000000000000001001000",
  "000000000000000000000001001",
  "000000000000000000000000011",
  "000000000000000000000000110",
  "000000000000000000000100100",
  "000000000000000000100100000",
  "000000000000000000110000000",
};

void setup() {
  pinMode(fl1, OUTPUT);
  pinMode(fl2, OUTPUT);
  pinMode(fl3, OUTPUT);

  pinMode(segA, OUTPUT);
  pinMode(segB, OUTPUT);
  pinMode(segC, OUTPUT);
  pinMode(segD, OUTPUT);
  pinMode(segE, OUTPUT);
  pinMode(segF, OUTPUT);
  pinMode(segG, OUTPUT);
  pinMode(segH, OUTPUT);
  pinMode(segI, OUTPUT);

  Serial.begin(9600);
}

void clearDisplay() {
  digitalWrite(fl1, HIGH);
  digitalWrite(fl2, HIGH);
  digitalWrite(fl3, HIGH);

  digitalWrite(segA, LOW);
  digitalWrite(segB, LOW);
  digitalWrite(segC, LOW);
  digitalWrite(segD, LOW);
  digitalWrite(segE, LOW);
  digitalWrite(segF, LOW);
  digitalWrite(segG, LOW);
  digitalWrite(segH, LOW);
  digitalWrite(segI, LOW);

  delay(dinamicDelay);
}

bool onoff(String pattern, int index) {
  char c = pattern.charAt(index);
  return c == '1';
}

void showPattern(String pattern) {
  int i = 0;
  clearDisplay();
  digitalWrite(fl1, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);

  clearDisplay();
  digitalWrite(fl2, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);

  clearDisplay();
  digitalWrite(fl3, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);
}

void loop() {
  for (int i = 0; i < patternLength; i++) {
    for (int j = 0; j < loopDinamicCount; j++) {
      showPattern(patterns[i]);
    } 
  }
}

どちらかというときれいなアニメーションをデザインする方がセンスいりそうです。今度じっくり作ってみたいと思っています。

LEDキューブちょっとしたインテリアにはよさそうです。いずれシフトレジスタをつかって4x4x4にもチャレンジしてみたいところです。

追伸

アナログピン0~5を使って、6つのモードを表現するようする制御も追加しました。
アナログピンを5Vにそれぞれつなげばモード変更できます。

なお、グローバル変数がメモリを使いすぎてたので、文字列じゃなくてintの配列にしてデータを表現するようにしました。

こんな感じでジャンパワイヤでモードを選択します。

IMG_1531.JPG

コードはこんな形に進化しました。さすがにArduino IDEで実装するのが大変だったのでVS CodeのArduino拡張を使ってやったら便利すぎました。

#include <Arduino.h>

// 階層ごとのカソード
// ↑ 上側
// 3
// 2
// 1
// ↓ 基盤側
int fl1 = 4; 
int fl2 = 3; 
int fl3 = 2; 

// セグメントごとのアノード
// ポジション
// ABC → ワイヤー側
// DEF → ワイヤー側
// GHI → ワイヤー側
int segA = 5; 
int segB = 6; 
int segC = 7;
int segD = 8;
int segE = 9;
int segF = 10;
int segG = 11;
int segH = 12;
int segI = 13;

int dinamicDelay = 1; // ダイナミック点灯時のディレイ 6回実行される (1の場合6ms)

// パターンは 
// フロア1 ABCDEFGHI, フロア2 ABCDEFGHI, フロア3 ABCDEFGHI
// の順に1か0で表現、1がオン、0がオフ

// mode 0 (接続無しも同様)
int mode0Length = 1;
int show0Count = 30;
int mode0[1][27] = {
  {
    1,1,1,
    1,1,1,
    1,1,1,

    1,1,1,
    1,1,1,
    1,1,1,

    1,1,1,
    1,1,1,
    1,1,1,
  },
};

// mode 1 噴水
int mode1Length = 6;
int show1Count = 30;
int mode1[6][27] = {
  {
    0,0,0,
    0,1,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,1,0,
    1,1,1,
    0,1,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    1,1,1,
    1,1,1,
    1,1,1,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    1,0,1,
    0,0,0,
    1,0,1,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    1,1,1,
    1,0,1,
    1,1,1,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    1,0,1,
    0,0,0,
    1,0,1,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
};

// mode 2 縄跳び
int mode2Length = 8;
int show2Count = 20;
int mode2[8][27] = {
  {
    1,1,1,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    1,1,1,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    1,1,1,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    1,1,1,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    1,1,1,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    1,1,1,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    1,1,1,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    1,1,1,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
};

// mode 3 斜め縄跳び
int mode3Length = 6;
int show3Count = 20;
int mode3[6][27] = {
  {
    0,0,0,
    0,0,0,
    1,0,0,

    1,0,0,
    0,1,0,
    0,0,1,

    0,0,1,
    0,0,0,
    0,0,0,
  },
  {
    0,0,0,
    0,0,0,
    0,0,0,

    1,0,1,
    0,1,0,
    1,0,1,

    0,0,0,
    0,0,0,
    0,0,0,
  },
  {
    0,0,1,
    0,0,0,
    0,0,0,

    1,0,0,
    0,1,0,
    0,0,1,

    0,0,0,
    0,0,0,
    1,0,0,
  },
  {
    0,1,0,
    0,0,1,
    0,0,0,

    1,0,0,
    0,1,0,
    0,0,1,

    0,0,0,
    1,0,0,
    0,1,0,
  },
  {
    0,0,0,
    0,1,0,
    0,0,0,

    1,0,0,
    0,1,0,
    0,0,1,

    0,0,0,
    0,1,0,
    0,0,0,
  },
  {
    0,0,0,
    1,0,0,
    0,1,0,

    1,0,0,
    0,1,0,
    0,0,1,

    0,1,0,
    0,0,1,
    0,0,0,
  },
};

// mode 4 横回転
int mode4Length = 4;
int show4Count = 30;
int mode4[4][27] = {
  {
    0,0,1,
    0,0,0,
    1,0,0,

    0,0,1,
    0,0,0,
    1,0,0,

    0,0,1,
    0,0,0,
    1,0,0,
  },
  {
    0,0,0,
    1,0,1,
    0,0,0,

    0,0,0,
    1,0,1,
    0,0,0,

    0,0,0,
    1,0,1,
    0,0,0,
  },
  {
    1,0,0,
    0,0,0,
    0,0,1,

    1,0,0,
    0,0,0,
    0,0,1,

    1,0,0,
    0,0,0,
    0,0,1,
  },
  {
    0,1,0,
    0,0,0,
    0,1,0,

    0,1,0,
    0,0,0,
    0,1,0,

    0,1,0,
    0,0,0,
    0,1,0,
  },
};

// mode 5 点灯無し
int mode5Length = 1;
int show5Count = 30;
int mode5[1][27] = {
  {
    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,

    0,0,0,
    0,0,0,
    0,0,0,
  },
};

void setup() {
  pinMode(fl1, OUTPUT);
  pinMode(fl2, OUTPUT);
  pinMode(fl3, OUTPUT);

  pinMode(segA, OUTPUT);
  pinMode(segB, OUTPUT);
  pinMode(segC, OUTPUT);
  pinMode(segD, OUTPUT);
  pinMode(segE, OUTPUT);
  pinMode(segF, OUTPUT);
  pinMode(segG, OUTPUT);
  pinMode(segH, OUTPUT);
  pinMode(segI, OUTPUT);

  Serial.begin(9600);
}

void clearDisplay() {
  digitalWrite(fl1, HIGH);
  digitalWrite(fl2, HIGH);
  digitalWrite(fl3, HIGH);

  digitalWrite(segA, LOW);
  digitalWrite(segB, LOW);
  digitalWrite(segC, LOW);
  digitalWrite(segD, LOW);
  digitalWrite(segE, LOW);
  digitalWrite(segF, LOW);
  digitalWrite(segG, LOW);
  digitalWrite(segH, LOW);
  digitalWrite(segI, LOW);

  delay(dinamicDelay);
}

bool onoff(int* pattern, int index) {
  return pattern[index] == 1;
}

void showPattern(int* pattern) {
  int i = 0;
  clearDisplay();
  digitalWrite(fl1, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);

  clearDisplay();
  digitalWrite(fl2, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);

  clearDisplay();
  digitalWrite(fl3, LOW);
  digitalWrite(segA, onoff(pattern, i++));
  digitalWrite(segB, onoff(pattern, i++));
  digitalWrite(segC, onoff(pattern, i++));
  digitalWrite(segD, onoff(pattern, i++));
  digitalWrite(segE, onoff(pattern, i++));
  digitalWrite(segF, onoff(pattern, i++));
  digitalWrite(segG, onoff(pattern, i++));
  digitalWrite(segH, onoff(pattern, i++));
  digitalWrite(segI, onoff(pattern, i++));
  delay(dinamicDelay);
}

/*
 * アナログピンの番号がモードに対応
 */
int getMode() {
  int mode = 0;
  int max = 0;

  for (int i = 0; i <= 5; i++) {
    int value = analogRead(i);
    if (value > max) {
      mode = i;
      max = value;
    }
  }

  int threshold = 1000;
  if (max >= threshold) {
    return mode;
  } else {
    return 0;
  }
}

void loop() {
  int mode = getMode();
  Serial.print("mode: ");
  Serial.println(mode);
  // ループで実行するダイナミック点灯の回数 (100だとx6, 600msごとの遷移)
  int loopDinamicCount; 

  if (mode == 0) {
    loopDinamicCount = show0Count; 
    for (int i = 0; i < mode0Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode0[i]);
      }
    }
  }

  if (mode == 1) {
    loopDinamicCount = show1Count; 
    for (int i = 0; i < mode1Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode1[i]);
      }
    }
  }

  if (mode == 2) {
    loopDinamicCount = show2Count; 
    for (int i = 0; i < mode2Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode2[i]);
      }
    }
  }

  if (mode == 3) {
    loopDinamicCount = show3Count; 
    for (int i = 0; i < mode3Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode3[i]);
      }
    }
  }

  if (mode == 4) {
    loopDinamicCount = show4Count; 
    for (int i = 0; i < mode4Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode4[i]);
      }
    }
  }

  if (mode == 5) {
    loopDinamicCount = show5Count; 
    for (int i = 0; i < mode5Length; i++) {
      for (int j = 0; j < loopDinamicCount; j++) {
        showPattern(mode5[i]);
      }
    }
  }

}