🐺

PicoRuby + DFPlayer miniで音楽再生

に公開

はじめに

電子工作していると音を鳴らしたくなることがある。ブザーみたいなのであれば割とできそうな感じだがちょっと味気ない。かといってまともにサウンドプログラムするのも大変だ。そんなときに便利なのがDFPlayer mini。

mciroSDカードが挿せて、そこにMP3ファイルを入れておけば再生できる。スピーカーもそのまま繋げられる。マイコンからは再生、ボリューム設定といった各種命令を送るだけで再生できるのでサウンド処理の知識も不要だ。

実際に使うには各種命令を送る必要があり、Arduino用ではライブラリがあるので簡単に動かせる。Picorubyにはそのような便利なものは多分ないので、それを動かせるようにした、というのが今回の話。

実際に動いている様子はこちら。

https://x.com/warumizu/status/1949070850705260799

材料

材料 買い方・備考
Raspberry Pi Pico WH 今回はpico 2ではないpico 1を使った。
DFPlayer mini 秋月電子で買った正規品で1050円。ピンヘッダ付きなのではんだ付けの必要なし。もっと安い互換品もあるらしいが、試してないので動くかは分からない。 追記: 試したら動いた。
スピーカー これも秋月電子で買った。ブレッドボードにそのまま挿せるので手軽。
microSDカード MP3ファイルを入れてDFPlayer miniに挿す。

microSDの準備

DFPlayer mini用のmicroSDはFAT32でフォーマットしておく。手元にあった32GBのmicroSDで動作を確認。

このSDカード直下にmp3ファイルを置けば再生できるが、再生順序はアルファベット順ではなく書き込み順になるようだ。0001.mp3, 0002.mp3, ...などのファイルをPC側に用意して、まとめてコピーすると多分その順になる。

配線

Raspberry Pi Pico WHとDFPlayer miniのピン配置は以下のようになっていて、

配線はこんな感じ。

実際の配線

Arduino用の配線例を見るとTX, RXとの接続の間に抵抗を入れていることが多いが、これはArduinoのロジック電圧が5Vだからで、Raspberry Pi Picoは3.3Vなので要らない、ような気がする。が、その辺は詳しくないので分からない。とりあえず今のところ煙が出たりはしていない。

プログラムと動作

下の方に挙げたコードをdfplayer.rbという名前で/homeに保存する。あとはirb上で試す。

# irb上で以下を入力
load 'dfplayer.rb'
dfp = DFPlayer.new(unit: :RP2040_UART0, txd_pin: 0, rxd_pin: 1)
dfp.reset           # 最初に一度リセットする
dfp.set_volume(20)  # ボリューム設定 (0-30)
dfp.play(2)         # 2番目のファイルを再生する
# dfplayer.rb
require 'uart'

## Sample usage
# dfp = DFPlayer.new(unit: :RP2040_UART0, txd_pin: 0, rxd_pin: 1)
# dfp.reset
# dfp.set_volume(10)  # set volume to 10 (0-30)
# dfp.play(4)         # play file 4

class DFPlayer
  def initialize(unit: unit, txd_pin: txd_pin, rxd_pin: rxd_pin)
    @uart = UART.new(unit: unit, txd_pin: txd_pin, rxd_pin: rxd_pin, baudrate: 9600)
  end

  def reset
    msg_bytes = [0x0c, 0x01, 0x00, 0x00]
    send_msg_bytes msg_bytes
  end

  # value: 0-30
  def set_volume(value)
    msg_bytes = [0x06, 0x01, 0x00, value]
    send_msg_bytes msg_bytes
  end

  def play(file_num)
    param1 = (file_num >> 8) & 0xFF
    param2 = file_num & 0xFF
    msg_bytes = [0x03, 0x01, param1, param2]
    send_msg_bytes msg_bytes
  end

  def array_to_str(byte_array)
    msg = "a" * byte_array.length
    byte_array.each_with_index{|x, i|
      msg.setbyte(i, x)
    }
    msg
  end

  def send_byte_array(byte_array)
    print("send_byte_array:")
    byte_array.each{|x|
      printf(" %02x", x)
    }
    print("\n")
    msg = array_to_str(byte_array)
    @uart.write msg
  end

  # byte_array: bytes of VER, Len, CMD, Feedback, para1, para2
  def calc_checksum(byte_array)
    sum = 0
    byte_array.each{|x|
      sum += x
    }
    checksum = -sum & 0xFFFF
    byte0 = checksum & 0xFF
    byte1 = (checksum >> 8) & 0xFF
    return [byte1, byte0]
  end

  # msg_bytes: bytes which contains CMD, Feedback, para1 and para2
  # $S, VER, Len, checksum and $O are added in this method
  def send_msg_bytes(msg_bytes)
    msg_bytes.unshift(msg_bytes.length + 2) # add Len
    msg_bytes.unshift(0xff)     # add VER
    # checksum is calculated with VER, Len, CMD, Feedback, para1 and para2
    checksum = calc_checksum(msg_bytes)
    msg_bytes.unshift(0x7e)     # add $S
    msg_bytes.push(checksum[0]) # add checksum
    msg_bytes.push(checksum[1])
    msg_bytes.push(0xef)        # add $O
    send_byte_array msg_bytes
    sleep 1
  end
end

もう少し詳しく

UARTによる通信

DFPlayer miniの詳細情報が載っているマニュアルは秋月電子のページからダウンロードできる。DFPlayer miniとの通信はUARTという方式を使うようだ。PicorubyにはUARTで通信するためのクラスがすでに用意されているので、それを使えばRubyコードだけで使えるようになる。

PicorubyでのUARTの使い方はRubyではじめる電子工作という本に載っていたのでこれを参考にさせてもらった。

制御コマンド

マニュアルによれば制御コマンドのフォーマットは以下のようになっている。

名前 バイト数 意味
$S 1 開始バイト。常に0x7e。
VER 1 バージョン。詳細は書かれていないが0xffで動いた。
Len 1 Lenからchecksumまでの長さ(checksumは含まない)。とは言っても可変長の命令はなさそうで、Resetのようなパラメータなしの命令でもpara1, para2に0x00を設定するので、結果的にここは常に0x06になりそう。
CMD 1 コマンド。Resetは0x0cなど。詳細はマニュアルを参照。
Feedback 1 送ったコマンドに対して返信を返すかどうか。とりあえず0x01を指定しているが無視している。
para1 1 パラメータの上位バイト。
para2 1 パラメータの下位バイト。
checksum 2 チェックサム。
$0 1 終了バイト。常に0xEF。

para1, para2はいわゆるビッグエンディアンの16ビット整数になる。例えばspecify trackコマンドで1000番 (0x03E8) を指定したいときはpara1を0x03, para2を0xe8にする。

checksumについては「Accumulation and verification」という説明だけなのでちょっと分かりにくい。こういうときはArduino用ライブラリのコードが参考になる。VERからpara2までのバイトを合計して、その結果の符号を変えればよい。

例えばResetとしてボリュームを10にして1番目のファイルを再生するなら以下のようになる。

7e ff 06 0c 01 00 00 fe ee ef
7e ff 06 06 01 00 0a fe ea ef
7e ff 06 03 01 00 01 fe f6 ef

まとめ

ということでPicoruby+DFPlayer miniで音声ファイルを再生できるようになった。最低限の命令を実装しただけで足りないところは色々あるけれど、とりあえず遊べるようにはなったと思う。

Discussion