201404022351聲音偵測 + RGB LED燈

--- 2019/10/15 聲明 ---

很感謝仍有人對這個arduino應用有興趣

本人已淡出科技業多年,科技日新月異,我想我已跟不上時代

有問題的朋友建議可以多嚐試其它思維,debug很辛苦但也很有趣!

diy好玩的地方就在於自己獨自解決問題,謝謝大家!

----------------------------

周末假日沒事就會去騎Ubike,每次行經大稻埕時,傍晚會有街頭藝人表樂表演

酷的不是表演,而是旁邊那顆粉光LED櫻花樹!!

這年頭街頭藝人競爭激烈......除了表演要高水準外,如果沒來個酷炫的燈光

就會留不住人潮,留不住人潮就等於沒有收入,沒有收入怎麼去找藝旦阿蕊咧!!

因此我腦中就產生了一個唉滴兒.......

偌若使用麥克風偵測聲音,RGB LED燈再根據聲音大小進而改變亮暗呢?

且要適應各個場所和環境,所以要做成電池供電型的,體積不能太大

因此需要做的事有:

1. RGB LED燈

2. 高感度聲音檢測模組

3. arduino pro mini

4. 裝置整合與實作

 

《RGB LED燈》

關於LED的使用是學習arduino的初階課程,在這裡就不多做介紹

但是............你不是說要改變亮度大小!這個怎麼做啊!!!!!

在這邊要先介紹什麼是PWM (Pulse WidthModulation 脈衝寬度調變)

PWM是將類比信號轉換為脈波的一種技術,改成數字編碼,狀態只有ON/OFF

而ON的周期稱為脈沖寬度,調整脈沖寬度,進而得到類比模擬值

當ON(5V)、OFF(0V)的狀態下,就可以得到介於0V~5V之間的穩定電壓

先看看arduino官網上對PWM的介紹 http://arduino.cc/en/Tutorial/PWM

再看看下面這張圖,在相同的頻率下,當25% Duty Cycle時,LED為全亮狀態的1/4亮

以此類推,100%時就是最亮狀態


