ソロでたのしむ

キャンプ、電子工作、DIY、乗り物などの趣味を紹介

LEDサウンドレベルメーターの自作。RGB LEDをナイトライダー風に光らせる

LEDサウンドレベルメーターの自作。WS2812Bをナイトライダー風に光らせる

ナイトライダーのK.I.T.T.をご存知ですか?

私と同年代の方々は、頭の中でテーマソングの演奏が始まってしまったのではないでしょうか。

おさらいしておくと、ナイトライダーは80年代のアメリカのドラマで、主人公マイケル.ナイトと彼に仕える人工知能を搭載した車の話です。

K.I.T.T.は人工知能の名前で「キット」と呼ばれていました。調べてみてわかったのですが、Knight Industries Two Thousand(ナイト2000)の頭文字をとったものだったそうです。

今回再現するのは、キットが喋る時に連動するボイスインジケータ の光り方だけで、ディテールにはこだわりません。

ドラマでは「了解です、マイケル」などたくさん喋っていましたが、私は運転していて話しかけられても照れるので、キットの声は再現しません。

かわりに、車内の音の大きさに反応するようにして、音楽や人の声にあわせて光るようにします。

 

市販品があった!

 今回Arduinoで自作しようと決意する前に、市販品で同じようなものがないか探してみたところ、過去に販売されていました。

シガーソケットに差し込むタイプのUSB電源(2個)ですが、いくつかのパターンで喋るようです。

しかし、もう生産されていないようで、日本でもアメリカでもオークションサイトで高値で取引きされています。

簡単に入手できれば、ボックスだけ使って光り方はArduinoで制御したかったのですが、残念です。

Arduinoと接続するモジュール

 ボイスインジケータ を再現するにあたり、WS2812Bを8x8個並べたカラーLEDモジュールと、サウンドセンサーモジュールを使います。

LEDはバーグラフタイプのモジュールの方が実物に近いのですが、ちょうどいい大きさのものが見つからなかったので、使いやすいWS2812Bで進めます。

 サウンドセンサーで検知した音の大きさに応じて、キットのように上下方向にグラフを立てて、カラーLEDモジュールに表示します。

 表示イメージとパターン

 8x8マトリックスLEDのうち6x8個を使って、ボイスインジケータを再現します。

 実物同様の赤色だけで光るパターンの他に、他の色で光ったり、余っている2x8個を違う光り方で装飾したりするパターンにも切り替えられるようにします。

パターン① KITTを再現します(赤色)。

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

パターン② KITT風で、色を徐々に変化させます。

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

パターン③ パターン①に加え、3本のインジケーターの間の余っているスペースに光を流します。今回は上下ですが左右に流れる光こそナイトライダーのイメージそのものですね。

パターン④ パターン②に加え、3本のインジケーターの間の余っているスペースに光を流します。

f:id:solocamptouring:20180408122511j:plain
f:id:solocamptouring:20180408122530j:plain

パターン⑤ KARRも再現します(本当はアンバーですが、私の趣味で緑色)。

パターン⑥ KARR風で、色を徐々に変化させます。

KARRのインジケータをArduinoとWS2812Bで再現
パターン⑤(左)と、パターン⑥(右)

パターン⑦ パターン⑤に加え、3本のインジケーターの間の余っているスペースに光を流します。

f:id:solocamptouring:20180408123033j:plain
f:id:solocamptouring:20180408123048j:plain

 

回路図

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

Fritzingで回路図を作成しました。

マイクモジュールは、MAX4466を搭載したものを使っています。

可変抵抗(ボリューム)を使い、表示7パターンを切り替えられるようにします。

スケッチ(プログラム)は、記事の最後に掲載しておきます。

完成しました!

 まずブレッドボード上で回路を組んで、ipodタッチで出した音をマイクで拾いながら、マイク感度とLED残像調整をしてスケッチを完成させました。

ナイト2000のボイスインジケータをArduinoとWS2812Bで作成

 

完成後の作動状況は動画で確認できます。

7種類の光り方だけ見たい場合は、2:37くらいから確認してみてください。


Arduino project #3 「サウンドレベルメーターをWS2812Bでつくる」Sound level meter with WS2812B

 

 

今回作ったものは、車のオーバーヘッドコンソールに組み込んで光らせますが、その場所には既に、以前の記事で紹介した8x8LEDが搭載されていて、レインボーカラーなどで光っています。

www.solocamptouring.com

 今回のナイトライダー化は、既に搭載されているArduinoにマイクモジュールを追加して、スケッチ(プログラム)を書き換えるだけなので、車での作業はあっという間に終了しました。

f:id:solocamptouring:20180415185857j:plain

スケッチを紹介します

簡単なスケッチ(プログラム)で思い通りの光らせ方ができるGitHubのFastLEDライブラリを使います。

