ikko LABS
← Blog
ArduinoESP32電子工作I2SJLCPCBMP3再生NS4168基板設計

子どものおもちゃに曲を入れたくて、ESP32と自作NS4168アンプ基板を作った記録

2026年10月5日

子どものおもちゃに曲を入れたくて、ESP32と自作NS4168アンプ基板を作った記録

この記事の要点

  • ESP32の内蔵フラッシュ(LittleFS)にMP3を3曲入れ、I2SアンプのNS4168で再生できました
  • 音が出なかった原因は、ピンの取り違え(DATAとSDATA)でした。サンプリングレートも疑いましたが、測り直して無関係だと分かりました
  • アンプ部分はJLCPCBで自作の基板にして、3.3V給電で動くことを確認しました

はじめに

子ども向けのおもちゃに曲を入れて鳴らしたくて、ESP32の内蔵フラッシュにMP3を入れ、I2Sアンプで再生する構成を作りました。

できあがった構成はシンプルですが、音が出るまでに何度も無音とノイズで止まりました。試した順に記録を残します。

やったこと結果
曲の保存先を決める内蔵フラッシュ(LittleFS)にした
ブレッドボードで鳴らすI2Sアンプモジュールで再生できた
アンプ基板を作るJLCPCBで製造し、3.3V給電で再生できた

完成した構成

内蔵フラッシュのMP3をESP32がデコードし、I2Sの3本でNS4168に送ります。

使ったもの

項目内容
マイコンESP32 DevKit(ESP32-WROOM-32、フラッシュ4MB)
アンプNS4168搭載のI2Sアンプモジュール(MEMSマイクLMD2718付き)。あとで自作の基板に置き換え
スピーカー8Ω
開発環境Arduino IDE 2.3.4、ESP32 core 3.3.10
ライブラリESP8266Audio 2.4.1(名前はESP8266ですがESP32で動きます)
音源無料で配布されているフリー音源。MP3、48kbps、44.1kHz、モノラルに変換
保存先内蔵フラッシュのLittleFS(約2.9MBを割り当て)

配線(モジュールを使ったとき)

ESP32モジュール役割
GPIO26BCLKビットクロック
GPIO25LRCLK左右のクロック
GPIO22SDATA音声データ
5V(VIN)5V電源
GNDGNDグラウンド

このあと、アンプ部分を自作の基板に置き換えました。同じスケッチのまま、3.3V給電で動いています。

曲の保存先をどう決めたか

3つの方法を検討しました。

方法追加する部品今回の結果
音声IC(WT588F02B-8S)音声IC在庫が見つからず、試していない
microSDカードカードモジュール、SPIの信号線4本再生できた
内蔵フラッシュ(採用)なし再生できた

音声IC:在庫が見つからなかった

おもちゃの音源といえば、音声IC(WT588Fなど)が定番です。最初はWT588F02B-8Sを考えました。

JLCPCBで実装まで頼む前提で在庫を調べたところ、次の状態でした(2026年8月に調べた時点)。

  • JLCPCBの部品ページ(C2917616)は存在する
  • LCSCの商品ページは404で、在庫検索にも出てこない

ほかの音声ICも同じように在庫が見つからなかったので、在庫が豊富なESP32とI2Sアンプ(NS4168、C910588)の組み合わせに切り替えました。

注意

在庫は調べた時点のものです。発注前に必ず自分で確認してください。

microSDカード:再生できたが、部品が増える

ESP32にmicroSDカードモジュールをSPIでつなぎ、カードに入れたMP3を再生する方法も試しました。こちらも再生できました。

項目microSDカード内蔵フラッシュ
追加する部品DFPlayer mini、microSDカード、抵抗なし
追加する配線I2Cの信号線2本(Tx、Rx)と電源なし
曲の入れ替え別途SDカードに追加PCから書き込む
容量大きい今回は約2.9MB

内蔵フラッシュなら、追加の部品も配線も要りません。今回は内蔵フラッシュにしました。

内蔵フラッシュ:4MBに3曲入った

4MBのフラッシュを「スケッチ1MB + LittleFS約2.9MB」に分けました。48kbpsで2分前後の曲が3曲入りました。

ファイルサイズ
00001.mp3681,866バイト
00002.mp3763,525バイト
00003.mp3742,992バイト
合計2,188,383バイト(領域の71%)