(圖片來源 http://arduino.cc )

 

到這邊,看一下實作的範例,相信更能理解理論

我是使用4腳共陰的RGB LED燈,這樣子需要3根PWM腳位分別控制R.G.B三色

通常最長腳都是接地,規格上R燈最大電壓2.35V、G.B燈則是3.5V

龜毛一點的話,R燈需要接比較大的電阻

但因為紅色的強度其實很弱,在混色後,藍光總是會比較搶眼

這點讓我很不滿!所以就同樣都接220 ohm電阻

基本上是不會有問題的!(其實是懶得買電阻.................)

 

一開始先測試RGB LED燈,所以我先用arduino Mega 2560來試

Mega 2560的PWM腳位是2-13、44-46,先使用pin 2,3,4 來實驗RGB LED燈

RGB_LED程式碼下載

/* Define R,G,B pwm pin */
#define RED_PIN   2
#define GREEN_PIN 3
#define BLUE_PIN  4

/* Color selection enum */
enum
{
  BLACK = 0x30,   //ASCII = 0
  RED = 0x31,     //ASCII = 1
  GREEN = 0x32,   //ASCII = 2
  BLUE = 0x33,    //ASCII = 3
  YELLOW = 0x34,  //ASCII = 4
  MAGENTA = 0x35, //ASCII = 5
  CYAN = 0x36,    //ASCII = 6
  WHITE = 0x37,   //ASCII = 7
  MIXCOLOR = 0x38 //ASCII = 8
};

/* Serial port receive buffer */
#define BUFFER_SIZE 16
char inBuffer[BUFFER_SIZE];

int fade_val;
int DELAY_DURATION = 33;

void setup()
{
  /* Initialize pin mode */
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  analogWrite(RED_PIN, 0);
  analogWrite(GREEN_PIN, 0);
  analogWrite(BLUE_PIN, 0);
  
  Serial.begin(9600);
  
  /* if analog input pin 0 is unconnected, random analog noise will cause the call to randomSeed() to generate
     different seed numbers each time the sketch runs. randomSeed() will then shuffle the random function. */
  randomSeed(analogRead(RED_PIN));
  randomSeed(analogRead(GREEN_PIN));
  randomSeed(analogRead(BLUE_PIN));
  
  Serial.println("Setup() finished....");
}

void loop()
{
  /* When user inputs command ..... */
  while (Serial.available() > 0)
  {
    Serial.readBytes(inBuffer, BUFFER_SIZE);
    switch(inBuffer[0])
    {
      case BLACK:
        Serial.print(">> Black ");
        analogWrite(RED_PIN, 0);
        analogWrite(GREEN_PIN, 0);
        analogWrite(BLUE_PIN, 0);
        Serial.println("<<");
        break;
        
      case RED:
        Serial.print(">> Red ");
        OneColor(RED_PIN, GREEN_PIN, BLUE_PIN);
        Serial.println("<<");
        break;
      
      case GREEN:
        Serial.print(">> Green ");
        OneColor(GREEN_PIN, RED_PIN, BLUE_PIN);
        Serial.println("<<");
        break;
      
      case BLUE:
        Serial.print(">> Blue ");
        OneColor(BLUE_PIN, RED_PIN, GREEN_PIN);
        Serial.println("<<");
        break; 
      
      case YELLOW:
        Serial.print(">> Yellow ");
        TwoColor(RED_PIN, GREEN_PIN, BLUE_PIN);
        Serial.println("<<");
        break;
      
      case MAGENTA:
        Serial.print(">> Magenta ");
        TwoColor(RED_PIN, BLUE_PIN, GREEN_PIN);
        Serial.println("<<");
        break; 
      
      case CYAN:
        Serial.print(">> Cyan ");
        TwoColor(GREEN_PIN, BLUE_PIN, RED_PIN);
        Serial.println("<<");
        break;
      
      case WHITE:
        Serial.print(">> White ");
        ThreeColor(RED_PIN, GREEN_PIN, BLUE_PIN);
        Serial.println("<<");
        break;
      
      case MIXCOLOR:
        Serial.print(">> Mix Color for 10 seconds ");
        for (int i=0; i<50; i++)
        {
          analogWrite(RED_PIN, random(255));
          analogWrite(GREEN_PIN, random(255));
          analogWrite(BLUE_PIN, random(255));
          delay(200);
        }
        Serial.println("<<");
        break;
        
      default:
        Serial.println(">> Input Error! <<");
        break;
    }
  }
}

void OneColor(int _on, int _off1, int _off2)
{
  analogWrite(_ON, 0);
  analogWrite(_off1, 0);
  analogWrite(_off2, 0);
  delay(300);
  
  /* darkness -> brightness */
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on, fade_val);
    delay(DELAY_DURATION);
  }
  /* brightness -> darkness */
  for (fade_val=255; fade_val>0; fade_val=fade_val-5)
  {
    analogWrite(_on, fade_val);
    delay(DELAY_DURATION);
  }
  /* darkness -> brightness */
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on, fade_val);
    delay(DELAY_DURATION);
  }
}

void TwoColor(int _on1, int _on2, int _off)
{
  analogWrite(_on1, 0);
  analogWrite(_on2, 0);
  analogWrite(_off, 0);
  delay(300);
  
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    delay(DELAY_DURATION);
  }
  for (fade_val=255; fade_val>0; fade_val=fade_val-5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    delay(DELAY_DURATION);
  }
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    delay(DELAY_DURATION);
  }
}

void ThreeColor(int _on1, int _on2, int _on3)
{
  analogWrite(_on1, 0);
  analogWrite(_on2, 0);
  analogWrite(_on3, 0);
  delay(300);
  
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    analogWrite(_on3, fade_val);
    delay(DELAY_DURATION);
  }
  for (fade_val=255; fade_val>0; fade_val=fade_val-5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    analogWrite(_on3, fade_val);
    delay(DELAY_DURATION);
  }
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    analogWrite(_on3, fade_val);
    delay(DELAY_DURATION);
  }
}

測試RGB LED燈的話,就是R,G,B單色測試、2色混合、3色混合

