macsbug

Just another WordPress.com site

Audio Spectrum Display with M5STACK

with one comment

M5STACK で Audio Spectrum Display を作りました。            2017.12.31

音をディスプレー にスペクトラム表示します。
音を「Sound Detection Sensor 」( FC-04 ) ( 322円 ) で受けて、その信号を
M5STACK の GPIO36 に入力し スペクトラム を ディスプレー に 表示します。

スケッチ:
_ tobozo氏の「ESP32_Spectrum_Display_03.ino」を M5STACK に移植しました。
_ tobozo氏のソースは G6EJD氏の「ESP32_Spectrum_Display_02.ino」です。
_ 謝辞:G6EJD氏 と tobozo氏に感謝致します。

_ tobozo氏の使用した ディスプレーは 128×64 OLED SSD1306 です。
_ M5STACKのディスプレーは 320×240 TFT ILI9341 です。


.
準備:

No  Nomen  Link、Description   Price
1  M5STACK BASIC  Aliexpress:M5Stack Store ( $33.25 ) ( SALE )  3767円
2  Sound Detection Sensor  
2  ebay:shieldsfans ( AU $1.00)   88円
2  ebay:alice1101983 ( US $0.99)  112円
2  Ali:3C Top-rated Seller (US $0.79)  112円
4  ピンソケット(メス)
1×8 ( 8P )
 秋月電子通商    30円
5  切れる基板
0.3mm厚 両面スルーホール
ユニバーサル基板
 秋月電子通商    60円
 —————————–  ————————————————-   ——
6  G6EJD/
_ Audio-Spectrum-Display
  ESP32_Spectrum_Display_03.ino
7  kosme/arduinoFFT   arduinoFFT

.


.
配線:
GPIO36 は入力専用で SENSOR_VP と言う名前です。
参考:ESP32 Hardware Design Guidelines:Page 27 4.1.2 Pin Definition

_ VCC(FC-04) = 3V3(M5), GND = GND, OUT = AD(GPIO36)へ接続します。
_ FC-04 の電源は 3.3V で使用します。
_ FC-04 の出力は ESP32 のGPIO36 ( SENSOR_VP ) へ入ります。

秋月のハサミで切れる基板と ピンソケットで製作しました。

FC-04の調整:
_ FC-04 のボリュームの調整は 電源を入れ 緑のLEDが点滅する
_ 場所に設定します。スペクトラム表示を見ながらも調整してください。


.
マイクとアンプ:
マイクのアナログ信号は 最適な値である事が 重要です。
_ 以下5つを試し使用できる製品は 左の「FC-04」という製品でした。
_ 販売:右は 秋月電子通商の製品です。他は ebay や Aliexpress です。
_ FC-04 以外は ゲインレベルが正しく取れず 表示範囲が狭いです。
_ 原因は ゲインが取り難い事や飽和しやすく動作範囲が狭い為です。
FC-04:OUT はアナログでは無くデジタルです。何故 デジタルが 動作に
_ 適しているか 分析していません。

_ tobozo氏は 以下の様に述べています。
_「正しい結果が得る方法は、約50mV〜100mV pk-pkのオーディオを
_ DCオフセットなしでADCポートに直接供給することです。」

マイクアンプについての 記事は macsbug:「 FC-04 回路メモ 」にあります。


.
スケッチ変更内容:
_ 1. 「tft.drawFastHLine」を「M5.Lcd.drawFastHLine」に変更します。
_ 2. 「tft.fillRect」 を「M5.Lcd.fillRect」に変更します。
_ 資料:M5STACK の命令は 「M5Stack.h」に定義されています。


.
M5STACK analogRead 時の Speaker nois 問題と解決方法。
問題:analogRead() 時に Speaker (GPIO25) からノイズがでます。
_  例:vReal[i] = analogRead(A0);
方法:dacWrite(25, 0); // Speaker OFF //
解説:
_ GPIO25 (Channel 1) と GPIO26 (Channel 2) は two 8-bit DAC
_ (digital to analog converter) channels です。( 16bit )。
_ GPIO25 の 出力 (サウンド) は NS4148 AUDIO AMPLIFER に入り
_ AUDIO OUTPUT は SPEAKER に接続さています。
_ 資料:M5STACKの回路図
_ 資料:ESP32 Datasheet:Page 8, 2.2 Pin Description:GPIO25=DAC_1
_ GPIO25 は DAC端子ですので dacWrite(25, 0); で出力を制御します。
_ 信号解析:GPIO25 は 無音で 3mVac、analogRead(A0),で 10mVac 発生し
_ スピーカーからプツプツ音がでます。電源は問題なく GPIO25 からの出力
_ が原因の様です。


