前段時間聽音樂以爲無聊,便想着音樂光聽也沒意思啊,能不能 「看見」 音樂呢?因而谷歌了一番,發現還真有人作了將音樂可視化的東西,那就是音樂節奏燈。說的簡單點就是LED燈光顏色亮度等隨着音樂的節奏而發生變化,看了下他們的實現方法有不少,不過大都比較複雜,並且燈只可以作節奏燈也比較浪費,因而我便動手作了一個既能夠看成普通檯燈使用,又能夠隨着音樂而閃爍的動感節奏燈,一箭雙鵰。html
工欲善其事,必先利其器。那麼作這樣一個音樂動感節奏等須要準備哪些材料呢?react
以上材料能夠在淘寶買到,燈罩和3D打印的底座能夠按照本身的實際需求來進行本身選擇,爲了防止打廣告的嫌疑我就不放購買連接了,能夠自行設計打印~web
準備好上述的材料以後,咱們就能夠開始進行燈的製做啦~,節奏燈的主要結構以下圖:bootstrap
咱們使用Arduino UNO做爲主要的計算和處理模塊,藍牙4.0模塊和手機進行通訊,利用手機APP來選擇模式(後續會講),在節奏燈的模式下,經過聲音傳感器來採集聲音,經過獲得的聲音來控制燈帶的顏色和閃爍,在彩色燈的模式下,利用手機來控制燈的顏色,理論上的有160萬可調顏色。接下來介紹下詳細的步驟。app
Arduino UNO 開發的環境爲Arduino IDE,軟件下載地址爲 https://www.arduino.cn/thread-5838-1-1.html ,默認安裝爲最新版便可。安裝完IDE以後還須要安裝第三方的庫。ide
1) WS2812B的庫 FastLED, 選擇 項目->加載庫->管理庫,而後在輸入欄輸入FastLED,選擇最新的版本安裝,FastLED庫的更多使用方法能夠參考 http://www.taichi-maker.com/homepage/reference-index/arduino-library-index/fastled-library/oop
2) 物聯網開發平臺庫 Blinker, 在Blinker 官網https://doc.blinker.app 頁面下載最新的Blinker庫,而後:post
Window:將下載好的blinker庫解壓到 個人電腦>文檔>Arduino>libraries 文件夾中
Mac OS:將下載好的blinker庫解壓到 文稿>Arduino>libraries 文件夾中網站
能夠在文件->示例 查看庫是否安裝成功。ui
具體的線路圖因爲時間緣由沒有畫,因此直接按照後續的代碼能夠找到每一個模塊的鏈接方式,固然這些鏈接方式均可以自定義,而後在代碼內作簡單修改便可。須要注意的是藍牙模塊採用軟串口鏈接,即RX,TX鏈接在ARDUINO UNO的2 和 3 號腳,而不是0和1 號腳,這個在BLINKER的網站上會說明,若是你代碼燒錄不了,查查是否是這個緣由。
1 #define BLINKER_PRINT Serial 2 #define BLINKER_BLE 3 4 #include <FastLED.h> 5 #include <Blinker.h> 6 7 /** BASIC CONFIGURATION **/ 8 9 //The amount of LEDs in the setup 10 #define NUM_LEDS 100 11 //The pin that controls the LEDs 12 #define LED_PIN 6 13 //The pin that we read sensor values form 14 #define ANALOG_READ 0 15 16 //Confirmed microphone low value, and max value 17 #define MIC_LOW 0.0 18 #define MIC_HIGH 300.0 19 /** Other macros */ 20 //How many previous sensor values effects the operating average? 21 #define AVGLEN 5 22 //How many previous sensor values decides if we are on a peak/HIGH (e.g. in a song) 23 #define LONG_SECTOR 20 24 25 //Mneumonics 26 #define HIGH 3 27 #define NORMAL 2 28 29 //How long do we keep the "current average" sound, before restarting the measuring 30 #define CYCLES 30 * 1000 31 32 33 float fscale( float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve); 34 void insert(int val, int *avgs, int len); 35 int compute_average(int *avgs, int len); 36 void visualize_music(); 37 38 //How many LEDs to we display 39 int curshow = NUM_LEDS; 40 41 /*Not really used yet. Thought to be able to switch between sound reactive 42 mode, and general gradient pulsing/static color*/ 43 int mode = 0; 44 45 //Showing different colors based on the mode. 46 int songmode = NORMAL; 47 48 //Average sound measurement the last CYCLES 49 unsigned long song_avg; 50 51 //The amount of iterations since the song_avg was reset 52 int iter = 0; 53 54 //The speed the LEDs fade to black if not relit 55 float fade_scale = 1.2; 56 57 //Led array 58 CRGB leds[NUM_LEDS]; 59 60 /*Short sound avg used to "normalize" the input values. 61 We use the short average instead of using the sensor input directly */ 62 int avgs[AVGLEN] = {-1}; 63 64 //Longer sound avg 65 int long_avg[LONG_SECTOR] = {-1}; 66 67 68 // LED Model 1/Music LED 2/Color LED 69 int LED_Model = 2; 70 71 //Keeping track how often, and how long times we hit a certain mode 72 struct time_keeping { 73 unsigned long times_start; 74 short times; 75 }; 76 77 //How much to increment or decrement each color every cycle 78 struct color { 79 int r; 80 int g; 81 int b; 82 }; 83 84 struct time_keeping high; 85 struct color Color; 86 // when you use the MusicLED as a Color LED 87 CRGB LEDColor(0,0,0); 88 uint8_t Bright = 255; 89 90 // declare the button 91 BlinkerRGB RGB1("RGBKey"); 92 BlinkerButton Button1("switch"); 93 94 95 // rgb1_callback 96 void rgb1_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value) 97 { 98 // change the color of strip by your set on Blinker 99 LEDColor.r = r_value; 100 LEDColor.g = g_value; 101 LEDColor.b = b_value; 102 Bright = bright_value; 103 fill_solid(leds,NUM_LEDS,LEDColor); 104 105 FastLED.show(); 106 } 107 108 void button1_callback(const String & state) { 109 if(LED_Model == 1) 110 { 111 LEDColor.r = 255; 112 LEDColor.g = 255; 113 LEDColor.b = 255; 114 Bright = 0; 115 fill_solid(leds,NUM_LEDS,LEDColor); 116 BLINKER_LOG2("strip_state: ","OFF"); 117 LED_Model = 2; 118 } 119 else if(LED_Model == 2) 120 { 121 LED_Model = 1; 122 } 123 FastLED.show(); 124 } 125 126 127 void setup() { 128 Serial.begin(9600); 129 //Set all lights to make sure all are working as expected 130 FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS); 131 for (int i = 0; i < NUM_LEDS; i++) 132 leds[i] = CRGB(0, 0, 255); 133 FastLED.show(); 134 delay(1000); 135 136 //bootstrap average with some low values 137 for (int i = 0; i < AVGLEN; i++) { 138 insert(250, avgs, AVGLEN); 139 } 140 141 //Initial values 142 high.times = 0; 143 high.times_start = millis(); 144 Color.r = 0; 145 Color.g = 0; 146 Color.b = 1; 147 148 Blinker.begin(); 149 //attach the RGB1 SlidersRGB 150 RGB1.attach(rgb1_callback); 151 Button1.attach(button1_callback); 152 } 153 154 /*With this we can change the mode if we want to implement a general 155 lamp feature, with for instance general pulsing. Maybe if the 156 sound is low for a while? */ 157 void loop() { 158 Blinker.run(); 159 160 if(LED_Model == 1) 161 visualize_music(); 162 163 delay(1); // delay in between reads for stability 164 } 165 166 167 /**Funtion to check if the lamp should either enter a HIGH mode, 168 or revert to NORMAL if already in HIGH. If the sensors report values 169 that are higher than 1.1 times the average values, and this has happened 170 more than 30 times the last few milliseconds, it will enter HIGH mode. 171 TODO: Not very well written, remove hardcoded values, and make it more 172 reusable and configurable. */ 173 void check_high(int avg) { 174 if (avg > (song_avg/iter * 1.1)) { 175 if (high.times != 0) { 176 if (millis() - high.times_start > 200.0) { 177 high.times = 0; 178 songmode = NORMAL; 179 } else { 180 high.times_start = millis(); 181 high.times++; 182 } 183 } else { 184 high.times++; 185 high.times_start = millis(); 186 187 } 188 } 189 if (high.times > 30 && millis() - high.times_start < 50.0) 190 songmode = HIGH; 191 else if (millis() - high.times_start > 200) { 192 high.times = 0; 193 songmode = NORMAL; 194 } 195 } 196 197 //Main function for visualizing the sounds in the lamp 198 void visualize_music() { 199 int sensor_value, mapped, avg, longavg; 200 201 //Actual sensor value 202 sensor_value = analogRead(ANALOG_READ); 203 Serial.println(sensor_value); 204 205 //If 0, discard immediately. Probably not right and save CPU. 206 if (sensor_value == 0) 207 return; 208 209 //Discard readings that deviates too much from the past avg. 210 mapped = (float)fscale(MIC_LOW, MIC_HIGH, MIC_LOW, (float)MIC_HIGH, (float)sensor_value, 2.0); 211 avg = compute_average(avgs, AVGLEN); 212 213 if (((avg - mapped) > avg*0.8)) //|| ((avg - mapped) < -avg*0.8)) 214 return; 215 216 //Insert new avg. values 217 insert(mapped, avgs, AVGLEN); 218 insert(avg, long_avg, LONG_SECTOR); 219 220 //Compute the "song average" sensor value 221 song_avg += avg; 222 iter++; 223 if (iter > CYCLES) { 224 song_avg = song_avg / iter; 225 iter = 1; 226 } 227 228 longavg = compute_average(long_avg, LONG_SECTOR); 229 230 //Check if we enter HIGH mode 231 check_high(longavg); 232 233 if (songmode == HIGH) { 234 fade_scale = 3; 235 Color.r = 5; 236 Color.g = 3; 237 Color.b = -1; 238 } 239 else if (songmode == NORMAL) { 240 fade_scale = 2; 241 Color.r = -1; 242 Color.b = 2; 243 Color.g = 1; 244 } 245 246 //Decides how many of the LEDs will be lit 247 curshow = fscale(MIC_LOW, MIC_HIGH, 0.0, (float)NUM_LEDS, (float)avg, -1); 248 249 /*Set the different leds. Control for too high and too low values. 250 Fun thing to try: Dont account for overflow in one direction, 251 some interesting light effects appear! */ 252 for (int i = 0; i < NUM_LEDS; i++) 253 //The leds we want to show 254 if (i < curshow) { 255 if (leds[i].r + Color.r > 255) 256 leds[i].r = 255; 257 else if (leds[i].r + Color.r < 0) 258 leds[i].r = 0; 259 else 260 leds[i].r = leds[i].r + Color.r; 261 262 if (leds[i].g + Color.g > 255) 263 leds[i].g = 255; 264 else if (leds[i].g + Color.g < 0) 265 leds[i].g = 0; 266 else 267 leds[i].g = leds[i].g + Color.g; 268 269 if (leds[i].b + Color.b > 255) 270 leds[i].b = 255; 271 else if (leds[i].b + Color.b < 0) 272 leds[i].b = 0; 273 else 274 leds[i].b = leds[i].b + Color.b; 275 276 //All the other LEDs begin their fading journey to eventual total darkness 277 } else { 278 leds[i] = CRGB(leds[i].r/fade_scale, leds[i].g/fade_scale, leds[i].b/fade_scale); 279 } 280 FastLED.show(); 281 } 282 //Compute average of a int array, given the starting pointer and the length 283 int compute_average(int *avgs, int len) { 284 int sum = 0; 285 for (int i = 0; i < len; i++) 286 sum += avgs[i]; 287 288 return (int)(sum / len); 289 290 } 291 292 //Insert a value into an array, and shift it down removing 293 //the first value if array already full 294 void insert(int val, int *avgs, int len) { 295 for (int i = 0; i < len; i++) { 296 if (avgs[i] == -1) { 297 avgs[i] = val; 298 return; 299 } 300 } 301 302 for (int i = 1; i < len; i++) { 303 avgs[i - 1] = avgs[i]; 304 } 305 avgs[len - 1] = val; 306 } 307 308 //Function imported from the arduino website. 309 //Basically map, but with a curve on the scale (can be non-uniform). 310 float fscale( float originalMin, float originalMax, float newBegin, float 311 newEnd, float inputValue, float curve){ 312 313 float OriginalRange = 0; 314 float NewRange = 0; 315 float zeroRefCurVal = 0; 316 float normalizedCurVal = 0; 317 float rangedValue = 0; 318 boolean invFlag = 0; 319 320 321 // condition curve parameter 322 // limit range 323 324 if (curve > 10) curve = 10; 325 if (curve < -10) curve = -10; 326 327 curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 328 curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function 329 330 // Check for out of range inputValues 331 if (inputValue < originalMin) { 332 inputValue = originalMin; 333 } 334 if (inputValue > originalMax) { 335 inputValue = originalMax; 336 } 337 338 // Zero Refference the values 339 OriginalRange = originalMax - originalMin; 340 341 if (newEnd > newBegin){ 342 NewRange = newEnd - newBegin; 343 } 344 else 345 { 346 NewRange = newBegin - newEnd; 347 invFlag = 1; 348 } 349 350 zeroRefCurVal = inputValue - originalMin; 351 normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float 352 353 // Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine 354 if (originalMin > originalMax ) { 355 return 0; 356 } 357 358 if (invFlag == 0){ 359 rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin; 360 361 } 362 else // invert the ranges 363 { 364 rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange); 365 } 366 367 return rangedValue; 368 }
上述代碼編譯無誤後上傳到Arduino UNO便可。
Blinker軟件的安裝包能夠在官網上找到:
安裝好以後註冊帳號,登錄,在主頁面右上角選擇-->添加設備-->Arduino-->藍牙接入,而後會自動發現咱們的藍牙,鏈接上以後,如圖。
上面表示是否已經鏈接,如今表示已鏈接。而後添加上面的兩個組件,編輯組件參數以下:
完成以後即可以經過RGB滑條來控制顏色,經過模式按鈕來控制燈的模式。
我是本身在網上買的燈罩,而後根據本身的需求3D打印的底座,你們能夠發揮本身的想象力來創做出不同的外觀~
而後來一波安裝完成的照片:
最後更新一波實測視頻(因爲不能同時錄像和控制,因此只放了節奏燈效果):
地址:https://www.bilibili.com/video/av38471827/