MP3のサイズの計算

サイズ(バイト)= ビットレート(bps)× 秒数 ÷ 8

48kbpsで120秒の曲なら、48,000 × 120 ÷ 8 = 720,000バイトです。サンプリングレートは関係しません。

今回入れた3曲は、無料で配布されているフリー音源です。自分で用意した曲や市販の曲ではありません。

曲をESP32に入れる手順

手順やること使うもの
1フラッシュを、スケッチ用と曲用に分けるpartitions.csv、Arduino IDE
2MP3を48kbps・44.1kHz・モノラルに変換するffmpeg
3MP3をまとめて、LittleFSのイメージにするPython(littlefs-python)
4イメージをESP32に書き込むesptool

1. パーティションを分ける

スケッチと同じフォルダにpartitions.csvを置き、Arduino IDEの「ツール → Partition Scheme」でCustomを選びます。

ファイル partitions.csv

# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x100000,
spiffs, data, spiffs, 0x110000, 0x2F0000,

1行が1つの領域です。上から順に、次の意味になります。

名前開始位置サイズ用途
nvs0x900020KB設定の保存
otadata0xe0008KB起動するアプリの情報
app00x100001MBスケッチ
spiffs0x110000約2.9MB曲(LittleFSとして使う)

補足

名前はspiffsのままで、LittleFSとして使えます。開始位置の0x110000は、あとで曲を書き込むときに使います。

2. MP3を変換する

コマンド MP3の変換

ffmpeg -i input.mp3 -ac 1 -ar 44100 -b:a 48k -map_metadata -1 00001.mp3

input.mp3が元の曲、00001.mp3が変換後のファイルです。

オプション意味
-ac 1モノラルにする
-ar 44100サンプリングレートを44.1kHzにする(16kHzや22.05kHzでも再生できます)
-b:a 48kビットレートを48kbpsにする
-map_metadata -1タグ情報を消す

3. LittleFSのイメージを作る

本来はArduino IDE 2.x用のプラグイン(arduino-littlefs-upload)で、dataフォルダごとアップロードできます。

私の環境(Arduino IDE 2.3.4)ではコマンドパレットに項目が出てこなかったので、Pythonのlittlefs-pythonでイメージを作りました。

フォルダは次のように置きます。

フォルダの構成

作業フォルダ/
├─ make_littlefs.py ← 下のスクリプト
├─ data/
│ ├─ 00001.mp3
│ ├─ 00002.mp3
│ └─ 00003.mp3
└─ littlefs.bin ← 実行するとできる

Python make_littlefs.py

from littlefs import LittleFS
import os, glob
SIZE, BLOCK, PROG = 0x2F0000, 4096, 256
fs = LittleFS(block_size=BLOCK, block_count=SIZE // BLOCK,
read_size=PROG, prog_size=PROG, disk_version=0x00020000)
for p in sorted(glob.glob("data/*.mp3")):
with fs.open("/" + os.path.basename(p), "wb") as f:
f.write(open(p, "rb").read())
open("littlefs.bin", "wb").write(bytes(fs.context.buffer))
設定値意味
SIZE0x2F0000曲の領域のサイズ。partitions.csvと合わせる
BLOCK4096フラッシュのブロックサイズ
PROG256読み書きの単位
disk_version0x00020000LittleFSのバージョン2.0

実行は次の2行です。1行目はライブラリのインストールで、最初の1回だけです。

コマンド イメージを作る

py -m pip install littlefs-python
py make_littlefs.py

この設定で作ったイメージは、ESP32 core 3.3.10のLittleFSでマウントできました。

4. esptoolで書き込む

コマンド 曲のイメージを書き込む

py -m esptool --chip esp32 --port COM5 --baud 921600 write-flash 0x110000 littlefs.bin
部分意味
--port COM5ESP32をつないだポート。自分の環境の番号に変える
--baud 921600書き込みの速度
write-flash書き込みのコマンド。esptool v5でwrite_flashから名前が変わった
0x110000書き込む位置。partitions.csvの値と合わせる
littlefs.bin手順3で作ったイメージ

注意

シリアルモニタを開いたままだと、ポートを開けず失敗します。閉じてから実行してください。

補足

曲とスケッチは別々に書き込みます。Arduino IDEの「→」ボタンで書き込まれるのはスケッチ(0x10000)だけです。

スケッチ

