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

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

![Honeyview_IMG_1456.jpg](https://qiita-image-store.s3.amazonaws.com/0/2382/29afbfab-c731-630f-5f92-4a92f155d502.jpeg)

- 動いている動画は[こちら](https://youtu.be/n1MJITN7zWU)

作り方は、

- [【Arduino】3* 3 LED Cube【作ってみた】](http://ism1000ch.hatenablog.com/entry/2014/04/15/150442)

まるまるこれです。

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

材料は

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

です。道具としては

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

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

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

![Honeyview_IMG_1428.jpg](https://qiita-image-store.s3.amazonaws.com/0/2382/9336ef3b-0e24-d98a-ff2b-be37374fa927.jpeg)

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

なお3x3x3のLEDキューブであればArduino Unoだけで、カソードx3とアノードx9 で12ピンしか使わず、既存ピンだけであまりも出るうえ、ダイナミック点灯するため抵抗も不要で作れて結構よい感じです。
(1kΩの抵抗が必要との指摘がありました。ダイナミック点灯で3階層に分けて点灯するためArduinoの5Vを分配することにはなり、LEDが燃えたりはしませんが、ATMegaの制約などもあるため抵抗は入れていたほうが良さそうです。なお抵抗を入れなくても半年ぐらいは連続動作させても問題ありませんでした)

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

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

![IMG_1459.JPG](https://qiita-image-store.s3.amazonaws.com/0/2382/36d10512-3f50-8779-bb33-31dd5e64d692.jpeg)

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

![IMG_1458.JPG](https://qiita-image-store.s3.amazonaws.com/0/2382/3250a615-e676-5461-2373-f29124d9a2ab.jpeg)

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

```c++
// 階層ごとのカソード
// ↑ 上側
// 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](https://qiita-image-store.s3.amazonaws.com/0/2382/119541b6-8f83-b3f8-cd21-d49603d6762c.jpeg)

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

```c++
#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]);
      }
    }
  }

}
```