ソロでたのしむ

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

炎も躍るサウンド レベル メーター。IKEAのフロアランプをカラフルに改造

炎のフロアランプを自作しよう

今回は、お家でキャンプ気分シリーズの、スピンオフ編です。

焚き火を眺めてすごす、キャンプでの優雅な時間を、屋内でも楽しみたい!ということで、背が高いフロアランプを、カラーLEDを使って改造して、焚き火台風にしてみます。

 

炎が躍るサウンド レベル メーター。WS2812Bでファイヤーします

焚き火とサウンドレベルメーター

アウトドアを楽しめない週末が続くと、いろいろな思いがこみ上げてきます。

  • 殺風景な部屋に、フロアランプが欲しい!
  • キャンプに行けないのであれば、部屋で焚き火気分を楽しめないかな?
  • 外出できないのであれば、しばらく封印していたDIYでもやってみようか...

こんな妄想たちを、一気に実現するには、焚き火風のフロアランプを自作するしかない!と確信しました。

テーマパークの仕掛けで見かける、本物の炎のような、フェイクな焚き火です。

ファンの風によって布をチラつかせて、さらに光も当てることで、本物の焚き火と錯覚してしまう、あれですね。

購入した方が安くすみそうな気がしますが、自作もできそうな気がします。

 

しかし、構造を考えていて、音について悩みが生じます。

くつろぎの時間を過ごしたいところで、ファンの音に耐えられるだろうか?と。

テーマパークのような、他の音が存在する場所なら気にならないと思いますが、プライベートな部屋ではファンの音は雑音でしかありません。

ファンを使わずに炎のような光を実現するには、小さな光源がたくさん必要になりそう...と考えていて、それならLEDで表現してみましょう!と、コンセプトがみえてきました。

LEDの直接光では、炎のように見せるのは難しいと思われるので、日本の伝統的なフロアライト、行燈の中にLEDたちを閉じ込めてしまいます。

行燈の中に焚き火があっても、いいじゃない!

行燈をサウンド レベル メーターに!

不規則に揺らぐ焚き火の光は、見ていて飽きませんよね〜

あの揺らぎを人為的に表現するには、乱数発生プログラムを使ってLEDを不規則に光らせることが近道だと思いました。

...でも、それだけだと不規則すぎて面白くない気もします。

相変わらずの捻くれ者ぶりを発揮してしまったので、一週間ほど悩んでみました。

時間をかければ閃くこともあるようで、ふと音楽に合わせて光らせてみたら面白いのでは?と思いつきます。

行燈の中で、炎と、音楽に合わせて光が踊っているフロアーランプ!

これは欲しい!と納得できるアイデアが浮かんだので、さっそく制作に取り掛かります。

IKEAで行燈選び

アウトドアには行けない日が続いているものの、家具屋さんのIKEAが家の近くにあり、毎週末のように通って、気分転換をしています。

そんなIKEAには、今回の行燈サウンド レベル メーターの改造ベースにピッタリのフロアランプがあることを知っています。

IKEAのフロアランプに、カラフルなLEDを仕込もう!

フードの材質が紙なので、光の指向性があるLEDとの相性もバッチリです。

いつもIKEAでは散歩スピードでショールームを楽しんでいますが、今回は売り場に直行して、フロアランプを入手してきました。

Arduinoと接続するモジュール

フロアランプを、行燈風サウンド レベル メーターとして光らせるために、個別に制御できるRGB LEDのWS2812Bをを使います。

「〇〇番目のLEDさんは、△△色に光ってください〜」

という情報さえ流せば、その通りに光るので、多くのLEDを個別に光り方を変えるのに便利なものです。

1mあたりにWS2812Bが60m個並べられているカラーLEDテープを3mと、サウンドセンサーモジュール、そして可変抵抗(ボリューム)3個を入手しました。

以前に、可変抵抗1個を使ってナイトライダー風サウンド レベル メーターを作ったことがありますが、今回はフロアランプでもあるので、

  • 光量
  • 光らせ方

の3項目を可変抵抗で選択できるように進化させます。

表示イメージとパターン

もともとは、「焚火 in 行燈」コンセプトで始めたプロジェクトですが、純粋にサウンド レベル メーター風のパターンも含めて、合計7種類の光らせ方を準備します。

 行燈なので、まずは白色で光らせます。

写真は紫色のように写っていますが、実際には白色です。