スケッチは5つの部分に分かれています。①〜⑤を上から順につなげると、1つのスケッチになります。

部分やっていること
① 設定ピン番号、曲のリスト、音量を決める
② 再生の開始と停止曲のファイルを開き、MP3デコーダにつなぐ
③ ファイル一覧LittleFSに入っているファイルを表示する
④ setup()起動時に1回だけ動く。I2Sを設定し、1曲目を鳴らす
⑤ loop()繰り返し動く。再生を進め、操作を受け付ける

① 設定

Arduino ① 設定

#include <Arduino.h>
#include <LittleFS.h>
#include <AudioFileSourceLittleFS.h>
#include <AudioFileSourceBuffer.h>
#include <AudioFileSourceID3.h>
#include <esp_partition.h>
#include <AudioGeneratorMP3.h>
#include <AudioOutputI2S.h>
constexpr int PIN_I2S_BCLK = 26;
constexpr int PIN_I2S_LRC = 25;
constexpr int PIN_I2S_DOUT = 22; // モジュールのSDATAへ
constexpr int PIN_NEXT_SW = 0; // 曲送りボタン(使わないなら -1)
AudioGeneratorMP3 *mp3 = nullptr;
AudioFileSourceLittleFS *file = nullptr;
AudioFileSourceBuffer *buff = nullptr;
AudioFileSourceID3 *id3 = nullptr;
AudioOutputI2S *out = nullptr;
const char *playlist[] = { "/00001.mp3", "/00002.mp3", "/00003.mp3" };
constexpr size_t PLAYLIST_LEN = sizeof(playlist) / sizeof(playlist[0]);
size_t currentTrack = 0;
float gain = 0.3f;

自分の環境に合わせて変えるのは、次の4か所です。

変える場所内容
PIN_I2S_BCLK / PIN_I2S_LRC / PIN_I2S_DOUTI2Sの3本をつなぐGPIO
PIN_NEXT_SW曲送りボタンのGPIO。使わないなら-1
playlist再生するファイル名。LittleFSに入れた名前と合わせる
gain音量。0.0〜2.0(最初は0.3)

② 再生の開始と停止

Arduino ② 再生の開始と停止

void stopPlayback() {
if (mp3 && mp3->isRunning()) mp3->stop();
delete id3; id3 = nullptr;
delete buff; buff = nullptr;
delete file; file = nullptr;
}
bool startTrack(size_t index) {
stopPlayback();
if (!LittleFS.exists(playlist[index])) {
Serial.printf("ファイルがありません: %s\n", playlist[index]);
return false;
}
file = new AudioFileSourceLittleFS(playlist[index]);
buff = new AudioFileSourceBuffer(file, 8192);
id3 = new AudioFileSourceID3(buff);
if (!mp3) mp3 = new AudioGeneratorMP3();
out->SetGain(gain);
bool ok = mp3->begin(id3, out);
Serial.printf("再生: %s (%s)\n", playlist[index], ok ? "OK" : "失敗");
return ok;
}

音声データは、次の順に流れます。

ファイル → バッファ → タグの処理 → MP3デコーダ → I2S出力

クラス役割
AudioFileSourceLittleFSLittleFSのファイルを読む
AudioFileSourceBuffer読んだデータを8192バイトためる
AudioFileSourceID3MP3の先頭にあるタグ情報を処理する
AudioGeneratorMP3MP3をデコードする
AudioOutputI2SI2Sで出力する

③ ファイル一覧

Arduino ③ ファイル一覧

void listFiles() {
Serial.println("--- LittleFS 内のファイル ---");
File root = LittleFS.open("/");
for (File f = root.openNextFile(); f; f = root.openNextFile()) {
Serial.printf(" %-16s %8u bytes\n", f.name(), (unsigned)f.size());
}
Serial.printf("使用量: %u / %u bytes\n",
(unsigned)LittleFS.usedBytes(), (unsigned)LittleFS.totalBytes());
}

起動時に、曲が入っているかを確認するための表示です。

④ setup()

Arduino ④ setup()