LED光の残像効果は、スケッチ内のfadeToBlackBy()の部分で調整しています。

#include "FastLED.h"
#define DATA_PIN    4
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 8
#define WIDTH 8
#define NUM_LEDS HEIGHT*WIDTH
CRGB leds[NUM_LEDS];
#define BRIGHTNESS  3

const int sampleWindow = 20; // Sample window width in mS 
unsigned int sample;
int volts;
int micPin = 2;
int selectPin = 5;
int val = 0;
int numLedsToLight = 0;
int numLedsSide = 0;

void setup() {
  delay(3000); //delay for recovery
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS); 
}

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { kittOriginal,  kittColor, kittOriginalWithSinelon,  kittColorWithSinelon,  karrOriginal,  karrColor,   karrOriginalWithSinelon };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 96; // rotating "base color" used by many of the patterns
  
void loop()
{
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
 
   // collect data for a while
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(micPin);
      if (sample < 1024)
      {
         if (sample > signalMax)
         {
            signalMax = sample;  // save the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;  // save the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
   volts = (peakToPeak*5/130); // adjusting to the output of the mic 


 val = analogRead(selectPin); //check the volume to choose the pattern
      if (val < 150) {
    gCurrentPatternNumber = 0;
       } else if (val < 300){
    gCurrentPatternNumber = 1;
       } else if (val < 450){
    gCurrentPatternNumber = 2;
       } else if (val < 600){
    gCurrentPatternNumber = 3;
       } else if (val < 750){
    gCurrentPatternNumber = 4;
       } else if (val < 900){
    gCurrentPatternNumber = 5;
       } else {
    gCurrentPatternNumber = 6;      
       }

 
  gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array
  FastLED.show();  // send the 'leds' array out to the actual LED strip

  EVERY_N_MILLISECONDS( 300 ) { gHue++; } // slowly cycle the "base color" through the rainbow
}

void sinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,16,23);
  leds[pos] += CHSV( gHue, 255, 192);
}
void sinelonTwo()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,40,47);
  leds[pos] += CHSV( gHue, 255, 192);
}

void onecolorsinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,16,23);
  leds[pos] += CRGB::MidnightBlue;
}

void onecolorsinelonTwo()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,40,47);
  leds[pos] += CRGB::MidnightBlue;
}

void kittOriginal() 
{
       if(volts < 4){
        numLedsToLight = volts; 
          if(volts < 1){
          numLedsSide = 0; 
          }else{
          numLedsSide = numLedsToLight-1; 
          }
       }else{
        numLedsToLight = 4;
        numLedsSide = 3;      
       }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 4-numLedsSide; led < 4; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 4; led < 4+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 12-numLedsSide; led < 12; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 12; led < 12+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 52-numLedsSide; led < 52; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 52; led < 52+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 60-numLedsSide; led < 60; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 60; led < 60+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
}

void kittColor() 
{
       if(volts < 4){
        numLedsToLight = volts; 
          if(volts < 1){
          numLedsSide = 0; 
          }else{
          numLedsSide = numLedsToLight-1; 
          }
       }else{
        numLedsToLight = 4;
        numLedsSide = 3;      
       }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 4-numLedsSide; led < 4; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 4; led < 4+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 12-numLedsSide; led < 12; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 12; led < 12+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 52-numLedsSide; led < 52; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 52; led < 52+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 60-numLedsSide; led < 60; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 60; led < 60+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
}

void kittOriginalWithSinelon() 
{
  sinelon() ;
  sinelonTwo() ;
  kittOriginal() ;
}

void kittColorWithSinelon() 
{
  onecolorsinelon() ;
  onecolorsinelonTwo() ;
  kittColor()  ;
}
void karrOriginal() 
{
       if(volts < 4){
        numLedsToLight = volts; 
        numLedsSide = volts;  
        }else{
        numLedsToLight = 4;
        numLedsSide = 4;      
        }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 8-numLedsSide; led < 8; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 0; led < 0+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 16-numLedsSide; led < 16; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 8; led < 8+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 56-numLedsSide; led < 56; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 48; led < 48+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 64-numLedsSide; led < 64; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 56; led < 56+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
}
void karrColor() 
{
       if(volts < 4){
        numLedsToLight = volts; 
        numLedsSide = volts;  
        }else{
        numLedsToLight = 4;
        numLedsSide = 4;      
        }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 8-numLedsSide; led < 8; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 0; led < 0+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 16-numLedsSide; led < 16; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 8; led < 8+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 56-numLedsSide; led < 56; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 48; led < 48+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 64-numLedsSide; led < 64; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 56; led < 56+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
}
void karrOriginalWithSinelon() 
{
  sinelon() ;
  sinelonTwo() ;
  karrOriginal() ;
}