工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド

必要な部品と材料

部品数量備考
5mm RGB拡散型LED 共通アノード512個8×8×8 = 512ボクセル、eBayで約$30
DM13A LEDドライバー (定電流シンク)12個1チップ16ch、24個のシフトレジスタ+トランジスタ置換
74HC138 3-8ラインデコーダー1個8段アノード選択用
IRF9640 PチャネルMOSFET8個アノード側ハイサイドスイッチング
Arduino UNO (またはATmega328P単体)1個16MHz水晶、22pF×2で単体化可能
5V 2.5A スイッチング電源1個最大約20W、全点灯時のピーク電流対応
抵抗 10kΩ8本MOSFETゲートプルアップ
抵抗 1kΩ8本MOSFETゲート駆動用
抵抗 100Ω8本LED電流制限 (DM13A側で定電流制御だが保護用)

使用ソフトウェア

プロジェクト概要

Kevin Darrah氏のRGBキューブ制作記に触発されて製作した8×8×8 RGB LEDキューブです。Kevin氏の設計では24個のシフトレジスタ(74HC595)、192個のトランジスタ、640本の抵抗が必要でしたが、DM13A定電流LEDドライバー (16ch × 12個 = 192ch) を採用することで部品点数を大幅に削減しました。DM13Aは1チップ約$1 (eBay) と安価です。

キューブ本体の組み立て

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド
Kevin Darrah氏のオリジナルキューブ (参考)

キューブ構造の組み立て手法はKevin氏の動画を参考にしています。512個のLEDを8層×8行×8列の立方体マトリクスに配線する作業は非常に時間がかかりますが、ジグを自作すれば効率化可能です。

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド
DM13Aドライバーボード (12チップ実装)

制御基板の製作

市販基板 (スルーホールメッキ) のコストを避けるため、トナーメソッドで自作基板を作成し、DM13Aチップを実装。各LEDの接続はワイヤーラッピングワイヤーで手配線しました (Eagleファイルに手配線版とオートルート版の両方を同梱)。

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド
部品面
Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド
半田面 (手配線)

マイコン・アノード基板にはMSGEQ7 (7バンドグラフィックイコライザー) とマイクプリアンプも搭載済みですが、現バージョンでは未使用です。将来的に音声連動エフェクトを実装予定。市販のUNO/Nanoを使う場合は、74HC138とPチャネルMOSFET 8個 + 抵抗のみをユニバーサル基板に実装すれば動作します。

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド
40×9mm桧材で自作ケース、5V 20W電源内蔵

アニメーションはKevin氏のコードをベースに、いくつか追加しています。

完成と所感

電子回路の簡略化により部品点数・配線工数は大幅に減らせましたが、512個のLEDを立体配線する作業自体は非常に時間がかかります。しかし完成時の達成感と美しさは格別です。600個の5mm共通アノード拡散RGB LEDはeBayで約$30で入手可能です。

Arduinoスケッチ (Cube_8x8x8_V1.ino)

SPI通信でDM13Aへデータ転送、Timer1割り込みによる多重化 + 4bit BAM (Bit Angle Modulation) で16階調フルカラー表示を実現。主要構造:

/* 8x8x8 RGB LED Cube by John Bradnam based on Kevin Darrah
 * V12 04/17/2013 - BAMタイミング修正、ブランクピン制御移動、ビット演算修正
 */

#include <SPI.h>

#define LATCH_PIN 2
#define BLANK_PIN 4
#define DATA_PIN 11  // SPI MOSI (固定)
#define CLOCK_PIN 13 // SPI SCK  (固定)
#define LAYER_A 5    // 74HC138 A
#define LAYER_B 6    // 74HC138 B
#define LAYER_C 7    // 74HC138 C
#define SWITCH_PGM 10
#define SWITCH_SEQ 9

#define CUBE_SIZE 8
#define CUBE_MAX (CUBE_SIZE - 1)
#define LEDS_PER_LEVEL (CUBE_SIZE * CUBE_SIZE) // 64