void setup() {
Serial.begin(115200);
delay(300);
if (PIN_NEXT_SW >= 0) pinMode(PIN_NEXT_SW, INPUT_PULLUP);
// 曲の領域が想定どおりかを表示する
const esp_partition_t *p = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if (p) {
Serial.printf("曲の領域: 開始=0x%06X サイズ=%u bytes\n",
(unsigned)p->address, (unsigned)p->size);
} else {
Serial.println("曲の領域が見つかりません。Partition Scheme を Custom に");
}
out = new AudioOutputI2S();
out->SetBuffers(12, 1024);
out->SetPinout(PIN_I2S_BCLK, PIN_I2S_LRC, PIN_I2S_DOUT);
out->SetRate(44100);
out->SetGain(gain);
if (!LittleFS.begin(false)) {
Serial.println("LittleFS のマウントに失敗");
return;
}
listFiles();
startTrack(currentTrack);
Serial.println("n=次の曲 / p=前の曲 / s=停止 / +,-=音量");
}
順処理
1シリアルを115200bpsで開始する
2曲送りボタンのピンを入力にする
3曲の領域の位置とサイズを表示する
4I2S出力を作り、ピンと44.1kHzを設定する
5LittleFSをマウントし、ファイル一覧を表示する
61曲目を再生する

⑤ loop()

Arduino ⑤ loop()

void loop() {
if (mp3 && mp3->isRunning()) {
if (!mp3->loop()) {
mp3->stop();
currentTrack = (currentTrack + 1) % PLAYLIST_LEN;
startTrack(currentTrack);
}
}
if (Serial.available()) {
char c = Serial.read();
if (c == 'n') {
currentTrack = (currentTrack + 1) % PLAYLIST_LEN;
startTrack(currentTrack);
} else if (c == 'p') {
currentTrack = (currentTrack + PLAYLIST_LEN - 1) % PLAYLIST_LEN;
startTrack(currentTrack);
} else if (c == 's') {
stopPlayback();
} else if (c == '+' || c == '-') {
gain += (c == '+') ? 0.1f : -0.1f;
gain = constrain(gain, 0.0f, 2.0f);
if (out) out->SetGain(gain);
Serial.printf("gain=%.1f\n", gain);
}
}
if (PIN_NEXT_SW >= 0 && digitalRead(PIN_NEXT_SW) == LOW) {
delay(30);
if (digitalRead(PIN_NEXT_SW) == LOW) {
currentTrack = (currentTrack + 1) % PLAYLIST_LEN;
startTrack(currentTrack);
while (digitalRead(PIN_NEXT_SW) == LOW) delay(10);
}
}
}
処理内容
再生を進めるmp3->loop()を呼び続ける。曲が終わったら次の曲へ進む
シリアルの操作文字を受け取って、曲送りや音量の変更をする
ボタンの操作GPIO0のボタンが押されたら、次の曲へ進む

操作は次のとおりです。

操作動作
シリアルでn次の曲
シリアルでp前の曲
シリアルでs停止
シリアルで+ / -音量を上げる / 下げる
GPIO0のボタン次の曲

起動時の表示

次のように表示されれば、曲は正しく入っています。

表示 シリアルモニタ

曲の領域: 開始=0x110000 サイズ=3080192 bytes
--- LittleFS 内のファイル ---
00001.mp3 681866 bytes
00002.mp3 763525 bytes
00003.mp3 742992 bytes
再生: /00001.mp3 (OK)
表示意味
曲の領域: 開始=0x110000パーティションがpartitions.csvのとおりになっている
00001.mp3〜00003.mp33曲が入っている
再生: /00001.mp3 (OK)1曲目の再生を始めた

音が出るまでにハマったところ

試した構成の移り変わり

順ESP32の出力アンプ結果
1内蔵DAC(GPIO25/26)PAM8403(GF1002)ザーというノイズだけ
2NoDAC(1ビット出力 + RCフィルタ)PAM8403(GF1002)鳴るが、サーというノイズが乗る
3I2SNS4168のモジュール最初は無音。配線を直して鳴った
4I2SNS4168の自作基板鳴った

ハマったところの一覧

番号症状原因対処
1内蔵DACから、ザーというノイズしか出ないライブラリの古い書き方が非推奨になっていた別の出力方法に進んだ
2曲は鳴るが、サーというノイズが乗るNoDAC出力では下げきれなかったI2S入力のアンプに切り替えた
3無音マイク用のDATAにつないでいたSDATAにつなぎ直した
416kHzの音源が無音3番の配線ミスの状態で試していた(レートは無関係)配線を直したあとに測り直した
5LittleFSをマウントできない不明イメージを書き直した