WS2812Bで行燈を改造。(白色)

 サウンドレベルのバーが立ちます。色は炎のように、オレンジのグラデーションです。

WS2812Bで行燈を改造。(サウンド レベル メーター 赤色)

 サウンドレベルのバーが立ちます。色は可変抵抗で選択した単色です。

WS2812Bで行燈を改造。(サウンド レベル メーター 好きな色)

 サウンドレベルのバーが立ちます。色は徐々に変化します。

WS2812Bで行燈を改造。(サウンド レベル メーター 色は徐々に変化)

 焚き火風に光らせます。色は、「オレンジ白色」の3色です。

WS2812Bで行燈を改造。(ファイヤー 赤色)

 焚き火風に光らせます。色は、「水色白色」の3色です。

WS2812Bで行燈を改造。(ファイヤー 青色)

 焚き火風に光らせます。色は、「可変抵抗で選択した色白色」の2色です。

WS2812Bで行燈を改造。(ファイヤー 好きな色)

 焚き火風に光らせます。色は、「徐々に変化する白色」の2色です。

WS2812Bで行燈を改造。(ファイヤー 徐々に変化する色)

回路図

Arduinoで自作するサウンド レベル メーターの回路図

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

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

3個の可変抵抗(ボリューム)は、

  • R1;光量
  • R2;表示7パターンの切り替え
  • R3;色

の調節(選択)をするために使います。

Arduinoを使って、LEDを炎のパターンで光らせる

Arduinoのアナログ入力には、それぞれの可変抵抗から電圧で情報が送られます。

  • 電圧; 0〜5V
  • Arduinoの読み取り値; 0〜1023(整数)

0〜1023をArduinoのスケッチ(プログラム)で選択肢に置き換えれば、LEDの発光を調節できます。

今回は180個が繋がっているものを使うWS2812Bは、先程も簡単に紹介しましたが、それぞれのLEDが個別に信号処理して、色や光量を認識して発光するので、信号線1本と電源の、合計3本の電線を繋ぐだけで、自在に光を制御できる優れものです。

WS2812Bで、炎のランプを!

スケッチは、記事の最後に掲載しておきますね。

炎が踊るフロアーランプ

LEDの配置

LEDを、ランプ中心部の柱にどう配置するか?について、少し試行錯誤しました。

  • 螺旋状に巻く?
  • 縦の列状に配置する?

結論から言うと、縦に配置した方が、LEDの貼り付け作業も、炎やサウンドレベルメーターのスケッチ(プログラム)の作成も楽です。

私は4列で配置することにしました。

WS2812B ファイヤー

180個のWS2812Bテープなので、

「45行x4列 」

になる筈ですが、試行錯誤の段階でちょっとした事故があり、数セルが使えなくなってしまったので、やむなく43x 4の配置になりました。

ウエルカム点灯

電源投入時の動作は、3種類を仕込んでおきます。

  1. ランダム点滅(白色)

    f:id:solocamptouring:20210626123315j:plain

  2. 白色光を上下に一回走らせる

    f:id:solocamptouring:20210626123321j:plain

  3. レインボー色の光を上下に一回走らせる

    f:id:solocamptouring:20210626123435j:plain

静止画だと、どれだけ綺麗に光っているか?が伝わらないですが、光り方はメイキング動画でも、ご確認いただけます。

 

youtu.be

スケッチ(プログラム)

スケッチは、色々なパターンの光らせ方が準備されている、GitHubのFastLEDライブラリをベースにさせていただきます。

炎(fire)パターンは、派生バージョンも含めていくつか紹介されており、Fire2012WithPaletteの、炎を作成する部分のスケッチを使用させていただき、4列の炎専用に少し手を加えました。

もっと短いスケッチにできると思いますが、メモリー容量に余裕があるので良い!ということにして、同じ命令文が何度もでてきます。

#include "FastLED.h"
#define DATA_PIN    5
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 43
#define WIDTH 4
#define NUM_LEDS HEIGHT*WIDTH
#define MAX_BRIGHTNESS 90      // Full is 164
#define MIN_BRIGHTNESS 5       // 25% is better
#define lighting_PER_SECOND 30 // Mainly for fire frame pattern

CRGB leds[NUM_LEDS];
CRGBPalette16 gPal;