.
参考:
Github:G6EJD/ESP32-8266-Audio-Spectrum-Display
Github:tobozo/ESP32-8-Octave-Audio-Spectrum-Display
YouTube:G6EJD:Tech Note 077 – ESP32 8-Octave Audio Spectrum Display
YouTube:tobozo tagada:ESP32 Audio Spectrum from saturation to silence
YouTube:tobozo tagada:ESP32 Audio Spectrum on WROVER Kit V3
YouTube:Tech Note 069 – Using the ESP32 ADC and some of its more advanced functions
_ ADCの使用方法、線形性の説明、減衰設定の説明、ADCチャンネルで
_ 使用されるピンの変更方法を示し、多項式を使用して精度を1%未満に
_ 改善する関数を示します。
G6EJD / ESP32-ADC-Accuracy-Improvement-function

macsbug:SplitRadixRealP FFT Analyzer of Arduino DUE
_ Arduino Due + SplitRadixRealP の例。


.
ESP32 MH-ET LIVE D1 mini + OLED 128×64 で Audio Spectrum Display を製作しました。
_ QI で ワイヤレス電源にしてみました。
_ macsbug:Arduinoの電源をワイヤレス化
価格:ESP32 MH-ET LIVE D1 mini: $7.2 ( 811円 )
価格:OLED 128×64:$2.91 ( 328円 )


.
感想:
1.  ESP32 による「ESP32 Spectrum Display」
_ 基本は G6EJD氏により「ESP32_Spectrum_Display_01.ino」と
_  ESP32_Spectrum_Display_02.ino」が作成されました。そして
_ tobozo氏により「ESP32_Spectrum_Display_03.ino」が作成されました。
_ 基本と高速化に G6EJD氏 と tobozo氏 に感謝致します。

2. マイクとアンプ:
_ 音のサンプリングはゲインと帯域がないと 充分に機能しません。
_ 動作範囲の広いボードを使用するべきですが 最善のボードが見つかりません。
_ 課題: AGC機能のあるアンプを使用する必要があると思います。
_ 現在 他のボードを注文 及び 調査中です。

3. M5STACK の ケース。
_ M5STACK はケースが綺麗で素晴らしいです。
_ PROT Module は ありますが REAR CASE がありません。
_ M5STACK の強みは綺麗さです。綺麗に仕上げる為に 本体周辺の部品や
_ ケースを販売して欲しいです。止めネジや やや斜めに於ける台も欲しいです。
_ Jimmy Lai さん に期待しています。

4. FC-04の電源:
_ VCC = 5V での検証はしていません。

5. メモ:高速抽画。
_ 全画面消去の「M5.Lcd.fillScreen」を使用するとチラつきが発生します。
_ 対策は「M5.Lcd.fillScreen」を使用せず 同じ抽画を2度操作します。
_ 横線の高速抽画:
_  1. 前回の線を「drawFastHLine」+ 黒で抽画する。
_  2. 今回の値を前回に保存。
_  3. 今回の線を「drawFastHLine」で 抽画する。
_ 横線複数の高速抽画:
_  1. 前回の線全てを「fillRect」+ 黒で塗りつぶす。
_  2. 今回の値を前回に保存。
_  3. 今回の線を「drawFastHLine」で 抽画する。


.
スケッチ:

/* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
 * The MIT License (MIT) Copyright (c) 2017 by David Bird. 
 * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * See more at http://dsbird.org.uk 
*/
// https://github.com/tobozo/ESP32-8-Octave-Audio-Spectrum-Display/tree/wrover-kit
// https://github.com/G6EJD/ESP32-8266-Audio-Spectrum-Display
// https://github.com/kosme/arduinoFFT


#include "arduinoFFT.h"          // Standard Arduino FFT library 
arduinoFFT FFT = arduinoFFT();
#include <M5Stack.h>
#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 40000 
// Hz, must be 40000 or less due to ADC conversion time.
// Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.

struct eqBand {
  const char *freqname;
  uint16_t amplitude;
  int peak;
  int lastpeak;
  uint16_t lastval;
  unsigned long lastmeasured;
};

eqBand audiospectrum[8] = {
  //Adjust the amplitude values to fit your microphone
  { "125Hz", 500, 0, 0, 0, 0},
  { "250Hz", 200, 0, 0, 0, 0},
  { "500Hz", 200, 0, 0, 0, 0},
  { "1KHz",  200, 0, 0, 0, 0},
  { "2KHz",  200, 0, 0, 0, 0},
  { "4KHz",  100, 0, 0, 0, 0},
  { "8KHz",  100, 0, 0, 0, 0},
  { "16KHz", 50,  0, 0, 0, 0}
};

unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;
uint16_t tft_width  = 320; // ILI9341_TFTWIDTH;
uint16_t tft_height = 240; // ILI9341_TFTHEIGHT;
uint8_t bands = 8;
uint8_t bands_width = floor( tft_width / bands );
uint8_t bands_pad = bands_width - 10;
uint16_t colormap[255]; // color palette for the band meter (pre-fill in setup)

void setup() {
  Serial.begin(115200);
  M5.begin();
  dacWrite(25, 0); // Speaker OFF
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setTextColor(YELLOW, BLACK);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setRotation(0);
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
  delay(2000);
  for(uint8_t i=0;i<tft_height;i++) {
    colormap[i] = M5.Lcd.color565(tft_height-i*.5, i*1.1, 0);
  }
  for (byte band = 0; band <= 7; band++) {
    M5.Lcd.setCursor(bands_width*band + 2, 0);
    M5.Lcd.print(audiospectrum[band].freqname);
  }
}

void loop() {
  for (int i = 0; i < SAMPLES; i++) {
    newTime = micros()-oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) { 
      // do nothing to wait
    }
  }
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);

  for (int i = 2; i < (SAMPLES/2); i++){ 
    // Don't use sample 0 and only first SAMPLES/2 are usable. 
    // Each array eleement represents a frequency and its value the amplitude.
    if (vReal[i] > 1500) { // Add a crude noise filter, 10 x amplitude or more
      byte bandNum = getBand(i);
      if(bandNum!=8) {
        displayBand(bandNum, (int)vReal[i]/audiospectrum[bandNum].amplitude);
      }
    }
  }
  
  long vnow = millis();
  for (byte band = 0; band <= 7; band++) {
    // auto decay every 50ms on low activity bands
    if(vnow - audiospectrum[band].lastmeasured > 50) {
      displayBand(band, audiospectrum[band].lastval>4 ? audiospectrum[band].lastval-4 : 0);
    }
    if (audiospectrum[band].peak > 0) {
      audiospectrum[band].peak -= 2;
      if(audiospectrum[band].peak<=0) {
        audiospectrum[band].peak = 0;
      }
    }
    // only draw if peak changed
    if(audiospectrum[band].lastpeak != audiospectrum[band].peak) {
      // delete last peak
     M5.Lcd.drawFastHLine(bands_width*band,tft_height-audiospectrum[band].lastpeak,bands_pad,BLACK);
     audiospectrum[band].lastpeak = audiospectrum[band].peak;
     M5.Lcd.drawFastHLine(bands_width*band, tft_height-audiospectrum[band].peak,
                           bands_pad, colormap[tft_height-audiospectrum[band].peak]);
    }
  } 
}

void displayBand(int band, int dsize){
  uint16_t hpos = bands_width*band;
  int dmax = 200;
  if(dsize>tft_height-10) {
    dsize = tft_height-10; // leave some hspace for text
  }
  if(dsize < audiospectrum[band].lastval) {
    // lower value, delete some lines
    M5.Lcd.fillRect(hpos, tft_height-audiospectrum[band].lastval,
                    bands_pad, audiospectrum[band].lastval - dsize, BLACK);
  }
  if (dsize > dmax) dsize = dmax;
  for (int s = 0; s <= dsize; s=s+4){
    M5.Lcd.drawFastHLine(hpos, tft_height-s, bands_pad, colormap[tft_height-s]);
  }
  if (dsize > audiospectrum[band].peak) {
    audiospectrum[band].peak = dsize;
  }
  audiospectrum[band].lastval = dsize;
  audiospectrum[band].lastmeasured = millis();
}

byte getBand(int i) {
  if (i<=2 )             return 0;  // 125Hz
  if (i >3   && i<=5 )   return 1;  // 250Hz
  if (i >5   && i<=7 )   return 2;  // 500Hz
  if (i >7   && i<=15 )  return 3;  // 1000Hz
  if (i >15  && i<=30 )  return 4;  // 2000Hz
  if (i >30  && i<=53 )  return 5;  // 4000Hz
  if (i >53  && i<=200 ) return 6;  // 8000Hz
  if (i >200           ) return 7;  // 16000Hz
  return 8;
}

.
CARD STAND:
ダイソーにある 2個100円の L型カードスタンド。64 x 45 x 20 mm。
M5STACK にピッタリ です!


広告

Written by macsbug

12月 31, 2017 @ 9:00 am

カテゴリー: ESP32, ESP8266

コメント / トラックバック1件

Subscribe to comments with RSS.

  1. […] インターフェースのピンは M5STACKの内側に刺して使用します。 使用例は Audio Spectrum Display with M5STACK […]


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。