勝手な電子工作・・

勝手なオリジナル電子工作に関する記事を書きます

これでOK!睡眠タイマー付きMP3プレイヤーをArduinoで(その2)

f:id:a-tomi:20210202121028j:plain

前回の記事の続きです。

工夫をした結果、ついにノイズの心配なくコントロールできるようになりました!

DFPlayerのノイズにつながり易いシリアル通信でのコントロールをやめて、IOボタン機能をArduinoでコントロールするようにしたらバッチリです。上の写真のとおり、大きなキャパシターは不要ですし、電源5VをATmega328Pと問題なく共用できます。そして良い音が出ます。

新たな回路は次のとおりです。毎度ながら汚い手書きですみませんが m(_ _)m

f:id:a-tomi:20210202133753j:plain
今回はDFPlayerのIO_1、IO_2ボタンにオープンコレクター出力をつないでコントロールします。このために2SC1815トランジスタを経由しますが、小信号用のNPNトランジスタなら何でもよいです。小さなトランジスタアレイ部品でもよいですね。2つだけなのでここではディスクリート(単体)部品を使っています。このやり方ならDFPlayerの発振の恐れをかんたんに回避できます。

f:id:a-tomi:20210202131610j:plain

上の写真で右下にあるIO_1、IO_2ボタンが、短押し(short push)だとそれぞれ前の曲、次の曲で、長押し(long push)だとそれぞれボリュームを下げる、上げるとなっています。また、右上にあるBUSY端子は曲の演奏中はLOWに、曲が終わり停止中はHIGHの信号を出します。

この3つの端子を使えばそのままプレイヤーとして機能します。「勝手な電子工作」としてはこれをマイコンでコントロールしたい。そして自動演奏(最後の次は最初にLoop)と、スリープ時間での停止がしたいわけです。スリープは曲の終了を待たず先にボリュームをゼロにしておけばさらによいかと。

DFPlayerのデータシートには、短押し、長押しの時間の記述箇所がなぜか見当たりません^^;  ここでは短押しは100mS、長押しは850mSにしますがこれで問題なし。(短くしたいですが、実験の結果、長押しを700mSにすると前後の曲選択ととられることがある)。

また、こういうスイッチ入力端子の場合、デバイス側では押下の検知後にチャタリングの影響回避として30~50mSのホールド時間がとられていると推定します。よって短押しも確実さのため上記の長さとしました。

Arduinoのプログラムは次のように作りました。ライブラリーなどを何も使わずに簡単にできますので、UNOの容量は10%以下に収まります。

/********************************************************
 * DFPlayer with Sleep-timer and Volume-control,        *
 *   without using any libraries for DFPlayer-mini.     *
 *     Initial version-01 (c)Jan.30,2021 Akira Tominaga *
 * How to operate:                                      *
 *   Insert micro SD with mp3 or wav files, in playing  *
 *   sequences. Maximum 255 files x 99 folders.         *
 *   Power off & on to re-play after the timer expired. *
 * Revisions:                                           *
 *   V02: Changed all pin assignments.     Feb.1,2021   *
 *****************************************************:**/
 #define pBusy 15       // DFPlayer BUSY pin, H when busy
#define Tmr30 11        // 30 min switch for sleep-timer 
#define Tmr60 10        // 60 min switch for sleep-timer
uint8_t vTsv = 0;       // save-a to detect timer changes
#define vol_D 5         // volume-down button
#define vol_U 8         // volume-up button
#define ledG  9         // led green for sleep-timer
#define IO1 17          // DFPIO1 short-on=<<,long-on=Vol-
#define IO2 16          // DFPIO2 short-on=>>,long-on=Vol+
#define sOn 100         // time (mS) for short-on
#define lOn 850         // time (mS) for long-on
unsigned long slpT = 0; // elapsed timer mS (large value)
#define stopOff 0       // stop signal is off
#define stopOn 1        // stop signal is on
uint8_t stopSig = stopOff; // initialize stop signal

