DIY Arduinoスノーフレーク:30個LEDディスプレイプロジェクト
部品と材料
| Arduino Nano R3 | ×1 |
プロジェクト概要
このスノーフレークは30個のLEDで構成され、Arduino Nanoマイクロコントローラーで個別に制御可能な17の独立したセグメントにグループ化されています。各LEDグループはPWMで調光可能で、美しいアニメーションを作成できます。
ツール
必要なのははんだごて、はんだ、ペンチだけです。
構造
まずパターンを選択します。私は素敵でシンプルな雪の結晶パターンを選び、Arduino Nanoが六角形のコア内部に収まるサイズで印刷しました。
配線を兼ねた支持構造は、0.8mmの真鍮ロッドをハンダで接続して作成。合計2mのロッドを使用しました。なぜフリーフォームなのか? ずっと挑戦してみたかったからで、忍耐と技術の試練になります。
まず単一のロッドを曲げてコアの六角形を作り、端をはんだ付け。さらに6本のロッドを六角形の頂点に追加してグランド配線を完成。すべてのLEDのカソードリードをここに接続して雪の結晶パターンを作ります。トリッキーだったのはSMD LEDの実装でしたが、段ボールと両面テープで作った治具で対応しました。
次にArduino Nanoマイクロコントローラーをコア構造の下に配置し、マイコンピンをすべてのLEDアノードリードに接続する3層の真鍮ロッド配線分のスペースを確保。これは膨大な忍耐を要しました。配線間のショートを避けつつ、電流制限抵抗も追加し、見た目もきれいにする必要があります。
葉のLEDはそれぞれ最寄りのArduino出力ピンに個別接続。枝のLEDは2個ずつグループ化してPWMピンに接続。コアのLEDも2個ずつグループ化して残りのピンに接続。Arduino NANOは出力ピンが18個しかありません(A6とA7は入力専用)。タッチセンサー用に1ピン必要なので、残り17ピンしかなく、コアLEDの2組をまとめて4個グループにしています。220Ω抵抗でピンあたり約8mAに制限、合計240mA(ATmega328チップの安全最大値は200mAと言われますが動作します)。
タッチセンサー
スノーフレークとインタラクトするため、もう1本の真鍮ロッドを追加して静電容量式タッチセンサーを作成。Paul Stoffregen氏の優れたライブラリとチュートリアルを見つけました。タッチセンサーでアニメーション変更、オン/オフ、タッチ時の輝きなど、様々なインタラクションが可能です。
最終注意点
はんだ付け前にすべてのダイオードをチェックすることを忘れないでください。後から交換するのは非常に困難です(破損や逆極性接続の場合)。
コード
当初はハードウェアPWMピンに接続された枝LEDのみ調光可能だと思っていました。しかし、すべてのピンをハードウェアPWMのように扱える素晴らしいソフトウェアPWMライブラリが存在しました! このセットアップでアニメーションの可能性が無限に広がります。以下のコードに初期アニメーションのいくつかを含めています。
snowflake.ino
#include "SoftPWM.h" #includebyte edgeLedPins[] = {13, A4, A5, 2, 8, 12}; byte middleLedPins[] = {10, 6, 3, 5, 9, 11}; byte innerLedPins[] = {A2, A3, A1, 4, 7, A1}; ADCTouchSensor touchSensor = ADCTouchSensor(A0, 1); void setup() { Serial.begin(115200); SoftPWMBegin(); } byte animation = 0; long touchAt = 0; void loop() { switch (animation) { case 0: _fill(100); break; case 1: shinyAnimation(); break; case 2: circleAnimation(); break; case 3: loopAnimation(); break; case 4: fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); animation++; break; case 5: smileAnimation(); break; default: animation = 0; break; } int touchValue = touchSensor.read(); if (touchAt + 2000 < millis() && touchValue > 1000) { touchAt = millis(); animation++; _fill(0); } } void fireworkAnimation() { for (int i = 0; i < 4; i++) { SoftPWMSet(innerLedPins[i], 100); delay(100); } SoftPWMSet(innerLedPins[4], 100); for (int i = 0; i < 6; i++) { SoftPWMSet(middleLedPins[i], 255); } delay(50); for (int i = 0; i < 6; i++) { SoftPWMSet(innerLedPins[i], 0); SoftPWMSet(edgeLedPins[i], 255); } delay(50); for (int i = 0; i < 6; i++) { SoftPWMSet(middleLedPins[i], 0); } delay(50); _fill(0); } void smileAnimation() { SoftPWMSet(innerLedPins[1], 100); SoftPWMSet(innerLedPins[3], 100); SoftPWMSet(middleLedPins[0], 255); SoftPWMSet(middleLedPins[5], 255); SoftPWMSet(edgeLedPins[2], 255); SoftPWMSet(edgeLedPins[3], 255); delay(2000); SoftPWMSet(innerLedPins[1], 0); SoftPWMSet(innerLedPins[3], 0); delay(100); SoftPWMSet(innerLedPins[1], 100); SoftPWMSet(innerLedPins[3], 100); delay(100); SoftPWMSet(innerLedPins[1], 0); SoftPWMSet(innerLedPins[3], 0); delay(100); } byte circleState[] = {100, 55, 10}; byte circleStateAnimation[] = {1, 1, 1}; void circleAnimation() { for (int i = 0; i < 3; i++) { if (circleState[i] >= 100) circleStateAnimation[i] = -1; else if (circleState[i] <= 10) circleStateAnimation[i] = 1; circleState[i] += circleStateAnimation[i]; } for (int i = 0; i < 6; i++) { SoftPWMSet(innerLedPins[i], circleState[0]); SoftPWMSet(middleLedPins[i], circleState[1]); SoftPWMSet(edgeLedPins[i], circleState[2]); } delay(5); } byte waveState[] = {100, 55, 10, 10, 55, 100}; byte waveStateAnimation[] = {1, 1, 1, -1, -1, -1}; void waveAnimation() { for (int i = 0; i < 6; i++) { if (waveState[i] >= 100) waveStateAnimation[i] = -1; else if (waveState[i] <= 10) waveStateAnimation[i] = 1; waveState[i] += waveStateAnimation[i]; } for (int i = 0; i < 6; i+=2) { SoftPWMSet(innerLedPins[i], waveState[0]); SoftPWMSet(middleLedPins[i], waveState[1]); SoftPWMSet(edgeLedPins[i], waveState[2]); SoftPWMSet(innerLedPins[i+1], waveState[3]); SoftPWMSet(middleLedPins[i+1], waveState[4]); SoftPWMSet(edgeLedPins[i+1], waveState[5]); } delay(10); } byte loopCounter = 0; byte loopState = 150; void loopAnimation() { SoftPWMSet(innerLedPins[loopCounter], loopState); SoftPWMSet(middleLedPins[loopCounter], loopState); SoftPWMSet(edgeLedPins[loopCounter], loopState); loopCounter = _nextIndex(loopCounter, 1); if (loopCounter == 0) { loopState = (loopState == 150 ? 0 : 150); } delay(100); } byte slowOnCounter = 0; byte slowOnState = 150; void slowOnAnimation() { byte randomLed = random(0, 18); if (randomLed < 6) SoftPWMSet(innerLedPins[randomLed], slowOnState); else if (randomLed < 12) SoftPWMSet(middleLedPins[randomLed-6], slowOnState); else SoftPWMSet(edgeLedPins[randomLed-12], slowOnState); slowOnCounter++; if (slowOnCounter >= 50) { slowOnCounter = 0; slowOnState = (slowOnState == 150 ? 0 : 150); } delay(50); } byte shinyState[] = {0, 100, 0, 100, 0, 100}; byte shinyStateAnimation[] = {1, 1, 1, 1, 1, 1}; byte shinyCounter = 0; void shinyAnimation() { for (int i = 0; i < 6; i++) { if (shinyState[i] >= 100) shinyStateAnimation[i] = -1; else if (shinyState[i] <= 0) shinyStateAnimation[i] = 1; shinyState[i] += shinyStateAnimation[i]; SoftPWMSet(edgeLedPins[i], shinyState[i]); } shinyCounter++; if (shinyCounter > 10) { shinyCounter = 0; for (byte r = random(1, 3); r > 0; r--) { byte randomLed = random(0, 12); if (randomLed < 6) SoftPWMSet(innerLedPins[random(0, 6)], 255); else SoftPWMSet(middleLedPins[random(0, 6)], 255); } } else { for (int i = 0; i < 6; i++) { SoftPWMSet(innerLedPins[i], 20); SoftPWMSet(middleLedPins[i], 20); } } delay(30); } byte fadingState[] = {0, 100, 0, 100, 0, 100}; byte fadingStateAnimation[] = {1, 1, 1, 1, 1, 1}; void fadingAnimation() { for (int i = 0; i < 6; i++) { if (fadingState[i] >= 100) fadingStateAnimation[i] = -1; else if (fadingState[i] <= 0) fadingStateAnimation[i] = 1; fadingState[i] += fadingStateAnimation[i]; SoftPWMSet(edgeLedPins[i], fadingState[i]); SoftPWMSet(middleLedPins[_nextIndex(i, 1)], fadingState[i]); SoftPWMSet(innerLedPins[i], 50); } delay(20); } void _fill(byte value) { for (int i = 0; i < 6; i++) { SoftPWMSet(edgeLedPins[i], value); SoftPWMSet(middleLedPins[i], value); SoftPWMSet(innerLedPins[i], value); } } byte _prevIndex(short index, byte step) { index -= step; while (index < 0) index += 6; return index; } byte _nextIndex(short index, byte step) { index += step; while (index > 5) index -= 6; return index; }
回路図
回路図画像を参照してください。
製造プロセス
- ローラーピン:エトルリア起源から現代の職人技へ
- 現代ボウリングピンの進化と職人技
- 古代フィビュラから現代製造へ:安全ピン完全解説
- Raspberry Pi I/Oブレイクアウトケーブルとプラグボードアダプターの自作:ステップバイステップガイド
- 動的LED三角アート: Arduino UNOで作る45個RGB LEDディスプレイ
- DHT11温湿度センサーで作る温度アラート – LEDとピエゾブザーで視覚/聴覚通知
- ボールグリッドアレイ(BGA)とは:仕組みと重要性
- ダウェルピンがプロジェクトの精度を守る:家具・棚・機械での活用法
- ダウェルピンの材質:木材、金属、プラスチックの特徴と用途
- ダウェルピン選定の5つの重要ポイント
- TinyML:エンジニアが結集し、超低消費電力システム向けAIを標準化