Grove-MP3 V3-Music Playerを使用してみた【前編】

この記事は 「Seeed UG Advent Calendar 2019 - Qiita」の 4日目 の記事です。(埋まっていなかったのでいただきました)

今年一年は木魚ネタばかりやっていたようなので、そろそろ少し違うこともやろうと思っていました。 ようやく今年の作業も一段落という感じになってきたので、溜まっていた作業を消化しようかなろと思います。

先日Bazaarをみていたら新しいMP3PlayerのGroveモジュールを見つけました(以下V3と表記)。

f:id:ueponx:20191225212519p:plain

www.seeedstudio.com

まだ日本ではVer2.0が販売されていますが、海の向こうではVer3が発売しているようです。

掲載時とは修正がありましたのでご注意ください。

SeeedさんのBazaarの商品も技適関連が申請によって使用が緩和されているので、購入のハードルが下がっているようですが、自分みたいな営業に近い業務ではなかなか時間の捻出なども制限されてしまうので、いくら申請がかんたんでも億劫になってしまいます。

ですが、このモジュールであれば技適も関係ないしって感じで。使ってみようかなと説明ページを読んでみると以下のような機能のようです。

  • Supports MP3 format audio files
  • Sampling rate: 8~48KHz / bit rate: 8~320Kbps
  • Support up to 32GB TF card
  • Support speaker and earphone output audio at the same time
  • Compatible with 3.3V and 5V platform.
  • Support 32-level volume adjustment

ちなみにこれまでのV2.0は以下のような感じでした。

www.seeedstudio.com

  • General operations on audio files
  • On-board micro-SD slot and 3.5mm audio jack
  • Support sample rate of 8/11.025/12/16/22.05/24/32/44.1/48(KHz)
  • 24-bit DAC output, 90 dB(at Max.) dynamic output range, signal-noise ratio at 85 dB
  • MP3, WMW and WAV audio formats and FAT16, FAT32 files system supported
  • Embed 10 levels of equalization in total

基板の形状が変わったようですが、一番大きな違いは電源が5V3.3Vも使用可能になったということかもしれません。 JST2.0 speaker portやPlay/Pauseボタンがついた点も大きな違いといえます。このあたりは開発時には割と嬉しい機能だったり。

V3では制御チップがWT2003S-20SSになっているので制御も変わったかなと思ったらあれ情報はデータシートを見ないといけないみたい。ぐえ

データシートのURLはこちらです。

データシートを読んでみた

基本部分

基本的な動きとしては、これまでと大差なくGroveコネクタに接続をしてシリアルデータを送信ものになります。

初めて中国語のデータシートをみましたが、Googleさんの翻訳に助けられて無事に内容はわかった感じです。

f:id:ueponx:20191227104906p:plain

制御チップはフラッシュ、SDカード、USBに格納されたMP3データを読み出すことができるので、それぞれ再生用のコマンドがあります。ただ、今回のGrove モジュールではSDカードのみを考えれば良いのでそこだけを抜き出します。また、ディレクトリ構造にも対応しているのですが、文字数(5文字)も限られ、凝ったこともしないのでその部分は考えないことにします。

基本的なコマンドの構成は以下のようになります。

開始コード(0x7E)+Length(このバイトからチェックサムまでのバイト数)+コマンド(1バイト)+コマンドで必要となるデータ(0から数バイト)+チェックサム+終了コード(0xFE)

このバイト列を送信することで、命令を実行することができます。

そして、命令が実行されると以下のコードが返されます。