void setup() {          // *** Arduino setup() ***
  pinMode (IO1, OUTPUT);
  digitalWrite(IO1, LOW);
  pinMode (IO2, OUTPUT);
  digitalWrite(IO2, LOW);
  pinMode (pBusy, INPUT);
  pinMode (Tmr30,INPUT_PULLUP);
  pinMode (Tmr60,INPUT_PULLUP);
  pinMode (vol_D, INPUT_PULLUP);
  pinMode (vol_U, INPUT_PULLUP);
  pinMode(ledG, OUTPUT);
  digitalWrite(ledG, HIGH); // show that I waked-up
  Serial.begin(9600);
   for (uint8_t i = 0; i < 3; i++) { // decrease volume
    volDown();          // lower in the beginning 
  }
  digitalWrite(ledG, LOW); // LED off 
  Serial.println(F("DFP start"));
  playNext();           // start 1st music
  delay(300);           // delay before loop
}

void loop() {           // *** Arduino loop ***
  if (digitalRead(pBusy) == HIGH) { // if idle
    playNext();
  }
  if (digitalRead(vol_D) == LOW) { // vol.d button?
    while (vol_D == LOW) {} // button-released?
    volDown();          //  then decrease volume
  }
  if (digitalRead(vol_U) == LOW) { // vol.up button?
    while (vol_U == LOW) {} // button-released?
    volUp();             // then increase volue
  }
  setTmr();   // check and set sleep-timer
  delay(300);
}

/******************************
 *   User defined functions   *
 ******************************/
// *** Volume down as fnction volDown() ***
void volDown(void) {
  digitalWrite(IO1, HIGH);
  delay(lOn);           // long IO-on
  digitalWrite(IO1, LOW);
  Serial.println(F("Vol -" ));
  delay(10);
}
// *** Volume up as function volUp() ***
void volUp(void) {
  digitalWrite(IO2, HIGH);
  delay(lOn);           // long IO2-on
  digitalWrite(IO2, LOW);
  Serial.println(F("Vol +"));
  delay(10);
}

// *** Play the next as function playNext() ***
void playNext(void) {
  if (stopSig != stopOn) { // if not stop timing,
    digitalWrite(IO2, HIGH); // then play next, by
    delay(sOn);         // setting IO2-on
    digitalWrite(IO2, LOW); // for a short time
    Serial.println(F("Next file"));
  } else {
    Serial.println(F("Stopped"));
    while (true) {}   // and stop here
  }
}

// *** Set sleep-timer as function setTmr() ***
void setTmr(void) {
#define noSleep 2000000000 // 2 Billion mS (23days)
  static uint8_t vTmr;  // sleep-timer value (min)
  vTmr = 0;             // initialize static vTmr
  if (digitalRead(Tmr30) == LOW) { // if Tmr30 is set
    vTmr = vTmr + 30;   // then add 30 minutes
  }
  if (digitalRead(Tmr60) == LOW) { // if Tmr60 is set
    vTmr = vTmr + 60;   // then add 630minutes
  }
  if (vTmr != vTsv) {   // if sleep-timer changed
    vTsv = vTmr;        // save it
    Serial.print(F("Timer(min)=")); // and show it
    Serial.println(vTmr); 
  }
  static unsigned long lTmr = vTmr; // convert type
  slpT = lTmr * 60 * 1000 + 10000;  // mS val.+10sec
  if (vTmr == 0) {      // if no timer set,
    slpT = noSleep;     // then set a huge value
    digitalWrite(ledG, LOW); // and timer-LED off
  }
  if (vTmr > 0) {       // if sleep-timer set
    digitalWrite(ledG, HIGH); // timer-LED on
  }
  static unsigned long pTime;
  pTime = millis();     // get passed time
  if (slpT < pTime) {   // if timer expired,
    Serial.println(F("Timer expired"));
    digitalWrite(ledG, LOW); // timer-LED off
    for (uint8_t i=0;i<10;i++){
      volDown();        // minimize volume
    }                   // power-off req'd afterward 
    stopSig = stopOn;   // and set stop-sign on
  }
}
// *** End of program

前回の記事でも書きましたが、Arduino-IDEを使ってArduinoーUNOへ普通にスケッチを書き込み、そしてATmega328p単体を抜き取って使うわけです。そうすれば本番機を小さく作るのが簡単なわけです。もちろん、ArduinoーUNOで結線しても、今回の回路は問題なくきれいに動きノイズは全くのりません。