const int sampleWindow = 50; // Sound sample window width in mS
unsigned int sample;
int SoundCenterAdjust = 0; // max 1024/2
int SoundLevelAdjust = 6; // defo and min 1 In case bar is small with the usual sound
int BRIGHTNESS = 50;
int scaleVol = 230; // Scale the heat value from 0-255
int SPARKING = 130; //50-200 out of 255
int COOLING = 70; // 20-100: Less = taller flames.  More = shorter flames. 
int colorSelect;
int micPin = 2;
int blightPin = 3;
int selectPin = 4;
int colorPin = 5;
int val = 0;
int numLedsToLight = 0;
long numLedsToLightCal = 0;
bool welcomeLight = true;
bool gReverseDirection = false;


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
typedef void (*SimplePatternList[])();

SimplePatternList gPatterns = { allwhite, soundBarFire, soundBarSelect, soundBarRotate, fireRed, fireBlue, fireSelect, fireRotate};

uint8_t gCurrentPatternNumber = 0;  // Index number of which pattern is current
uint8_t gHue = 96;                  // rotating "base color" used by many of the patterns 0to255
// End of the list of patterns to cycle through


void loop()
{
// Data collection
  // Choose the blightness
   if (analogRead(blightPin) < 50) {
    BRIGHTNESS = 0 ;
   } else {
    BRIGHTNESS = map(analogRead(blightPin), 50, 1024, MIN_BRIGHTNESS, MAX_BRIGHTNESS) ;
   } 
    FastLED.setBrightness(BRIGHTNESS); 

  // Choose the color
   if (analogRead(colorPin) < 50) {
    colorSelect = 0 ;
   } else if (analogRead(colorPin) < 950) {
    colorSelect = map(analogRead(colorPin), 50, 949, 0, 255) ;
   } else {
    colorSelect = 255;
   }

  // Choose the pattern
   val = analogRead(selectPin); //check the volume to choose the pattern
      if (val < 130) {
    gCurrentPatternNumber = 0;
       } else if (val < 260){
    gCurrentPatternNumber = 1;
       } else if (val < 390){
    gCurrentPatternNumber = 2;
       } else if (val < 520){
    gCurrentPatternNumber = 3;
       } else if (val < 650){
    gCurrentPatternNumber = 4;
       } else if (val < 780){
    gCurrentPatternNumber = 5;
       } else if (val < 910){
    gCurrentPatternNumber = 6;
       } else {
    gCurrentPatternNumber = 7;      
       }
    gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array

// End of data collection


// Welcome lighting
  if (welcomeLight == true) {
  // random speckles that blink in and fade smoothly
    for(int led = 0; led < 150; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    int pos = random16(NUM_LEDS);
    leds[pos] += CRGB::White;
    FastLED.show();  
    delay (100-led/2);
    }  
    
  // white up and down
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[led] += CRGB::White ; 
    leds[HEIGHT*2-1-led] += CRGB::White ; 
    leds[HEIGHT*2+led] += CRGB::White ; 
    leds[HEIGHT*4-1-led] += CRGB::White ; 
    FastLED.show();    
    delay (80);
    }

    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[HEIGHT-1-led] += CRGB::White ; 
    leds[HEIGHT+led] += CRGB::White ; 
    leds[HEIGHT*3-1-led] += CRGB::White ; 
    leds[HEIGHT*3+led] += CRGB::White ; 
    FastLED.show();    
    delay (80);
    }
  
  // rainbow up and down
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*2-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*2+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*4-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    FastLED.show();    
    delay (80);
     }
 
    for(int led = 0; led < HEIGHT; led++) { 
    fadeToBlackBy( leds, NUM_LEDS, 100);
    leds[HEIGHT-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*3-1-led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    leds[HEIGHT*3+led] = CHSV(255*led/HEIGHT, 255, 255) ; 
    FastLED.show();    
    delay (80);
    }    
    welcomeLight = false;  //End welcome lighting
  }else{

// Let's light them now
  random16_add_entropy( random());
  gPatterns[gCurrentPatternNumber]();
  FastLED.show();  // send the 'leds' array out to the actual LED strip
  FastLED.delay(1000 / lighting_PER_SECOND);
  EVERY_N_MILLISECONDS( 300 ) { gHue++; } // slowly cycle the color through the rainbow
// End of lighting
    }  
}