開啟arduino開發視窗的Serial Monitor後,輸入顏色對應的數字,對應程式碼為:

  while (Serial.available() > 0) /*接收到輸入指令*/
  {
    Serial.readBytes(inBuffer, BUFFER_SIZE);  /*將收到的資料以readBytes的方式存到inBuffer陣列*/
    switch(inBuffer[0])  /*只判斷陣列index 0的字元*/

例如輸入1,就會只閃爍紅色,過程是全滅→亮→全滅→亮

PWM數值的話以+5為單位,至於DELAY_DURATION = 33

這樣子相當於一秒有30次變化,看起來會較為連續smooth

 

《高感度聲音檢測模組》

市面上有許多模組套件,聲音偵測方面,大致上分為二款

音量超過一個門檻值,模組輸出Digital訊號,只反應有沒有聲音

另一種是有Digital訊號,也有輸出Analog訊號,可以藉由訊號大小轉換成音量大小

當然為了讓LED燈有亮暗變化,所以選擇有Analog訊號輸出的

此類模組製造商都會提供使用範例的程式,在這裡就不多做介紹

我也不想幫廠商打廣告,最多就提供個照片,我想......很容易百度到的.......

密訣在於照片中藍色的電位器的調整,這個在範例程式中沒有詳細說明

你可以先試著把AO的訊號print出來,最適合的門檻值為500,調整到這個數值以它為門檻

 

《arduino pro mini》

arduino的家族成員有很多,其中我最喜歡的就是Mega 2560和pro mini

2560不用說,一大堆腳位!可以一口氣整合好多想要的裝置!土豪哥首選利器!

另一個就是最迷你,麻雀雖小、五臟俱全的 pro mini !!!

你把十塊硬幣放在pro mini旁邊做面積對照都會覺得十塊硬幣太大了呢~~

詳細的介紹可連右列連結至官網 arduino pro mini

只不過pro mini的缺點是燒錄比較麻煩一點,因為不像其它家族成員可經USB燒錄

這也是為什麼pro mini價格可以cost down的原因之一

燒錄它的方式有許多種,最方便的就是直接買市場上都有賣的FT232RL晶片燒錄板

大小和pro mini,只要腳位接對,一樣是輕鬆燒錄,基本上只要接上5根,其中:

FT232RL 燒錄板 arduino pro mini
GND GND
VCC VCC
TX RX
RD TX
DTR DTR

 

《裝置整合與實作》

我的習慣是每個裝置都分別測試,確定work&understand後再整合起來(讓我烙個英文...3Q)

先前測試RGB LED時,是用Mega2560的pin 2,3,4 (PWM)

但在pro mini上,PWM腳位是pin 3, 5, 6, 9, 10 和 11,太大意惹!所以電路圖要修改一下


目標要做成攜帶型,所以必須使用電池供電,買的pro mini是5V/16MHz的

由於pro mini很強壯,灌12V給它也沒有問題!所以電源供應就用4顆3A的電池

另外聲音感測模組,找不到fritzing的圖,模組是使用LM393晶片,就根據規格接線,只是省略被動元件

LED燈之前就實驗過,這個問題不大,接下來解釋一下程式碼

Sound_LED程式碼下載


/* Define R,G,B pwm pin */
#define RED_PIN   3
#define GREEN_PIN 5
#define BLUE_PIN  6

/* Color selection enum */
enum
{
  RED = 0,
  GREEN,
  BLUE
};
#define DELAY_DURATION 33
int fade_val;

int SOUND_VOLUME_PIN = A0; /* Input pin for the potentiometer */
int SOUND_THRESHOLD = 500; /* Sound level threshold */
int SOUND_MAX = 600; /* Sound maximum level */
int soundValue;

int ledMaxValue;
int randomMaxColor;

void setup()
{
  /* Initialize pin mode */
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  analogWrite(RED_PIN, 0);
  analogWrite(GREEN_PIN, 0);
  analogWrite(BLUE_PIN, 0);
  
  /* if analog input pin 0 is unconnected, random analog noise will cause the call to randomSeed() to generate
     different seed numbers each time the sketch runs. randomSeed() will then shuffle the random function. */
  randomSeed(analogRead(RED_PIN));
  randomSeed(analogRead(GREEN_PIN));
  randomSeed(analogRead(BLUE_PIN));
  
  /* Initializing.....7 color testing.... */
  OneColor(RED_PIN, GREEN_PIN, BLUE_PIN);  //Red testing
  OneColor(GREEN_PIN, RED_PIN, BLUE_PIN);  //Green testing
  OneColor(BLUE_PIN, RED_PIN, GREEN_PIN);  //Blue testing
  TwoColor(RED_PIN, GREEN_PIN, BLUE_PIN);  //Yellow testing
  TwoColor(RED_PIN, BLUE_PIN, GREEN_PIN);  //Magenta testing
  TwoColor(GREEN_PIN, BLUE_PIN, RED_PIN);  //Cyan testing
  ThreeColor(RED_PIN, GREEN_PIN, BLUE_PIN);  //White testing
  
  Serial.begin(9600);
}

void loop()
{ 
  soundValue = analogRead(SOUND_VOLUME_PIN);
  if (soundValue > SOUND_THRESHOLD)
  {
    Serial.print("Sound Sensor Volume = ");
    Serial.println(soundValue);

    /* Sound level between 500~600, mapping it to analog value 0~255 */
    ledMaxValue = map(soundValue, SOUND_THRESHOLD, SOUND_MAX, 0, 255);
    /* For showing different color from LED, we pick a color (R, G or B) as base one. */
    randomMaxColor = random(0, 3);

    /* Show re-mapped PWM value and what base color is.*/
    Serial.print("  PWM = ");
    Serial.print(ledMaxValue);
    Serial.print(" , Max Color = ");
    switch(randomMaxColor)
    {
      /* For example, if re-mapped PWM value is 220 and base color is RED.
         Write value 220 to RED_PIN, then get the random value between 0~220 and 
         set it to GREEN_PIN and BLUE_PIN.
         Maybe GREEN value is 20, BLUE is 60. LED will flash "Crimson" color. */         
      case RED:
        Serial.println("Red");
        analogWrite(RED_PIN, ledMaxValue);
        analogWrite(GREEN_PIN, random(0, ledMaxValue));
        analogWrite(BLUE_PIN, random(0, ledMaxValue));
        break;
        
      case GREEN:
        Serial.println("Green");
        analogWrite(GREEN_PIN, ledMaxValue);
        analogWrite(RED_PIN, random(0, ledMaxValue));
        analogWrite(BLUE_PIN, random(0, ledMaxValue));
        break;
        
      case BLUE:
        Serial.println("Blue");
        analogWrite(BLUE_PIN, ledMaxValue);
        analogWrite(RED_PIN, random(0, ledMaxValue));
        analogWrite(GREEN_PIN, random(0, ledMaxValue));
        break;
        
      default:
        Serial.println("White");
        analogWrite(RED_PIN, ledMaxValue);
        analogWrite(GREEN_PIN, ledMaxValue);
        analogWrite(BLUE_PIN, ledMaxValue);
        break;
    }
    delay(DELAY_DURATION); /* no delay, LED will flash quickly */
  }
  else
  {    
    analogWrite(RED_PIN, 0);
    analogWrite(GREEN_PIN, 0);
    analogWrite(BLUE_PIN, 0);
  }
}

void OneColor(int _on, int _off1, int _off2)
{
  analogWrite(_on, 0);
  analogWrite(_off1, 0);
  analogWrite(_off2, 0);
  delay(300);
  
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on, fade_val);
    delay(DELAY_DURATION);
  }
}