1. 内蔵DACから音が出ない

最初はI2Sアンプを持っていなかったので、ESP32の内蔵DAC(GPIO25/26)からアナログアンプ(PAM8403搭載のGF1002)に入れようとしました。

Arduino 音が出なかった書き方

out = new AudioOutputI2S(0, AudioOutputI2S::INTERNAL_DAC);

コンパイルは通りますが、スピーカーからはザーというノイズしか出ませんでした。

ESP8266Audio 2.4.1では、この書き方は非推奨です。AudioOutputI2S.hのコンストラクタに、DAC出力にはAudioOutputInternalDAC()を使うようにというメッセージが付いています。

補足

私はAudioOutputInternalDACを試していません。このあと別の方法に進んだためです。

切り分けには、ライブラリを使わずdacWrite()だけでサイン波を出すスケッチが役立ちました。これで音が出たので、配線とアンプは正常で、原因はライブラリの使い方だと分かりました。

2. NoDAC出力 + PAM8403は、鳴るがノイズが乗る

次にAudioOutputI2SNoDACを試しました。I2Sのデータピンに1ビットの信号を出し、RCフィルタ(1kΩ + 10nF)で平滑して、アナログアンプに入れる方法です。

曲は鳴りましたが、サーというノイズが常に乗りました。オーバーサンプリングを上げたり、コンデンサを22nFに替えたりしても、気にならない程度までは下がりませんでした。

ここで、I2S入力のアンプに切り替えることにしました。

3. モジュールの「DATA」は、アンプの入力ではなかった

買ったのは、NS4168(アンプ)とLMD2718(MEMSマイク)が載ったモジュールです。データ線が2本あります。

ピン役割
CLKマイクのクロック
DATAマイクのデータ(ここにつないでいた)
LRCLKアンプのLRクロック
BCLKビットクロック
SDATAアンプのI2Sデータ入力(正しい接続先)

DATAに音声を流していたので無音でした。商品ページのピン説明を読んでSDATAにつなぎ直したら鳴りました。

シリアルモニタには「再生: OK」と出続けるので、ソフト側の問題だと思い込み、ここで一番時間を使いました。

4. 16kHzの音源が無音だった(原因は配線ミスだった)

容量を節約するために、最初はMP3を32kbps・16kHzで作っていました。これが無音でした。

同じメロディを出力レートだけ変えて鳴らすと、44100Hzのときだけ音が出ました。そこでサンプリングレートが原因だと考え、MP3を44.1kHzで作り直しました。

訂正(2026年10月)

公開時は「16kHzの音源だと無音になる」と書いていました。これは誤りでした。

当時のテストは、データ線をマイク側のDATAにつないだ状態(3番の配線ミス)で行ったものです。SDATAにつなぎ直したあと、16kHzを試し直していませんでした。

Xで「アンプのピンでLRCLKとBCLKを測るとよい」と助言をもらい、配線を直した状態で測り直しました。

クロックの測定

ESP32のパルスカウンタで、アンプ基板のピンに出ているLRCLKとBCLKを数えました。音源は440Hzの連続音で、レートだけを変えています。

設定したレートLRCLKBCLKBCLK ÷ LRCLK音
44100Hz44,106Hz1,411,318Hz32.0鳴る
16000Hz15,999Hz512,018Hz32.0鳴る
22050Hz22,058Hz705,659Hz32.0鳴る
32000Hz31,998Hz1,024,095Hz32.0鳴る
48000Hz48,006Hz1,536,193Hz32.0鳴る

表は、44100Hzで開始してからSetRate()で切り替えたときの値です。最初からそのレートで開始した場合も、同じ結果でした。

MP3での確認

続けて、同じ曲を3通りのMP3にして再生しました。当時無音だった16kHzのファイルも鳴りました。

ファイル結果
44.1kHz・48kbps鳴る
16kHz・32kbps(当時無音だったファイル)鳴る
22.05kHz・32kbps鳴る

分かったことは次のとおりです。

  • 16kHzの音源は再生できる。サンプリングレートは無音の原因ではなかった
  • 配線を間違えていたときに、44100Hzだけ音が出た理由は分かっていない
  • 配線を直したら、それ以前の切り分けの結果は取り直す必要がある

補足

ESP8266AudioのMP3デコーダは、再生するファイルに合わせて出力レートを設定し直します。setup()のSetRate(44100)があっても、16kHzのファイルは16kHzのまま出力されます。

