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

Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション

必要な部品と用品

Arduino Nano R3×1
サウンド検出モジュール×1
ブレッドボード×1
ジャンパーワイヤー×1式
アドレサブルLEDストリップ(WS2812B等)×1
IKEA DROPPARジャー(最大サイズ)×1
短いPVCパイプ×1

必要な工具

使用アプリ

プロジェクト概要

HELLO WORLD!!

音や音楽に反応して光る、見栄えの良いデスクライトをシンプルな部品と基本的なArduinoプログラミングで作ります。ゲーム中、音楽鑑賞中、あらゆる音に反応してデスク上で素晴らしいエフェクトを発揮します。YouTuber「Natural Nerd」氏のプロジェクトをベースに、筆者なりのアレンジを加えました。オリジナル動画:Youtube link

ステップ1: 主な材料とコスト目安

多くは代替可能ですが、以下はキーアイテムです。

見た目のアレンジ用:

総額約RM 83.30(約2,600円)。LEDストリップ(1mでRM 40)が最も高価です。

ステップ2: 電源配線

Arduino NanoのUSBケーブルをPCに直結で給電。外部電源(AC-DCアダプター)を使う場合は、必ずステップダウンモジュールで5Vに降圧してください。降圧なしで12Vを直結するとArduinoと部品が破損する恐れがあります。

Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション

サウンド検出モジュールはアナログ信号をArduinoに出力。両デバイスとも5V駆動。ステップダウンモジュールで12V→5V変換、または直接5V電源を使用。ArduinoのVINとサウンドモジュールのVCCをプラス、両GNDをマイナスに接続。LEDストリップの電源線も同じ電源に並列接続。

ステップ3: 検出モジュールとLEDストリップ接続

Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション

電源接続後、信号線を接続:

配線は収縮チューブで保護し、狭いケース内でショートしないよう注意。回路図通りに配線してください。

ステップ4: コード書き込み

このプロジェクトの核心はコードです。アナログ音量値をLED点灯数にマッピングする基本原理ですが、平均化、ピーク検出、フェードアウト等で「それっぽい」動きに仕上げています。提供コードを使用可能です。

ステップ5: ケース制作

Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション
  1. 蓋の加工:IKEAジャーの蓋はガラス製だったため、発泡ボードで同径の円盤を自作。濡れたマーカーでガラスの直径を紙に転写し、ボードに貼ってカット。
  2. ガラス除去:厚手のビニール袋でジャー全体を覆い、屋外でガラスを割る。飛散防止と怪我防止のため必須。破片を完全に除去。
  3. 円盤固定:発泡ボード円盤を蓋の中央にはめ込み、隙間なく固定。
  4. 配線穴あけ:ミニブレッドボードを蓋中央に配置。サウンドモジュール用(蓋外側配置のため)、Arduino USB用の穴を開ける。
  5. PVCパイプカット:マスキングテープで目印を付け、ジャーの高さに合わせてカット。
  6. LEDストリップ巻き付け:PVCパイプにLEDストリップをらせん状に巻き付け、余分な配線はパイプ内に収納。ホットグルーでブレッドボード上の余った発泡ボードに固定。
  7. サウンドモジュール取り付け:蓋外側に配置(外部の音を拾いやすくするため)。配線してブレッドボードへ接続。Arduinoは垂直立てでUSBケーブル接続しやすく。
Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション

試行錯誤の末、無事完成しました!

スケッチ(コード)

FastLEDライブラリを使用。音声反応ロジック:短期移動平均でノイズ除去、長期平均で楽曲レベル検出、ピーク時に「HIGHモード」へ遷移して色変化・高速フェード、通常時はゆっくりフェードアウト。

#include <FastLED.h>

/** 基本設定 **/
#define NUM_LEDS 60        // LED数
#define LED_PIN 6          // データピン
#define ANALOG_READ 0      // マイク入力ピン
#define MIC_LOW 0.0        // マイク最小値
#define MIC_HIGH 200.0     // マイク最大値

/** その他マクロ **/
#define AVGLEN 5           // 短期平均サンプル数
#define LONG_SECTOR 20     // 長期平均サンプル数
#define HIGH_MODE 3
#define NORMAL_MODE 2
#define MSECS 30000        // 長期平均リセット周期(ms)
#define CYCLES (MSECS / DELAY)
#define DEV_THRESH 0.8     // 異常値除外閾値
#define DELAY 1            // ループ遅延(ms)

float fscale(float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve);
void insert(int val, int *avgs, int len);
int compute_average(int *avgs, int len);
void visualize_music();

int curshow = NUM_LEDS;
int mode = 0;
int songmode = NORMAL_MODE;
unsigned long song_avg = 0;
int iter = 0;
float fade_scale = 1.2;
CRGB leds[NUM_LEDS];
int avgs[AVGLEN] = {-1};
int long_avg[LONG_SECTOR] = {-1};