void fireRed() 
{
  gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireRotate() 
{
  gPal = CRGBPalette16( CRGB::Black, CHSV( gHue, 255, 192), CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireBlue() 
{
  gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua,  CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void fireSelect() 
{
  gPal = CRGBPalette16( CRGB::Black, CHSV( colorSelect, 255, 192), CRGB::White);
  static uint8_t heat[NUM_LEDS]; // Array of temperature readings

  // Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / HEIGHT) + 2)); //heat-randam8 with a floor of 0
    }
  
  // Heat from each cell drifts 'up' and diffuses a little
    for( int k= HEIGHT - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
      heat[2*HEIGHT-1-k] = (heat[2*HEIGHT-1-k + 1] + heat[2*HEIGHT-1-k + 2] + heat[2*HEIGHT-1-k + 2] ) / 3;
      heat[2*HEIGHT+k] = (heat[2*HEIGHT+k - 1] + heat[2*HEIGHT+k - 2] + heat[2*HEIGHT+k - 2] ) / 3;
      heat[4*HEIGHT-1-k] = (heat[4*HEIGHT-1-k + 1] + heat[4*HEIGHT-1-k + 2] + heat[4*HEIGHT-1-k + 2] ) / 3;
    }
    
    //  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int x = random8(19);
      int y ;
      if(x > 14){
        y = 4*HEIGHT+14-x;
      } else if(x > 9){
        y = 2*HEIGHT-10+x;
      } else if(x > 4){
        y = 2*HEIGHT+4-x;
      } else {
        y = x;
      }
      heat[y] = qadd8( heat[y], random8(160,255) ); //heat+randam8
    }

    // Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      uint8_t colorindex = scale8( heat[j], scaleVol);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
       if( gReverseDirection ) {
        if(j < HEIGHT){
          pixelnumber = (HEIGHT-1) - j;
          }else if(j < 2*HEIGHT){
          pixelnumber = HEIGHT+j;
          }else if(j < 3*HEIGHT){
          pixelnumber = (3*HEIGHT-1) - j;
          }else{
          pixelnumber = 3*HEIGHT+j;
          }
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

void soundBarFire() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   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
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);

  for(int led = 0; led < numLedsToLight; led++) { 
  leds[led] += CHSV( 2*(numLedsToLight-1-led), 255, 192);
  }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++) { 
  leds[led] += CHSV( 2*(led-(HEIGHT*2-numLedsToLight)), 255, 192);
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
  leds[led] += CHSV( 2*(HEIGHT*2+numLedsToLight-1-led), 255, 192);
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
  leds[led] += CHSV( 2*(led-(HEIGHT*4-numLedsToLight)), 255, 192);
  }
}

void soundBarSelect() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   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
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);


  for(int led = 0; led < numLedsToLight; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
  leds[led] += CHSV( colorSelect, 255, 192);
  }
}

void soundBarRotate() 
{

  // Sound level to lighting LED amount
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   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
   numLedsToLightCal = ((peakToPeak-SoundCenterAdjust)*HEIGHT*SoundLevelAdjust)/1024 ;
   if(numLedsToLightCal >= HEIGHT){
    numLedsToLight = HEIGHT;
   }else {
   numLedsToLight = numLedsToLightCal; // adjusting to the output of the mic 
   }
    fadeToBlackBy( leds, NUM_LEDS, 200);

   for(int led = 0; led < numLedsToLight; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
   }
  for(int led = HEIGHT*2-numLedsToLight; led < HEIGHT*2; led++)  { 
   leds[led] += CHSV( gHue, 255, 192); 
  }
  for(int led = HEIGHT*2; led < HEIGHT*2+numLedsToLight; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
  }
  for(int led = HEIGHT*4-numLedsToLight; led < HEIGHT*4; led++) { 
   leds[led] += CHSV( gHue, 255, 192); 
  } 
}

void allwhite() 
{
  // all led white color
  fill_solid(leds, NUM_LEDS, CRGB::White); 
}

 

今回は、フロアランプに焚火を仕込みましょう!というコンセプトで、お家ですごす時間を楽しくしてみました。

毎週末を使って、製作期間は一か月ほどかかりましたが、初めて見る人には「フロアランプが燃えている!」と思わせそうな完成度で、満足しています。

WS2812Bでファイヤーパターンのフロアライト

室内で焚火はできないアパート暮らしですが、お部屋に焚き火風の光があると、本当にキャンプしているような気分になって、うっとりと見入っています。