f:id:a-tomi:20210202132718j:plain

これを実行するとシリアルモニターへ次のアウトプットが出ます。

f:id:a-tomi:20210202151142j:plain

出だしの音が少し大きすぎるので、プログラムでは最初にまずボリュームを3回下げています。

タイマー指定はパワーオンからの経過時間(+10秒としました)にしています。設定値は最初にでますが、演奏の途中で設定を変更すれば再設定され、追加で出力されるようにしてあります。曲を次々と演奏しますが、途中でボリュームボタンをいじるとそのように表示されます。

ATmega328p単体で実行する場合でも、Tx端子にシリアルモニター(作り方は次の記事の最後のほうに載せてあります。)をつなげば同じようにトレースができますね。

ソフトウェアシリアルをコンパクトに作るーArduino編-そしてトレースに使う - 勝手な電子工作・・

 

さて、本番機を作る前にこの記事を書こうとして試しました。そこで一苦労し、次のことに気づきました。

以下は余談ですから、読み飛ばされてもかまいませんが、DFPlayerを使ってもしノイズに悩まされる場合はご覧いただくと役立つかと思います。

==========================

UNOとの接続でいかにノイズが載らないとしても、下手なブレッドボードを使って下手な配置をすると発振がのる場合があります。次のような適当な配置だと音は出ますが、シュルシュルというかすかなノイズが混じり、びっくりしました。この場合、DFPlayerの電源に大きなCを入れても完全には解決しません。DFPlayerの発振だからです。

f:id:a-tomi:20210202152129j:plain

 

 接続線が長いのはまずいですが、ブレッドボードそのものにも問題の原因があることがわかりました。このブレッドボードは内部の接続部品が大きいため、浮遊容量(ストレ、Stray、寄生容量)が他のボードより大きいのです。裏をはがしてみれば一目瞭然ですが、次の写真で下のものです。

f:id:a-tomi:20210202152456j:plain

DFPlayerは小さなボードにデコーダー、DAC、それにアンプを積んでいるため、ストレが大きいと、出力が入力に回り込みやすいわけ。こういうブレッドボードでは配置をよく考えないと少し厳しいわけです。

 

テストであってもマイコン側の使用ピンを吟味して、DFlayerの入力ピンからは短く接続したいものです。そういうわけでブレッドボード自体をとりかえて、かつArduinoのピン割り当ても見直して試作したのが最初の写真です。

これであればたいへん安定しており、DFPlayerの電源に大きなCなどは不要です。ようやく完全に問題が出なくなったわけです^^; ここは普通のパスコン0.1μFでも大丈夫ですが、デジタルアンプを使うので一応1μFを入れてあります(D級アンプなので音量次第で電流はごく少ないし無音時はほぼ流れないわけです)。

ブレッドボードではなくいきなり基板を作っての本番機なら、そういう発振は簡単に防げそうでしょうが、なにせ事前にテストしたいものですから^^;

===========================

 

どっちにしても、余計な心配をしなくて済む今回の安定した回路で、近々本番機を基板から作ろうと思っています。そうすれば今後はきっとよく眠れますね・・・^^

シリアル接続でコントロールするのと比べた場合の機能上の違いは、イコライザの指定ができないことです。とはいえ、目的は睡眠用に小さな音で演奏するものなのでDefaultのLoudnessイコライザで十分。もし本格的なステレオを作る場合は、DFPlayerにStereo DAC出力ピンがついてます(大したものです)から、そこから他のアンプモジュールに入れる形にするのがよいですね。

 

ところで、あいにく決算期限と確定申告が迫ってます。ひょっとすると本番機の製作は少し後になるかもしれませんが、とにかく急いで追加記事を先に書かせていただきました。この記事がお役に立てばたいへん幸いです。

 

2021.2.22追記:チョコっと本番機を作ってしばらく試していました。音も良く入眠用に具合が良いので、ソフトも更新し次のリンクへ追加しておきました^^;

入眠タイマー付きMP3プレーヤーの製作(その3) - 勝手な電子工作・・

 

©2021 Akira Tominaga, All rights reserved.