磁流體音箱

為什麼會研究這個話題呢?也就是划到了一個廣告:磁流體音樂節奏…還不帶喇叭的,心想這不就是弄個聲音感測嗎?為何要三四千大洋!然後我注意到了「磁流體」這個詞,以及那坨黑黑的,查了一下網,這東西真的很有學問的,也有做成藍芽喇叭形態的(價格更是高),會隨音樂跳動,很酷的樣子,我不是想買一個,而是想知道這是怎麼弄的…(※以下圖片取自網路)

想研究的第一個行動當然就是上網科普,然後買來看看(就是花錢啦!)…在蝦x上查一下,發現我們這兒很少賣,多是對岸的賣家,原來鐵磁流體可不是很好做出來,一開始我的A計劃想生成看看,但看了方法之後就算了!應該要有化工底子的比較好,能買到人家弄好的就不錯了,若想拿來做音響專題,還得注意別買教具用的(專給小朋友看像玩具),要挑低密度磁流體,含量也不要太少,至少要3ml以上的,價格也都是幾百大洋起跳!

鐵磁流體

鐵磁流體的主要原料是10奈米四氧化三鐵。它是「對磁有反應的液體」,由美國航太總署(NASA)工程師Steve Papell在1960年代研發而成。磁流體的研發,原始目的在克服太空「無重力」所產生的許多困擾。例如,液體燃料產生飄浮狀態變得難以控制,造成機器在太空無法運作。其解決方法是添加磁流體,使液體受磁力控制,以克服飄浮現象。

參考資料: 

維基百科:zh.wikipedia.org/zh-tw/%E9%93%81%E7%A3%81%E6%B5%81%E4%BD%93 

臺灣網路科教館:www.ntsec.edu.tw/liveSupply/detail.aspx?a=6829&cat=6844&p=1&lid=19958 

我一開始認為,那些音箱上的磁流體隨著音樂跳動是因為喇叭的磁力(事實証明這個想法是錯的!),當然它也不是因為音波的關係,經過努力的搜尋,找到一文章:www.xjishu.com/zhuanli/62/202121668320.html一种磁流体音箱的制作方法),我了解了一點:是運用「電磁鐵」,可以自己想像一下,電磁鐵通電產生磁力吸引磁流體,那如果能操縱電磁鐵的電流大小就能動態改變磁力的大小,但想的有道理跟實作是有距離的~後來我又找到了另一篇:zhuanlan.zhihu.com/p/563216885?utm_id=0 (如何用esp32做一个磁流体音箱),這位大神使用ESP32和L298n來控制電磁鐵,還真是神來一筆,所以我就模仿他的方法來做一個…

我的磁流體音響

看這段影片就知我做出來了,這個Case在接線及程式上的難度並不高我可以看懂(可能是我之前有做過Esp32 BluetoothAudio和音頻分析儀),比較困難的反而是湊齊所需的零件吧!而我與所參考的原作也有許多不同,我比較重視音響播音的部份,所以我採用的喇叭及功放比較大,連帶影響了電源供應的部份…

磁流體瓶