// 4ビットBAM用: 各色4プレーン × 64バイト
byte red0[LEDS_PER_LEVEL], red1[LEDS_PER_LEVEL], red2[LEDS_PER_LEVEL], red3[LEDS_PER_LEVEL];
byte green0[LEDS_PER_LEVEL], green1[LEDS_PER_LEVEL], green2[LEDS_PER_LEVEL], green3[LEDS_PER_LEVEL];
byte blue0[LEDS_PER_LEVEL], blue1[LEDS_PER_LEVEL], blue2[LEDS_PER_LEVEL], blue3[LEDS_PER_LEVEL];

int level = 0;
int anodeLevel = 0;
int BAM_Bit = 0, BAM_Counter = 0;
int animation = 0;

void setup() {
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2); // 8MHz
  
  noInterrupts();
  // Timer1: CTCモード、64分周 (250kHz)、OCR1A=30 → 124μs周期 (約8kHz/段)
  TCCR1A = 0;
  TCCR1B = B00001011;
  TIMSK1 = B00000010;
  OCR1A = 30;
  
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LAYER_A, OUTPUT);
  pinMode(LAYER_B, OUTPUT);
  pinMode(LAYER_C, OUTPUT);
  digitalWrite(LAYER_A, LOW);
  digitalWrite(LAYER_B, LOW);
  digitalWrite(LAYER_C, LOW);
  pinMode(SWITCH_PGM, INPUT);
  pinMode(SWITCH_SEQ, INPUT);
  // BLANK_PINは割り込み内で出力モードに (起動時フラッシュ防止)
  SPI.begin();
  interrupts();
}

void loop() {
  if (digitalRead(SWITCH_PGM) == HIGH) {
    test_leds(); // テストモード
  } else {
    clean(); // 全消灯
    animation++;
    switch (animation) {
      case 1: rainVersionTwo(20); break;
      case 2: folder(10); break;
      case 3: sinwaveTwo(15); break;
      case 4: randomColor(10); break;
      case 5: wipe_out(10); break;
      case 6: bouncyvTwo(15); break;
      case 7: color_wheelTWO(10); break;
      case 8: harlem_shake(); break;
      case 9: ripples(10); break;
      case 10: animation = 0; break;
    }
  }
}

// LED制御: (段, 行, 列, R輝度0-15, G輝度0-15, B輝度0-15)
void LED(int level, int row, int col, byte red, byte green, byte blue) {
  level = constrain(level, 0, CUBE_MAX);
  row = constrain(row, 0, CUBE_MAX);
  col = constrain(col, 0, CUBE_MAX);
  red = constrain(red, 0, 15);
  green = constrain(green, 0, 15);
  blue = constrain(blue, 0, 15);
  
  int wholebyte = (level * LEDS_PER_LEVEL) + (row * CUBE_SIZE) + col; // 0-511
  int whichbyte = wholebyte / 8; // 0-63
  int whichbit = wholebyte & 7;  // 0-7
  
  // 4ビットプレーンへビット書き込み
  bitWrite(red0[whichbyte], whichbit, bitRead(red, 0));
  bitWrite(red1[whichbyte], whichbit, bitRead(red, 1));
  bitWrite(red2[whichbyte], whichbit, bitRead(red, 2));
  bitWrite(red3[whichbyte], whichbit, bitRead(red, 3));
  bitWrite(green0[whichbyte], whichbit, bitRead(green, 0));
  bitWrite(green1[whichbyte], whichbit, bitRead(green, 1));
  bitWrite(green2[whichbyte], whichbit, bitRead(green, 2));
  bitWrite(green3[whichbyte], whichbit, bitRead(green, 3));
  bitWrite(blue0[whichbyte], whichbit, bitRead(blue, 0));
  bitWrite(blue1[whichbyte], whichbit, bitRead(blue, 1));
  bitWrite(blue2[whichbyte], whichbit, bitRead(blue, 2));
  bitWrite(blue3[whichbyte], whichbit, bitRead(blue, 3));
}