【修正がありました】 コマンド(送信したものと同じものが送信コマンドの成否まず返され、その後にリターンコードが返されます。 リターンコード送信コマンドの成否には00(OK)、01(Fail)、02(ファイルが存在しない)などがあります。 リターンコードは存在するものとしないものがありますが、送信コマンド毎に異なっているようです。

まとめるとこんな感じになります。

【送信データのフォーマット】

意味 内容
開始コード 0x7E
Length このバイトからチェックサムまでのバイト数
コマンド 1バイト
コマンドで必要となるデータ コマンドによってバイト数は異なる(0〜数バイト)
チェックサム Lengthからチェックサム前までの論理和の下位1バイト
終了コード 0xFE

【受信データのフォーマット】(修正がありました)

意味 内容
コマンド 送信したコマンドの成否データ(1バイト)
00:OK
01:Fail
02:ファイルが存在しないなど
内容はコマンドによって違うようです
リターンコード コマンドによって異なります。

あと忘れていましたが、シリアルポートのビットレートは9600bpsとなります。

コマンド一覧

使用しそうなコマンドを抜き出してみました。

コマンド 意味 引数または戻り値
A2 SDカードからインデックス指定再生 ファイルのインデックス
A3 SDカードからファイル名指定再生 ファイル名
A4 SDカードからディレクトリ名とインデックス指定再生 ディレクトリ名、インデックス
AA 再生・一時停止 なし
AB 停止 なし
AC 一つ次の曲へ なし
AD 一つ前の曲へ なし
AE 音量調整 音量 0x00〜0x1Fの31段階
C1 現在の音量値取得 C1 XX
XX:0x00〜0x1Fの31段階
C2 現在の再生状態取得 C2 XX
XX:意味
01 play
02 Stop
02 Pause
C5 SDカードにある曲数取得 C5 XXXXX
XX:曲数

テスト用のコード

GroveモジュールなのでSeeeduinoでテストしてみようと思います。

今回はGroveをソフトウエアシリアルで接続しますので以下にしておきます。

#include <SoftwareSerial.h>
SoftwareSerial SSerial(SCL, SDA); //I2Cポートでソフトウエアシリアルを使用

SeeeduinoであればUART用のポートを使用してもいいのですが、今回のコードはシリアルポートに頻繁にアクセスするためボードへの アップロードが失敗してしまいます。突然アップロードが失敗するのでかなり混乱するのですが、以下のエントリーが参考になりました。

qiita.com

D0、D1を使ったシリアル通信を頻繁に使用するコードではこういうことが結構あるみたいです。今回はGroveコネクタの接続をUARTポートから外すことで 解決しますが、それではなかなか開発も辛いので、今回はI2Cポートを使用してソフトウエアシリアルとして使用しています。下図の③が書かれている ポートになります。

【参考:Seeedduinoのピンアサイン】

https://github.com/SeeedDocument/SeeeduinoV4/raw/master/images/hardware_seeed.png

送信部分

シリアルへの送信処理のコードはこんな感じになります。 処理は大きく分けて以下のようになります。

  1. Lengthの計算
  2. チェックサムの計算
  3. シリアルポートへの書き込み

【送信部分のソースコード

void GroveMP3V3_WriteCommand(U8 commandCode, const U8 *parameter, int parameterSize)
{
  U8 length = 1 + 1 + parameterSize + 1;// Lengthを計算
  U8 sum = 0;// チェックサムの初期化
  // 以下チェックサムの計算
  sum += length;
  sum += commandCode;
  for (int i = 0; i < parameterSize; i++) sum += parameter[i];

  //シリアルポートへの書き込み
  SSerial.write(0x7e);// 開始コード
  SSerial.write(length);// Length
  SSerial.write(commandCode);// コマンド
  SSerial.write(parameter, parameterSize);// コマンド引数
  SSerial.write(sum);// チェックサム
  SSerial.write(0xef);// 終了コード
}

受信部分

シリアルへの受信処理のコードはこんな感じになります。 処理は大きく分けて以下のようになります。

  1. シリアルデータ受信待ち
  2. コマンドデータの受信
  3. 返値の受信

【受信部分のソースコード

bool GroveMP3V3_ReadReturn(U8 *operationCode, U8 *returnValue, U8 returnValueSize)
{
  while (SSerial.available() < 1);// 受信待ち
  *operationCode = SSerial.read();// コマンドデータの受信

  for (int i = 0; i < returnValueSize; i++)
  {
    while (SSerial.available() < 1);// 受信待ち
    returnValue[i] = SSerial.read();// return値の受信
  }

  return true;
}

おわりに

コードのアップロードのトラブルでちょっと時間がかかってしまいました。 内容も少し長くなってしまったので【後編】に続きます。