struct time_keeping { unsigned long times_start; short times; };
struct color { int r; int g; int b; };
struct time_keeping high;
struct color Color;

void setup() {
  Serial.begin(9600);
  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
  for (int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(0, 0, 255);
  FastLED.show(); delay(1000);
  for (int i = 0; i < AVGLEN; i++) insert(250, avgs, AVGLEN);
  high.times = 0; high.times_start = millis();
  Color.r = 0; Color.g = 0; Color.b = 1;
}

void loop() {
  switch(mode) { case 0: visualize_music(); break; default: break; }
  delay(DELAY);
}

void check_high(int avg) {
  if (avg > (song_avg/iter * 1.1)) {
    if (high.times != 0) {
      if (millis() - high.times_start > 200.0) { high.times = 0; songmode = NORMAL_MODE; }
      else { high.times_start = millis(); high.times++; }
    } else { high.times++; high.times_start = millis(); }
  }
  if (high.times > 30 && millis() - high.times_start < 50.0) songmode = HIGH_MODE;
  else if (millis() - high.times_start > 200) { high.times = 0; songmode = NORMAL_MODE; }
}

void visualize_music() {
  int sensor_value = analogRead(ANALOG_READ);
  if (sensor_value == 0) return;
  int mapped = (int)fscale(MIC_LOW, MIC_HIGH, MIC_LOW, (float)MIC_HIGH, (float)sensor_value, 2.0);
  int avg = compute_average(avgs, AVGLEN);
  if (abs(avg - mapped) > avg * DEV_THRESH) return;
  insert(mapped, avgs, AVGLEN);
  insert(avg, long_avg, LONG_SECTOR);
  song_avg += avg; iter++;
  if (iter > CYCLES) { song_avg = song_avg / iter; iter = 1; }
  int longavg = compute_average(long_avg, LONG_SECTOR);
  check_high(longavg);
  if (songmode == HIGH_MODE) { fade_scale = 3; Color.r = 5; Color.g = 3; Color.b = -1; }
  else { fade_scale = 2; Color.r = -1; Color.b = 2; Color.g = 1; }
  curshow = (int)fscale(MIC_LOW, MIC_HIGH, 0.0, (float)NUM_LEDS, (float)avg, -1);
  for (int i = 0; i < NUM_LEDS; i++) {
    if (i < curshow) {
      leds[i].r = constrain(leds[i].r + Color.r, 0, 255);
      leds[i].g = constrain(leds[i].g + Color.g, 0, 255);
      leds[i].b = constrain(leds[i].b + Color.b, 0, 255);
    } else {
      leds[i] = CRGB(leds[i].r/fade_scale, leds[i].g/fade_scale, leds[i].b/fade_scale);
    }
  }
  FastLED.show();
}

int compute_average(int *avgs, int len) {
  int sum = 0; for (int i = 0; i < len; i++) sum += avgs[i]; return sum / len;
}

void insert(int val, int *avgs, int len) {
  for (int i = 0; i < len; i++) if (avgs[i] == -1) { avgs[i] = val; return; }
  for (int i = 1; i < len; i++) avgs[i-1] = avgs[i];
  avgs[len-1] = val;
}

float fscale(float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve) {
  // Arduino公式fscale関数(対数/指数スケーリング)
  if (curve > 10) curve = 10; if (curve < -10) curve = -10;
  curve = (curve * -.1); curve = pow(10, curve);
  if (inputValue < originalMin) inputValue = originalMin;
  if (inputValue > originalMax) inputValue = originalMax;
  float OriginalRange = originalMax - originalMin;
  float NewRange = (newEnd > newBegin) ? newEnd - newBegin : newBegin - newEnd;
  bool invFlag = (newEnd <= newBegin);
  float zeroRefCurVal = inputValue - originalMin;
  float normalizedCurVal = zeroRefCurVal / OriginalRange;
  if (originalMin > originalMax) return 0;
  float rangedValue = invFlag ? newBegin - (pow(normalizedCurVal, curve) * NewRange)
                              : (pow(normalizedCurVal, curve) * NewRange) + newBegin;
  return rangedValue;
}

回路図

Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション

製造プロセス

  1. TinyML言語検出器:Edge ImpulseとArduino Nano 33 BLE Senseで「はい」を多言語判別
  2. Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
  3. Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
  4. Arduinoでアイアンマンを自作:部品・センサー・手順ガイド
  5. ArduinoとBluetoothで作るスマート紛失防止タグ「Find Me」
  6. Arduino UNOとDFPlayer Miniで音声を再生する方法 - ステップバイステップガイド
  7. DIY Arduino加湿器コントローラー:リレーを用いた安全な高電圧制御
  8. Arduino超音波センサーとProcessingで作るリアルタイム距離測定ソナー
  9. MobBob:Arduino製ロボットをAndroidスマホでシームレスに制御
  10. Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム
  11. ゲーム用カスタムArduinoジョイスティックハンドルの製作