// Timer1割り込み: 多重化 + BAM出力
ISR(TIMER1_COMPA_vect) {
  PORTD |= 1 << BLANK_PIN; // 全消灯 (ブランキング)
  
  // BAMビット決定 (カウンターベース)
  if (BAM_Counter == (CUBE_SIZE * 1)) BAM_Bit++;
  else if (BAM_Counter == (CUBE_SIZE * 3)) BAM_Bit++;
  else if (BAM_Counter == (CUBE_SIZE * 7)) BAM_Bit++;
  BAM_Counter++;
  
  int bytes_per_level = LEDS_PER_LEVEL / 8; // 8
  int shift_max = level + bytes_per_level;
  
  // 現在のBAMビットに対応するプレーンをSPI転送
  switch (BAM_Bit) {
    case 0:
      for (int i = level; i < shift_max; i++) SPI.transfer(blue0[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(green0[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(red0[i]);
      break;
    case 1:
      for (int i = level; i < shift_max; i++) SPI.transfer(blue1[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(green1[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(red1[i]);
      break;
    case 2:
      for (int i = level; i < shift_max; i++) SPI.transfer(blue2[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(green2[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(red2[i]);
      break;
    case 3:
      for (int i = level; i < shift_max; i++) SPI.transfer(blue3[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(green3[i]);
      for (int i = level; i < shift_max; i++) SPI.transfer(red3[i]);
      if (BAM_Counter == (CUBE_SIZE * 15)) { BAM_Counter = 0; BAM_Bit = 0; }
      break;
  }
  
  // アノード選択 (74HC138)
  digitalWrite(LAYER_A, bitRead(anodeLevel, 0));
  digitalWrite(LAYER_B, bitRead(anodeLevel, 1));
  digitalWrite(LAYER_C, bitRead(anodeLevel, 2));
  
  // ラッチ → ブランキング解除
  PORTD |= 1 << LATCH_PIN;
  PORTD &= ~(1 << LATCH_PIN);
  PORTD &= ~(1 << BLANK_PIN);
  
  anodeLevel = (anodeLevel + 1) & CUBE_MAX;
  level = (level + bytes_per_level) & (LEDS_PER_LEVEL - 1);
  pinMode(BLANK_PIN, OUTPUT); // 初回のみ出力モード化
}

// 以下、アニメーション関数群 (rainVersionTwo, folder, sinwaveTwo, 
// randomColor, wipe_out, bouncyvTwo, color_wheelTWO, harlem_shake, ripples 等)
// コード長大のため省略。GitHubまたは添付ファイル参照。

回路図・設計ファイル

Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド Arduinoで作る8x8x8 RGB LEDキューブ 完全ガイド Eagle設計ファイル: eagle_files_WfqPEUP7Mp.zip (手配線版 + オートルート版)

製作のポイントとアドバイス

LEDキューブ組み立て

電子回路

ソフトウェア拡張

参考リソース

製造プロセス

  1. スマート渇きアラート植物アラーム – 最適な水やりタイミングを知らせるDIYセンサー
  2. Arduino Megaで作る美しい5x5x5 LEDキューブ
  3. イタリア語ワードクロック自作:Arduino Nano、NeoPixel、LCD、RTCで時刻を文字で表示
  4. Arduino MKR Fox 1200で作る信頼性の高いSigfox kWhメーター
  5. Arduino RGB LEDカラーミキサー:初心者向けDIYプロジェクト
  6. Bluetooth対応温度モニター:Arduino NanoとHC-06でスマホ監視
  7. 屋外対応DMX制御RGB LED投光器 – 低コスト・高性能・自作PCB
  8. 非接触ジェスチャー錠: APDS-9960とArduinoでセキュアアクセス
  9. Arduino Nanoコンパニオンキット – 電子工作に必須の部品とツールを搭載した多機能IC型デバイス
  10. Arduino用絶縁アナログ入力回路の設計:LM331 V-F変換+フォトカプラでノイズ耐性を確保
  11. Arduino UNOで作る精密反応速度テスター