除非你有化工經驗及對應的材料,不然建議上網買一買吧!到淘x是便宜許多的,像我手上這瓶,裡頭的水溶液(不是純水)100ml,磁流體3ml左右,我的case用的是5ml的,不一定只有黑色的,有各種色彩(人家真厲害

電磁鐵

這種電磁鐵我們這兒也不容易買(買到了也是上圖右那顆小的不夠力),是對岸的廠做的,所以淘x買也是比較便宜的,我使用這顆是朗碩P50/30圓形强吸力60KG直流12V 

電源模組

為了使用較大的功放推喇叭,我採用這塊龍邱多電源模組,它的電容做得很不錯,能承受較大的電流,在我們這兒的商家買會比淘x多100元,我這音響電源接入DC12V 2A,然後靠這電源模組供應三種電壓:12V,5V,3.3V

※如果你使用一般常見的電源模組,音樂開大聲點可能就會跳電了

L298N馬達模組

利用這個模組來輸出電壓給電磁鐵,例如把電磁鐵的二條線接OUT3和OUT4,那麼就會用到IN3,IN4及ENB三個Pin來收從esp32傳來的訊號

ESP32-Wroom-32

這兒用的esp32模組是30pin的就可以了,雖然它也可以直接做成藍芽音樂接收器,但音質及推力都不夠,所以只使用它的ADC(Analog-to-Digital Converter)及運算PWM輸出 

L358訊號放大模組

從功放板傳出的訊息是非常微弱的,直接到ESP32可能會偵測不到,所以需要加一個L358來把訊號放大100倍

我的音箱做成分離式的設計,到電子材料行可以買到小小的香蕉接頭(也稱燈籠接頭),兩對母接頭是要接二顆喇叭的,焊一下線是免不了的(線的另一頭是準備接到功放板的),其中一個正極的接頭多焊一條杜邦出來,是準備接到L358去的。

我買的這顆單體是4 Ω 5W的(這可以很大聲了)只要60大洋,自己設計個音箱殼裝進去,瞬間質感就提升了!喇叭線就焊上香蕉公接頭,這樣就可以很方便插拔

做個主控中心盒子

一樣就是練習測量及畫畫的時間,這個盒子是整個電路的集中點,TM1637模組及0.91吋oled其實是用來增加一點表面效度的(要不然esp32板子的功能不太有發揮,之後還有以加聲音感測之類的…),在這個設計裡,我感得最重要的就是電源模組了,它有很多pin位可以讓所有零件的電源都接上去

畫一組可以密合的設計圖,一半放電磁鐵一半放磁流體瓶,我有一個小巧思就是順著磁流體瓶的旁邊預留可以塞燈條的溝

接線測試

不管想得設計得多美好,能運作才是王道,所以在組裝之前一定先測試電機部份的運作,以前也沒玩過這種電磁鐵,是不是真的得用這麼大顆的才行呢?經過實際實驗,這顆的磁力剛好,理論上給12v的電壓它的磁力最強,可是供應它的L298N未必能一真提供那麼高的電壓,我想電磁電的等級至少要是30KG吸力的才夠,我們也可透過ESP32的程式來調整輸出的強度…

ZK-502M功放板

在這個系統中,如果想直接用ESP32建Bluetooth Audio也行,但光靠ESP32板子的推力,頂多能接1W的小喇叭吧!(很小聲而且音質不會太好),想大聲一樣得外接擴大機(功放板),不如就直接使用等級好一點的功放板常藍芽音樂接收器,放過ESP32吧!這塊ZK-502M不到200元,效能跟音質卻很不錯,我直接讓它接在主機外,它的供電一樣吃電源板,這種作法,不僅可以使用藍芽音樂也可以用AUX  IN,本身又附有聲音旋鈕,我覺得很適合…

接線圖

音樂的來源是功放板,所以ZK-502M的喇叭線其中一條正極接到L358信號放大器的IN,OUT出去接到ESP32的D32,功放板和L298N都是接12V的電源,ESP32的D2,D4,D15分別接L298N的ENB,IN3,IN4,這是電磁鐵的電壓隨音樂波動的重點;其他帶點的七段顯示器CLK接D19,DIO接D18(是用來顯示時間的),OLED模組的SCL和SDA就接ESP32的SCL和SDA

ESP32的程式碼(Arduino)

由於要抓時間,所以程式啟動時要連網,原作者的範例是寫死WIFI連線,但我改成使用WifiManager模組,這樣子的話喇叭拿到不同的地方,連不到原來的wifi會跑ap模式,用手機連上它就可以設定那兒的連線資訊

#include <U8g2lib.h>

#include "TM1637.h"

#include <WiFiManager.h>

#include <WiFiClientSecure.h>

#include <WiFi.h>

#define CLK  19 //Set the CLK pin connection to the display

#define DIO  18 //Set the DIO pin connection to the display

#define ANALOG_PIN_0   32

const char* ntpServer = "time.google.com";

const long  gmtOffset_sec = 28800; //台灣時區+8hr,28800=8*60*60

const int   daylightOffset_sec = 0;  //台灣無日光節約時間

TM1637 tm1637(CLK, DIO); //set up the 4-Digit Display.

int numCounter = 0;

bool dian = false;

void setClock() {

  struct tm timeinfo;

    if (!getLocalTime(&timeinfo))

    {

        Serial.println("Failed to obtain time");

    while (WiFi.status() != WL_CONNECTED)

    {

        delay(500);

        Serial.print(".");

    }

  }

     configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

     return;

    }

     tm1637.display(0,timeinfo.tm_hour/10);

    tm1637.display(1,timeinfo.tm_hour%10);

    tm1637.display(2,timeinfo.tm_min/10);

    tm1637.display(3,timeinfo.tm_min%10 );

    tm1637.point(!dian);                                

   WiFi.disconnect(true);

}

