まえがき
マルチカラーランプを作成するというアイデアは、生後6ヶ月の息子に点滅するガーランドの魅力的な効果を見た後に思いつきました。 似たようなものを作成したかったのです。たとえば、夜間ランプなど、モードを柔軟に構成できる機能と、リモートで制御できる機能など、いくつかの有用な機能のみを実行したかったのです。 そして、もちろん、作成されたデバイスは、中国の花輪よりも子供にとって魅力的でなければなりません。
注意、猫の下にはたくさんの写真があります。
準備手順
ランプシェードによる光の減光により、夜のスポットライトのように光ることがなく、スペースをほとんどとらず、同時に複数の色を同時に表示できるフロアランプ-フロアランプを作成することを選択しました。 同様の何かがすでにハブロフカニンによって作成されました-それはラズベリーで収集された天気を示すランプでした。 私はインターネットをサーフィンする仕事がありませんでした。「LEDを点滅」させたかったので、アンクルリャオから購入したNanoの小さなバージョンが自宅に横たわっていたので、Arduinkaを脳として取ることにしました。 リモートコントロールの場合-私は、いくつかの常緑樹に最も単純なBolutek bluetoothモジュールを使用することにしました。
次に、実際のランプを作成するという疑問が生じました。 車輪を再発明するのではなく、完成したものを採用することにしました。 最も近い500ルーブルで最も近いイケアで購入した紙製のランプシェードを備えたフロアランプが基本として登場しました。 ロッドの内側で、LEDストリップのステッカー用のいくつかの領域を固定するだけで、明るさの価値がある照明を作成することができます。 カラーテープ、モデル5050、1メートルあたり60個のLEDを、個別のLED制御なしで取りました。
このサイトは、床下暖房用の取り付け用金属テープで作られていました。これはすべての建設店で販売されています。 20mのテープリールで十分です。 直径約14 cmのテープからリングを巻いて、ランプロッドに固定できるようにしました。 同時に、リングはヒートシンクとして機能します-なぜなら テープは非常に顕著に加熱されます。
その後、彼はすべてのレベルの色を独立して管理する方法を考え始めました。arduinoのPWM出力の数は限られています。 選択されたレベルの数8 より大きなランプでは、金属パッドの重量の下で曲がり始めました。 それにもかかわらず、スウェーデンのデザイナーは、ランプロッドに他のものが取り付けられることを期待していませんでした。 したがって、出力電圧をスムーズに調整できる24の出力を制御する必要がありました。 1つのarduinにはそれほど多くの出力がありません(Megaがある場合がありますが、そのような解決策は原始的です)。したがって、この問題を解決するために、ShiftPWMライブラリを使用しました。 安価で手頃なmikruhs(シフトレジスタ74hc595およびuln2803キー)の助けを借りて、必要な数の制御出力とテープを供給するための電流を提供しました。
化身
まず最初に、彼はランプロッドに固定した金属テープの8つのパッドを作成しました。 ストック光源を固定する特性のため、レベル間で同じ距離を維持することはできませんでしたが、これは原理的ではないことが判明しました。
その後、彼はワイヤー用のケーブルを準備し、それを下のレベルに導きました。 コントローラーは、電源の近くにあるランプの下部に配置することにしました。 すべてのケーブル、たむろしないように、私は電気テープをロッドに巻きました:
次に、実際の回路を組み立てる番が来ました。 シフトレジスタ接続スキーム74hc595は、ShiftPWMマニュアルサイトから標準のものを採用しました。
シフトレジスタ接続スキーム
Uln2803接続図
ブレッドボード上に単一LED回路を使用して回路を組み立て、スケッチ例をアップロードしました。すべてが正常に機能することを確認しました。
その後、量産バージョンで回路を構築します。 なぜなら できるだけ早く回路を組み立てたいと思っていましたが、LUTテクノロジーを試す時間はありませんでした。コントローラをサイズ4 x 6 cmの2つのブレッドボードテキソライトボードに組み立てました。 1つ目は、リモートコントロール用のarduinoとbluetoothモジュール、2つ目は、テープを接続するためのレジスタ、キー、およびコネクタです。
それから彼は両方のボードをひねり、一種のサンドイッチが判明した。
電源として-私は、最大5Aの出力電流で、12Vの安価な中国語を使いました。
ケースとして-私はサイズに収まるスイッチボックスを使用しました:
次に-電源をケース内に配置し、回路基板からサンドイッチを配置し、内部のテープからワイヤを延長します。
それはかなりコンパクトなデバイスであることが判明しましたが、ランプシェードの下に隠されて簡単に管理され、注目を集めませんでした。
コントローラーを制御するために、以前にAndroid用のプログラムを作成しました。 一般に、Bluetooth端末は回路の制御に適しています-いくつかの定義済みコマンドを送信用にプログラムし、カスタムバックライトカラーを選択し、RGBカラーコードをR、G、B形式に設定すれば十分です。
次に、記入するスケッチコードを指定します。 このスキームには、10のプリセットモードと、独自の色を設定する機能があります。
スケッチ
/* * ShiftPWM non-blocking RGB fades example, (c) Elco Jacobs, updated August 2012. * * This example for ShiftPWM shows how to control your LED's in a non-blocking way: no delay loops. * This example receives a number from the serial port to set the fading mode. Instead you can also read buttons or sensors. * It uses the millis() function to create fades. The block fades example might be easier to understand, so start there. * * Please go to www.elcojacobs.com/shiftpwm for documentation, fuction reference and schematics. * If you want to use ShiftPWM with LED strips or high power LED's, visit the shop for boards. */ // ShiftPWM uses timer1 by default. To use a different timer, before '#include <ShiftPWM.h>', add // #define SHIFTPWM_USE_TIMER2 // for Arduino Uno and earlier (Atmega328) // #define SHIFTPWM_USE_TIMER3 // for Arduino Micro/Leonardo (Atmega32u4) // Clock and data pins are pins from the hardware SPI, you cannot choose them yourself. // Data pin is MOSI (Uno and earlier: 11, Leonardo: ICSP 4, Mega: 51, Teensy 2.0: 2, Teensy 2.0++: 22) // Clock pin is SCK (Uno and earlier: 13, Leonardo: ICSP 3, Mega: 52, Teensy 2.0: 1, Teensy 2.0++: 21) // You can choose the latch pin yourself. const int ShiftPWM_latchPin=8; // ** uncomment this part to NOT use the SPI port and change the pin numbers. This is 2.5x slower ** // #define SHIFTPWM_NOSPI // const int ShiftPWM_dataPin = 11; // const int ShiftPWM_clockPin = 13; // If your LED's turn on if the pin is low, set this to true, otherwise set it to false. const bool ShiftPWM_invertOutputs = false; // You can enable the option below to shift the PWM phase of each shift register by 8 compared to the previous. // This will slightly increase the interrupt load, but will prevent all PWM signals from becoming high at the same time. // This will be a bit easier on your power supply, because the current peaks are distributed. const bool ShiftPWM_balanceLoad = false; #define rxPin 2 #define txPin 4 #include <SoftwareSerial.h> #include <ShiftPWM.h> // include ShiftPWM.h after setting the pins! // Function prototypes (telling the compiler these functions exist). void oneByOne(void); void inOutTwoLeds(void); void inOutAll(void); void alternatingColors(void); void hueShiftAll(void); void randomColors(void); void fakeVuMeter(void); void rgbLedRainbow(unsigned long cycleTime, int rainbowWidth); void printInstructions(void); void setColor(int r, int g, int b); // Here you set the number of brightness levels, the update frequency and the number of shift registers. // These values affect the load of ShiftPWM. // Choose them wisely and use the PrintInterruptLoad() function to verify your load. unsigned char maxBrightness = 255; unsigned char pwmFrequency = 75; unsigned int numRegisters = 6; unsigned int numOutputs = numRegisters*8; unsigned int numRGBLeds = numRegisters*8/3; unsigned int fadingMode = 0; //start with all LED's off. int r = 0; int g = 0; int b = 0; unsigned long startTime = 0; // start time for the chosen fading mode SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); void setup(){ while(!Serial){ delay(100); } Serial.begin(9600); pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); // Sets the number of 8-bit registers that are used. ShiftPWM.SetAmountOfRegisters(numRegisters); // SetPinGrouping allows flexibility in LED setup. // If your LED's are connected like this: RRRRGGGGBBBBRRRRGGGGBBBB, use SetPinGrouping(4). ShiftPWM.SetPinGrouping(1); //This is the default, but I added here to demonstrate how to use the funtion ShiftPWM.Start(pwmFrequency,maxBrightness); printInstructions(); mySerial.begin(9600); } void loop() { String command = ""; while (mySerial.available() > 0) { char c = mySerial.read(); Serial.println(c); command.concat(c); } command.trim(); if (command == "") { } else { startTime = millis(); } int firstCommaPos = -1; int lastCommaPos = -1; firstCommaPos = command.indexOf(','); lastCommaPos = command.lastIndexOf(','); if (firstCommaPos != -1 && lastCommaPos != -1 && lastCommaPos != firstCommaPos) { String rStr = command.substring(0, firstCommaPos); String gStr = command.substring(firstCommaPos + 1, lastCommaPos); String bStr = command.substring(lastCommaPos + 1); // Serial.println("r is -> " + rStr); // Serial.println("g is -> " + gStr); // Serial.println("b is -> " + bStr); r = rStr.toInt(); g = gStr.toInt(); b = bStr.toInt(); fadingMode = 10; } if (command == "a") fadingMode = 0; if (command == "b") fadingMode = 1; if (command == "c") fadingMode = 2; if (command == "d") fadingMode = 3; if (command == "e") fadingMode = 4; if (command == "f") fadingMode = 5; if (command == "g") fadingMode = 6; if (command == "h") fadingMode = 7; if (command == "i") fadingMode = 8; if (command == "j") fadingMode = 9; Serial.println("command is -> " + command); switch(fadingMode){ case 0: // Turn all LED's off. ShiftPWM.SetAll(0); break; case 1: oneByOne(); break; case 2: inOutAll(); break; case 3: inOutTwoLeds(); break; case 4: alternatingColors(); break; case 5: hueShiftAll(); break; case 6: randomColors(); break; case 7: fakeVuMeter(); break; case 8: rgbLedRainbow(3000,numRGBLeds); break; case 9: rgbLedRainbow(10000,5*numRGBLeds); break; case 10: setColor(r,g,b); break; default: Serial.println("Unknown Mode!"); delay(1000); break; } } void setColor(int r, int g, int b) { ShiftPWM.SetAll(0); ShiftPWM.SetAllRGB(r,g,b); } void oneByOne(void){ // Fade in and fade out all outputs one at a time unsigned char brightness; unsigned long fadeTime = 500; unsigned long loopTime = numOutputs*fadeTime*2; unsigned long time = millis()-startTime; unsigned long timer = time%loopTime; unsigned long currentStep = timer%(fadeTime*2); int activeLED = timer/(fadeTime*2); if(currentStep <= fadeTime ){ brightness = currentStep*maxBrightness/fadeTime; ///fading in } else{ brightness = maxBrightness-(currentStep-fadeTime)*maxBrightness/fadeTime; ///fading out; } ShiftPWM.SetAll(0); ShiftPWM.SetOne(activeLED, brightness); } void inOutTwoLeds(void){ // Fade in and out 2 outputs at a time unsigned long fadeTime = 500; unsigned long loopTime = numOutputs*fadeTime; unsigned long time = millis()-startTime; unsigned long timer = time%loopTime; unsigned long currentStep = timer%fadeTime; int activeLED = timer/fadeTime; unsigned char brightness = currentStep*maxBrightness/fadeTime; ShiftPWM.SetAll(0); ShiftPWM.SetOne((activeLED+1)%numOutputs,brightness); ShiftPWM.SetOne(activeLED,maxBrightness-brightness); } void inOutAll(void){ // Fade in all outputs unsigned char brightness; unsigned long fadeTime = 2000; unsigned long time = millis()-startTime; unsigned long currentStep = time%(fadeTime*2); if(currentStep <= fadeTime ){ brightness = currentStep*maxBrightness/fadeTime; ///fading in } else{ brightness = maxBrightness-(currentStep-fadeTime)*maxBrightness/fadeTime; ///fading out; } ShiftPWM.SetAll(brightness); } void alternatingColors(void){ // Alternate LED's in 6 different colors unsigned long holdTime = 2000; unsigned long time = millis()-startTime; unsigned long shift = (time/holdTime)%6; for(unsigned int led=0; led<numRGBLeds; led++){ switch((led+shift)%6){ case 0: ShiftPWM.SetRGB(led,255,0,0); // red break; case 1: ShiftPWM.SetRGB(led,0,255,0); // green break; case 2: ShiftPWM.SetRGB(led,0,0,255); // blue break; case 3: ShiftPWM.SetRGB(led,255,128,0); // orange break; case 4: ShiftPWM.SetRGB(led,0,255,255); // turqoise break; case 5: ShiftPWM.SetRGB(led,255,0,255); // purple break; } } } void hueShiftAll(void){ // Hue shift all LED's unsigned long cycleTime = 10000; unsigned long time = millis()-startTime; unsigned long hue = (360*time/cycleTime)%360; ShiftPWM.SetAllHSV(hue, 255, 255); } void randomColors(void){ // Update random LED to random color. Funky! unsigned long updateDelay = 100; static unsigned long previousUpdateTime; if(millis()-previousUpdateTime > updateDelay){ previousUpdateTime = millis(); ShiftPWM.SetHSV(random(numRGBLeds),random(360),255,255); } } void fakeVuMeter(void){ // imitate a VU meter static unsigned int peak = 0; static unsigned int prevPeak = 0; static unsigned long currentLevel = 0; static unsigned long fadeStartTime = startTime; unsigned long fadeTime = (currentLevel*2);// go slower near the top unsigned long time = millis()-fadeStartTime; currentLevel = time%(fadeTime); if(currentLevel==peak){ // get a new peak value prevPeak = peak; while(abs(peak-prevPeak)<5){ peak = random(numRGBLeds); // pick a new peak value that differs at least 5 from previous peak } } if(millis() - fadeStartTime > fadeTime){ fadeStartTime = millis(); if(currentLevel<peak){ //fading in currentLevel++; } else{ //fading out currentLevel--; } } // animate to new top for(unsigned int led=0;led<numRGBLeds;led++){ if(led<currentLevel){ int hue = (numRGBLeds-1-led)*120/numRGBLeds; // From green to red ShiftPWM.SetHSV(led,hue,255,255); } else if(led==currentLevel){ int hue = (numRGBLeds-1-led)*120/numRGBLeds; // From green to red int value; if(currentLevel<peak){ //fading in value = time*255/fadeTime; } else{ //fading out value = 255-time*255/fadeTime; } ShiftPWM.SetHSV(led,hue,255,value); } else{ ShiftPWM.SetRGB(led,0,0,0); } } } void rgbLedRainbow(unsigned long cycleTime, int rainbowWidth){ // Displays a rainbow spread over a few LED's (numRGBLeds), which shifts in hue. // The rainbow can be wider then the real number of LED's. unsigned long time = millis()-startTime; unsigned long colorShift = (360*time/cycleTime)%360; // this color shift is like the hue slider in Photoshop. for(unsigned int led=0;led<numRGBLeds;led++){ // loop over all LED's int hue = ((led)*360/(rainbowWidth-1)+colorShift)%360; // Set hue from 0 to 360 from first to last led and shift the hue ShiftPWM.SetHSV(led, hue, 255, 255); // write the HSV values, with saturation and value at maximum } } void printInstructions(void){ Serial.println("---- ShiftPWM Non-blocking fades demo ----"); Serial.println(""); Serial.println("Type 'l' to see the load of the ShiftPWM interrupt (the % of CPU time the AVR is busy with ShiftPWM)"); Serial.println(""); Serial.println("Type any of these numbers to set the demo to this mode:"); Serial.println(" 0. All LED's off"); Serial.println(" 1. Fade in and out one by one"); Serial.println(" 2. Fade in and out all LED's"); Serial.println(" 3. Fade in and out 2 LED's in parallel"); Serial.println(" 4. Alternating LED's in 6 different colors"); Serial.println(" 5. Hue shift all LED's"); Serial.println(" 6. Setting random LED's to random color"); Serial.println(" 7. Fake a VU meter"); Serial.println(" 8. Display a color shifting rainbow as wide as the LED's"); Serial.println(" 9. Display a color shifting rainbow wider than the LED's"); Serial.println(""); Serial.println("Type 'm' to see this info again"); Serial.println(""); Serial.println("----"); }
ビデオはレインボーモードを示しています、私見で最も美しい操作モード:
そして、完成したデバイスの写真を2枚-カメラが色の豊かさを伝えることができないのは残念です。
まとめ
組み立てられたランプは、元のアイデアを完全に満たし、完成品のように見えます。ワイヤーは突き出ておらず、操作が簡単で、息子は満足しています-これは重要です!
コントローラーのすべてのコンポーネントはEbeeで購入しました。誰でもそこで見つけることができます-価格は最小限です。 ランプの総コストはおよそ判明しました:500rランプ自体、800r-LEDストリップ用、100r-電源、マイクロ回路-50r、200r-ブルートゥース、固定用ハウジングおよびスチールテープ-200r 合計-約1900r。
時間内に、夕方の自由時間で約2週間かかりました。 気が散らされなければ、きっとあなたはより速くそれをすることができます。
他にできること:
- ブレッドボードの代わりにLUTを使用する
- 電話のソフトウェアを変更します-エフェクトの明るさ/速度などを調整できるように
- Adruinkiの代わりに、Raspberryを使用してください。そして、自動的に照明をオンにし、既存のモードを手動で制御したり、ファームウェアをリロードせずに新しいモードを作成したりできます。
- 5050テープの代わりに、ws2811テープを使用してください。レベル全体だけでなく、個々のLEDも制御できます。
- 現在、ストックライトはマイクロコントローラーに接続されていませんが、リレーを配置して、電話からリモートで操作することができます
- 拍手で参加する-参加のためにプログラムに毎回登らないように
- ...
- さらに多くの調整
コメントでデザインを改善するためのコメント、批判、およびアイデアを見てうれしいです。