確認は自作基板(3.3V給電)で行いました。同じ曲で比べると、16kHz・32kbpsのファイルは454,833バイトで、44.1kHz・48kbps(681,866バイト)の約3分の2です。高い音は削られますが、容量を優先するなら選べます。

5. Corrupted dir pair でマウントに失敗する

スケッチを何度も書き換えているうちに、LittleFSがマウントできなくなりました。

表示 シリアルモニタ(エラー)

E (326) esp_littlefs: ... lfs.c:1383:error: Corrupted dir pair at {0x1, 0x0}
E (327) esp_littlefs: mount failed, (-84)

LittleFSのイメージを同じアドレスに書き直したら直りました。壊れた理由は分かっていません。

6. 細かいもの

エラー原因
setup()とloop()が重複するスケッチのフォルダに.inoを2つ置いた
main file missingフォルダ名と.inoの名前が違う
Partition SchemeのCustomが効かない開いているスケッチのフォルダにpartitions.csvがない

NS4168でアンプ基板を作る

モジュールで動いたので、マイクを外したアンプだけの基板を設計し、JLCPCBで製造しました。

項目内容
アンプICNS4168(ESOP-8)
層数2層
サイズ25mm × 18mm
電源3.3V
入力J1(6ピン)
出力J2(2ピン、スピーカー用)
製造JLCPCB(部品実装まで)

回路の要点

部品値役割
U1NS4168(ESOP-8)I2S入力のD級アンプ
C1 / C2 / C3100µF / 1µF / 100nF電源のパスコン
R110kΩCTRLをVDDへ(右チャンネル)
R3〜R533ΩI2Sの3本に直列
R61kΩシャットダウン制御用
FB1 / FB2フェライトビーズ出力のEMI対策
C4 / C51nF出力のEMI対策

パスコン(100µF + 1µF)と、出力のビーズ(1000Ω)+ 1nFは、データシート(NS4168 データシート Apr.2023 V1.2、Nsiway)の記載どおりです。

部品表

JLCPCBで実装してもらう前提で、LCSCの部品番号を付けています。

部品値LCSC区分
U1NS4168C910588Extended
C1100µF 1206C15008Basic
C21µF 0603C15849Basic
C3100nF 0603C14663Basic
C4, C51nF 0603C1588Basic
R110kΩ 0603C25804Basic
R3〜R533Ω 0603C23140Basic
R61kΩ 0603C21190Basic
FB1, FB2BLM21AG102SN1D 0805C85835Extended

電源を3.3Vにした理由

NS4168のデジタル入力のしきい値は、電源電圧に比例します(VIH = 0.7 × VDD)。ESP32の出力は3.3Vです。

電源(VDD)必要なVIHESP32の出力判定
3.3V2.31V3.3V規格内
5.0V3.50V3.3V規格外

モジュールは5Vで動いていましたが、基板にするなら規格内に収まる3.3Vにしました。レベルシフタも要りません。

出力は下がります(データシート値:3.6V・4Ωで1.2W、5V・4Ωで2.5W)。

注意

データシートには、電源電圧の記載が2通りあります。特性一覧は3.0〜5.5V、絶対最大定格は2.8〜5.0Vです。

絶対最大定格のほうが狭いのはおかしいので、5V以下で使うのが無難だと判断しました。

CTRLピン

CTRLは、電圧で3つの状態を切り替えます。

CTRL電圧動作
1.5V〜VDD右チャンネル
0.9〜1.15V左チャンネル
0〜0.4Vシャットダウン(1µA)

ESP8266Audioはモノラル音源を左右両方に出すので、どちらのチャンネルでも同じ音になります。R1でVDDにつないで右チャンネルにしました。

I2SはI2Cではない

名前が似ていますが別物です。

項目I2SI2C
用途音声データセンサーなどの制御
信号線BCLK・LRCLK・SDATAの3本SCL・SDAの2本
プルアップ抵抗不要必要

発注前の見直しで見つかったミス

発注前に、回路図・BOM・ガーバーをそれぞれ見直しました。8件見つかりました。