void TwoColor(int _on1, int _on2, int _off)
{
  analogWrite(_on1, 0);
  analogWrite(_on2, 0);
  analogWrite(_off, 0);
  delay(300);
  
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    delay(DELAY_DURATION);
  }
}

void ThreeColor(int _on1, int _on2, int _on3)
{
  analogWrite(_on1, 0);
  analogWrite(_on2, 0);
  analogWrite(_on3, 0);
  for (fade_val=0; fade_val<256; fade_val=fade_val+5)
  {
    analogWrite(_on1, fade_val);
    analogWrite(_on2, fade_val);
    analogWrite(_on3, fade_val);
    delay(DELAY_DURATION);
  }
}

這裡用到3個小密訣去撰寫,首先是 map(value, fromLow, fromHigh, toLow, toHigh) 這個函式

功能很簡單,就是將一個數值由原來的範圍轉換到另一段範圍

例如原先範圍是1-10、數值為5,如果轉換到1-20的範圍時,數值就會變成10

我設定聲音門檻值為500、最大值為600,這中間出現的值會對應到0~255的範圍

所以如果偵測到的值>=600的話,那麼PWM就吐出255,亦即LED會是最亮狀態

 

第2個小密訣是想要做出每次發光時,顏色是隨機變化的

發光前先隨機選擇出紅(R)、藍(G)或綠(G)那一個為主要顏色 randomMaxColor = random(0, 3);

接著另外2個顏色為配角,數值的話就是0至當下最大值之間隨機產生出來

例如選出紅色為主顏色,轉換後得到的PWM值為220

藍色和綠色就會在0~220之間隨機產生數值出來,假設藍色得到60、綠色得到20

最後呈現出來的顏色應該就會是猩紅色 (.............腥羶色我知道是什麼....但猩紅色是三小........)

我知道你們的疑惑是什麼...........猩紅色大概就是長這樣子 ●●●●●

 

第3個小密訣在還沒有實作出來前在腦中已經有想到,實驗也證明我的猜測是對的

由於燈光閃爍的速度非常快,快到大概只有張慶忠委員的30秒才跟得上!

因此在燈光亮起來,小小地delay了33ms,讓燈光亮度小小地持續一下,配合音樂節奏時會更準確

講了那麼多,直接來看手工土炮第一版的成品吧!

 

接著來看看實際測試的影片,事實證明了..............紅光真的不夠強............

 

沒有上一則|日誌首頁|沒有下一則
回應
關鍵字
    沒有新回應!