/********************OLED******************/

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0,U8X8_PIN_NONE);

const int WIDTH=128;

const int HEIGHT=32;

const int LENGTH=WIDTH;

int x;

int y[LENGTH];

void clearY(){           

  for(int i=0; i<LENGTH; i++){

    y[i] = -1;

  }

}

/***************波形图函数******************/

void drawY(){

  u8g2.drawPixel(0, y[0]);

  for(int i=1; i<LENGTH; i++){

    if(y[i]!=-1){

      //u8g.drawPixel(i, y[i]);

      

      u8g2.drawLine(i-1, y[i-1], i, y[i]);

    }else{

      break;

    }

  }

}     

void setup(){

  Serial.begin(115200);

  tm1637.init();

  tm1637.point(1);  //打開時鐘點

  tm1637.set(BRIGHT_TYPICAL);

  pinMode(ANALOG_PIN_0,INPUT_PULLUP); //設定ADC腳位

/********PWM配置**************/  

  ledcSetup(0,5000,8);

  ledcAttachPin(2,0);

/***********OLED***************/

u8g2.begin();

u8g2.enableUTF8Print();

x = 0;

clearY();

/**************************/

  u8g2.clearBuffer();          // clear the internal memory

  u8g2.setFont(u8g2_font_unifont_t_chinese2);  // 使用中文字型

  u8g2.setCursor(0, 20);

  u8g2.print("咖啡虫磁流体音箱");    

  u8g2.sendBuffer();

  delay(1000);   

  u8g2.clearBuffer();

  u8g2.drawStr(0,20,"SSID:CW_Speaker");  // 顯示SSID名稱

  u8g2.sendBuffer();

/*******************WiFiManager初始化*****************/

  WiFiManager wifiManager;

  wifiManager.setTimeout(180); 

  //第一個參數是SSID的名稱,第二個參數是密碼

  if(!wifiManager.autoConnect("CW_Speaker","12345678")) {

    Serial.println("failed to connect and hit timeout");

    delay(3000);

    ESP.restart();

    delay(5000);

  }

 configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

 setClock();

    WiFi.disconnect(true);

    WiFi.mode(WIFI_OFF);//关闭网络

    Serial.println("WiFi disconnected!");

}

void loop() {

 /***********讀取類比訊號*********************/

  int analog_value = 0;

  analog_value = analogRead(ANALOG_PIN_0);

  Serial.println(analog_value);

/*******OLED波形顯示******************/

  y[x] = map(analog_value, 0, 4095, HEIGHT-1, 0);

  Serial.write(y[x]);

  u8g2.firstPage();  

  do {

    drawY();

  } while( u8g2.nextPage() );

  x++;

  if(x >= WIDTH){

    x = 0;

    clearY();

  }

  int PWM = map(analog_value,0,4095,0,255);      //這行是轉換ADC波的信號到L298N的PWM強度

  digitalWrite(4,LOW);

  digitalWrite(15,HIGH);

  ledcWrite(0,PWM);   //送出PWM

  /*********顯示時間**************/

 setClock();  

}

繼續修正

Oled展示方式修改

改掉原本音樂播放時小螢幕上的波型模式,改以16軌音樂頻譜 frequency spectrum) 的方式,在程式方面,當ADC抓取類比音訊時,先進行1024階的取樣,再以ArduinoFFT程式庫將Sample的偏移值去掉,再依頻率高低分成16軌,呈現各頻率的強度。