対象見つかったミス対処
回路図BCLKとLRCLKが、U1で入れ違っていた入れ替えた
回路図C3が100µFになっていた100nFに直した
BOM100nFに選んだ部品(C85864)の在庫が23個しかなかったC14663に替えた
BOMビーズの番号(C16902)が1206サイズで、0805のフットプリントに載らないC85835に替えた
BOMアップロードするCSVの、LCSC番号の列が空だった番号を入れて作り直した
BOM抵抗とコンデンサが、すべてExtended部品だったBasic部品に替えた
レイアウト出力の配線が0.15mmと細かった太くしてから発注した
レイアウトスピーカーのコネクタが出力から遠い直さずに発注した

回路図

NS4168は、Pin2がLRCLK、Pin3がBCLKです。ネット名を上から順に並べたら逆になっていました。

BOM

Basic部品に替えると、Extended部品にかかる追加の手数料を減らせます。直したあとの部品表が、上の「部品表」です。

レイアウト

ガーバーの銅箔のつながりを回路図と突き合わせて、ショートや未接続がないことを確認しました。

データシートは、出力の配線を「短く太く」するよう求めています。コネクタの位置は今回そのままで、SPK+が裏面のGNDベタの中を長く走っています。次に作り直すときは、コネクタを出力の近くに移すつもりです。

届いた基板での動作確認

届いた基板をESP32 DevKitにつないで確認しました。スケッチはブレッドボードのときと同じものです。

基板のJ1信号ESP32
13V33V3
2LRCLKGPIO25
3BCLKGPIO26
4SDATAGPIO22
5SDGPIO27(シャットダウンを試すときだけ接続)
6GNDGND
確認したこと結果
3.3V給電での再生鳴った(44.1kHzのMP3、ESP32の3V3から給電)
ノイズ気にならなかった(NoDAC出力のときのサーという音と比べて)
音量十分だった(モジュールは5Vだったが、3.3Vでも足りた)
SDピンでのシャットダウン音が止まった
シャットダウン中の消費電流未測定

SDピンでシャットダウンできるか

SDピンは、R6(1kΩ)を通してCTRLにつながっています。CTRLはR1(10kΩ)で3.3Vに引き上げてあります。

GPIO27の状態CTRLの電圧アンプ
入力(Hi-Z)3.3V動く
Low出力約0.3V(計算値)シャットダウン

テスト用に、440Hzの連続音を出したまま、SDピンだけを切り替えるスケッチを書きました。切り替える部分は次のとおりです。

Arduino SDピンを切り替える部分

constexpr int PIN_AMP_SD = 27; // 基板の SD へ
void setAmp(bool on) {
if (on) {
pinMode(PIN_AMP_SD, INPUT); // Hi-Z。R1のプルアップでCTRL = 3.3V
} else {
digitalWrite(PIN_AMP_SD, LOW); // 先にLowを用意してから出力にする
pinMode(PIN_AMP_SD, OUTPUT); // CTRL = 約0.3V
}
}

SDピンをLowにすると、音が止まりました。ESP32はI2Sの信号を出し続けているので、止まったのはアンプ側です。

補足

CTRLの電圧(約0.3V)は計算値で、実測はしていません。シャットダウンの上限は0.4Vなので、余裕は約0.1Vです。

まだ確認していないこと

項目状況
モジュール(5V)での16kHz再生未確認。自作基板では再生できた
AudioOutputInternalDACを使った内蔵DAC出力未検証
シャットダウン中の消費電流、CTRL電圧の実測未測定
スピーカーのコネクタを出力の近くに移した基板次に作り直すときに対応

まとめ

  • ESP32の内蔵フラッシュ(LittleFS)にMP3を入れ、ESP8266AudioとI2Sアンプで再生できた
  • 無音の原因は、ピンの取り違え(DATAとSDATA)だった。サンプリングレートを疑ったのは、配線ミスの状態で試した結果だった
  • シリアルに「再生OK」と出ていても、音が出ているとは限らない。ライブラリを使わない最小のテスト(サイン波、内蔵メロディ)で、配線とソフトを分けて確認すると早い
  • 基板のデータは、発注前に回路図・BOM・ガーバーをそれぞれ見直すとミスが見つかる。今回は見直しで直したおかげで、届いた基板は1回目で動いた
  • NS4168は3.3V給電でも、おもちゃ用の小さなスピーカーには十分な音量が出た

参考リンク

子どものおもちゃに曲を入れたくて、ESP32と自作NS4168アンプ基板を作った記録 | ikko LABS