2023年8月26日土曜日

ESP32でMAX98357Aを使ってみた

 I2S接続のモノラルなアンプ付きDACがAliexpressで155円だったのでポチってみた。送料は一応このDAC2個の他に3点入れても170円だった(トータル1000円かからないお買い物)

MAX98357A搭載のモジュールなんだけど、1チップでI2SのDACとD級アンプが内蔵されていてスピーカーと直結できる模様。モノラルなのでステレオで出したい場合は2個必要らしい。一応2個買ったけど。

MAX98357A
見た目はAdafruitのものとほぼ同じである。基板の色が違うぐらいしか…
デフォルトでは5Vで動作させるとモードがLRミックスでゲインが9dbに設定されているっぽい。

さっそくESP32に接続して使ってみた。

ライブラリは有名所っぽいのが2種類あったのでまずはESP32-audioI2Sを試してみた。
こちらのライブラリは簡単にMP3なネットラジオを再生できるようなサンプルがあったのでそのまま試してみた。
しかしビットレートが高くなったとき?にプツプツと切れるっぽい。ESP32でストリーミングデータを受信しながらMP3デコードはちょっと厳しいのかな?

ライブラリもデュアルコアのESP32にしか対応していないっぽいし…

念のため別な方のライブラリも試してみることに。
ESP8266AudioはESP-WROOM-02でも音声が再生できるようなライブラリ。内蔵DACにも対応している。
ESP32でこのライブラリを使う場合のデフォルトピン配置は
LRC→25
BCLK→26
DIN→22
になっていた。このモジュールはアンプ内臓なのでVinは5Vに接続した。

こちらにはWebRadioというサンプルがあったのでそのまま使ってみた。しかしブラウザからシリアルコンソールに出たIPにアクセスしてもブラウザに"ERR_EMPTY_RESPONSE"というエラーが出て設定画面が開けない…

色々探していると、サンプルのweb.cpp内の処理でflush()関数がESP32 Arduino Coreのバージョン1と2で動作が異なるのに起因しているっぽい。このサンプルは1.0.x向けに作られているのでここらへんを2.0.xで動くように作り変えないといけない。GitHubのフォーラムに修正方法が載っていた。
ということでweb.cpp内の
    client->flush();
ってなってるところ2箇所を
    while (client->available()) {
      client->read();
    }
のように変更した。
Coreのv1ではflush()関数がバッファクリアしていたのに対して、v2ではflush()関数が処理が終わるのを待つような仕様になったためらしい。なのでバッファが0になるまでreadでバッファをクリアしている。(Serialも同じような仕様変更あったな…)
この変更をしたらブラウザで設定画面が開けるようになった。

同じラジオ局で試してみたがこっちのほうが安定性がいいかも。
最初の方はプツプツ切れたりするんだけど安定するとちゃんと再生できてるっぽい?

ちなみにESP32のI2S機能はGPIO Matrixに対応しているっぽいのでピンは入力専用ピン以外だったらどこでも使えそう。ライブラリによって微妙にピン設定が違うのでちょっとハマったけど、好きなピンに設定してしまえば良さそう。wclkPinはモジュール上はLRCだったりとか呼び方も微妙に違ったり…

さすが外付けDAC。ジャンクノートPCのスピーカを繋いで使ってみたけど音質は結構クリア。簡易的なWebラジオとして使う分には十分なんじゃないかな。


2023年8月5日土曜日

ESP32でWiFi SDカードリーダを作ってみた。

 前にESP32でFTPサーバを立てられるようにしていたんだけど、ESP32側で生成したログファイルとかをダウンロードするだけならブラウザ経由でダウンロードしたほうが手っ取り早いんじゃないかということでWeb版のファイルマネージャーを作ってみた。

SDカードはSD_MMC.hで高速にアクセスできる配線にしてある。起動時にプルアップしておけないIOは起動後にプルアップするようにGPIO27でプルアップしてみた。

#include <WiFi.h>
#include <WebServer.h>
#include "SD_MMC.h"
#include "esp_mac.h"  //MACアドレス取得用

#define WiFi_password "12345678"
#define WiFi_ch 3
#define SD_PULLUP 27

WebServer server(80);
File uploadFile;

String htmlHeader() {
  return R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESP32 SD_MMC File Manager</title>
</head>
<body>
<h2>ESP32 SD_MMC File Manager</h2>

<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="upload">
<input type="submit" value="Upload">
</form>
<hr>
)rawliteral";
}

void listFiles() {
  String html = htmlHeader();
  File root = SD_MMC.open("/");
  File file = root.openNextFile();
  while (file) {
    String name = file.name();
    html += "<p>";
    html += name;
    html += " [<a href=\"/download?name=" + name + "\">Download</a>]";
    html += " [<a href=\"/delete?name=" + name + "\">Delete</a>]";
    html += "</p>";
    file = root.openNextFile();
  }
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleDownload() {
  if (!server.hasArg("name")) {
    server.send(400, "text/plain", "Bad Request");
    return;
  }
  String filename = server.arg("name");
  String path = "/" + filename;
  File file = SD_MMC.open(path);
  if (!file) {
    server.send(404, "text/plain", "File Not Found");
    return;
  }
  server.sendHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
  server.sendHeader("Content-Type", "application/octet-stream");
  server.sendHeader("Connection", "close");
  server.streamFile(file, "application/octet-stream");
  file.close();
}

void handleDelete() {
  if (!server.hasArg("name")) {
    server.send(400, "text/plain", "Bad Request");
    return;
  }
  String filename = "/" + server.arg("name");
  if (SD_MMC.exists(filename)) {
    SD_MMC.remove(filename);
  }
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleUpload() {
  HTTPUpload& upload = server.upload();
  if (upload.status == UPLOAD_FILE_START) {
    String filename = "/" + upload.filename;
    uploadFile = SD_MMC.open(filename, FILE_WRITE);
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    if (uploadFile) {
      uploadFile.write(upload.buf, upload.currentSize);
    }
  } else if (upload.status == UPLOAD_FILE_END) {
    if (uploadFile) {
      uploadFile.close();
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(SD_PULLUP, OUTPUT);
  digitalWrite(SD_PULLUP, HIGH);
  if (!SD_MMC.begin()) {
    Serial.println("SD_MMC Mount Failed");
    return;
  }
  if (SD_MMC.cardType() == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }
  Serial.println("SD_MMC Ready");
  uint8_t macaddr[6];
  esp_read_mac(macaddr, ESP_MAC_WIFI_SOFTAP);  //WiFi Macアドレス読み込み
  char SSID[14];
  sprintf(SSID, "ESP32-%02X%02X", macaddr[4], macaddr[5]);
  WiFi.softAP(SSID, WiFi_password, WiFi_ch);  //SoftAPを起動
  server.on("/", listFiles);
  server.on("/download", handleDownload);
  server.on("/delete", handleDelete);
  server.on(
    "/upload", HTTP_POST, []() {
      server.sendHeader("Location", "/");
      server.send(303);
    },
    handleUpload);
  server.begin();
}

void loop() {
  server.handleClient();
}

一応アップロード機能もついてるのファイルアップロードもできる。ディレクトリは面倒なのでとりあえず非対応で。ログファイルを生成してダウンロードする用途だったら十分使えそうなレベル。スマホとかでもそのまま使えるのでFTPより便利かも…

2023年7月2日日曜日

JNHのキャッシュ付きM.2 SSD 2TBを買ってみた。

 最近SSDの空き容量が少なくなってきたのでM.2のSSDを探していたところ、JNHの2TBのSSDがリード7200MB/s、ライト6700MB/sなのに13500円ぐらいで売っていたのでポチってみた。Gen4対応だし、最近流行りのコスパ最強YMTC製NAND搭載のやつ。

S720D-2TNVHS
聞いたことないメーカーだったけどパッケージは普通にいい感じ。
S720D-2TNVHS
TBWも1500TBとこの価格にしては高め。そしてDDR4のDRAMキャッシュ付き。
前回購入したMicronのSSDは初めてキャッシュなしを買ってみたんだけどやっぱりもたつく時があったので今回はキャッシュ付きSSDを購入してみた。
S720D-2TNVHS
PS5対応となっているのでPS5対応でよく採用されているサイズのヒートシンクもついていた。
B550I AORUS PRO AX
早速取り付けてみた。このヒートシンク、B550I AORUS PRO AX付属のヒートシンクと大きさがほぼ同じサイズなのでヒートシンクを付け替えることなくそのまま装着できた。
B550I AORUS PRO AX
そんでもって上につくヒートシンクもそのまま取り付けられる優れもの。ただ結構熱が気になるけど…

CrystalDiskMark
ベンチマーク的にはスペック通りの速さ。
とりあえずSteamのゲームを移動しておこうかな。

2023年7月1日土曜日

USBで12VのPC用ファンを動かす。

 最近暑くなってきたのでUSB扇風機を使う機会が増えてきたんだけど、余ってる120mmのケースファンをUSBで駆動できたら活用できるんじゃないかと思って試してみた。

しかしPC用のケースファンって12VなのでUSBの5Vに直結すると風量がだいぶ下がってしまうんじゃないかと。ただでさえ最近は静音系のファンが多くて回転数も1200~1500rpmが多い気がするし。ということでちょっと前にESP32がモータを駆動するときにリセットしてしまう対策に使ったDCコンバータが何個か余っていたので昇圧DCコンバータを使ってみることに。

ちなみにUSB接続で昇圧DCコンバータを使って12Vファンを動かすアダプタはAinex(CA-USB12VA)とかサンワサプライ(TK-PWFAN6)からも出ているっぽい。

USB2.0端子だと一応規格の上限が0.5A程度なのでDCコンバータの効率にもよるけど、12Vの1700rpmぐらいのファンまでだったら規格内で回せそうな気がする。

SX1308昇圧DCコンバータは電圧調整用の可変抵抗が通常と反対(時計回しで電圧が下がる)なのでややこしい。ということで可変抵抗を反対に取り付けてみた。(1番ピンと3番ピンを入れ替え)これで調整しやすくなった。

USBケーブルは要らなくなったUSBケーブルをカットして使ったんだけど、スイッチ付きUSBケーブルを使えばよかったかも。ちなみにダイソーとかでも売っているとか。通信はできなくて良いのでダイソーのでもいいかも。

ファン側の端子は昔作ったファン停止アダプタ用に買ったのが余っていたので最初こっちで作ってみたんだけど、ファンが途中で抜けたりするとショートして危ないので余っていたファン延長ケーブルをカットして使った。

とりあえず組み立てたらまずは電圧調整。USBに接続して出力の電圧が12Vになるように可変抵抗を調整する。あとはファンを接続するだけ。

これ、電圧調整のところを回せばファンの回転数を調整できるんだけど、12V以上にも調整できてしまうのが怖い。ここは可変抵抗に直列で抵抗をいれることによって対策できそうなのでSX1308のデータシートを見ながら考えようかな。

あとやっぱりファンガードは付けた方が良い気がする。当たると結構痛い。

2023年6月26日月曜日

QcK miniを新しくしてみた。

 マウスパッドとしてSteelSeriesのQcKを使ってからずっとQcK miniを使い続けているんだけど、最近端っこが剥がれかけてきたので新しいのをポチってみた。
Amazonの購入履歴を見ると前回購入は2015年5月となっていたので実に8年も使っていた…
たしかに洗うといわゆるQcK汁が出てきて匂いがやばいのだがもうこんなに使っていたとは。

いろんなマウスパッドが出ているっぽいので一通り探してみたんだけど、結局なんだかんだコスパが良さそうなのでQcK miniをポチってしまった。

前回のパッケージは覚えていないけど丸まって届くスタイル。
裏面にもロゴが印字されていた。前回購入したのは裏側は無地だったのに。少し進化している?
表面にはSteelSeriesのロゴだけになっていた。前回購入品はSteelSeriesという文字も印刷されていたんだけど、新しいのはロゴだけに変更になったのかな。

さわり心地は安定のQcK。匂いはだいぶ気にならなくなった気がする。新しいときは手が臭くなるようなイメージが合ったので…
これならもっと汚れる前に頻繁に買い換えようと思う。
キーボードもマウスもRazerになったがやっぱりこのマウスパッドは縁にステッチがない分使いやすいので使い続けていきたいな。

今回は8年ぐらい使ってしまったけどもっと短い頻度で変えるべきだったな。

2023年6月18日日曜日

GaN搭載 USB急速充電器を買ってみた

 TypeCで充電するタイプのデバイスが増えてきたのでType Cが2ポートある充電器がほしいと思っていたんだけど、たまたまセールしていたのでポチってみた。

S-TR-255JP
GOKiiっていうよくわからないメーカーなんだけど、GaNで45Wでセールでお安かったので…
S-TR-255
お値段の割にはパッケージもしっかりしている感じがある。
GOKii S-TR-255JP
質感は結構いい感じ。マットな手触りで傷が付きにくそう。
GOKii S-TR-255
出力はこんな感じ。2ポートを使うと25Wと20Wに切り替わるらしいのでiPhoneの急速充電もオッケー。
GOKii S-TR-255JP
電源に指すとLEDが青白く光る。光具合も程よい明るさで、光り方も均一で安っぽさはない感じ。
Aukeyの65Wと比較。GaNの45Wにしては少し大きい?
幅も少し広め。コンセントは折り畳める。

まぁ1200円ならいい感じかなぁ。出張のときは小さいのを持って言ってるし。


2023年5月7日日曜日

LAN-W300N/RをUSB電源で使ってみた。

 野外でWiFiルータを使いたいということで、野外だと主に2.4Ghz帯のWiFiじゃないと使いにくい(5.6Ghz以外は電波法違反)ので2.4GHz帯しか使えないLAN-W300N/Rを持ち出してみた。まぁESP32-CAMで遊ぶためなので2.4GHzだけで十分。

USB-C対応のモバイルバッテリだとUSB PD Decoyを使うと普通に12Vが出せるので問題がないんだけど、今回はこの前購入したSX1308モジュールが余っていたので5Vから昇圧してみることに。一応LAN-W300N/Rの純正ACアダプタは12V0.5Aなのでスペック的には大丈夫そう。まぁそもそもルータのCPUは3.3Vなのでここまでの昇圧は必要なさそうだけどLAN-W300N/RにPSP用の給電ケーブルで5Vで給電したら調子が悪かったので結局12Vまで昇圧してみることに。

SX1308モジュールにUSBケーブルをハンダして、出力が12Vになるように調整。DCジャックを取り付けてモバイルバッテリーで供給してみたら普通に起動できてしまった。消費電力的に大丈夫なのかと思って昇圧DCコンバータを触ってみてもほんのりあったかいぐらいで問題なさそう。一応SX1308自体は2Aまで行けるらしいけどこの無線ルータの純正ACアダプタが12V 0.5Aなので結構余裕そう。

コンパクトにしたかったし、電圧を変更できないように面実装タイプの抵抗に置き換えた。抵抗サンプルブック便利。この個体はVrefが0.615VぐらいだったのでR1が10kΩ、R2が510Ω+20Ωに設定した。

USBチェッカで電流を測ってみたけど300mAぐらいだった。この無線LANルータのエネルギー消費効率は2.3Wらしいけど昇圧回路通しても消費電力は1.5Wぐらいということに。変換効率91%以上としてもなかなか低消費電力。たまに少し電流が流れることがあるのでこの電流計では見れないぐらいのスパイクがあるのかもしれないけど。(WANにネット回線を繋いでWiFiクライアント1台で測定)

WANにLANケーブルを指してDHCPのルータモードで起動しても普通に通信できた。iPhone(20Mhz/144Mbps)でスピードテストすると86Mbpsぐらいは出ていた。ちなみにスピードテスト中は電流も5Vで450mAぐらい流れる

チャンネルボンディングの設定の比較もしてみた。詳細設定→無線LAN設定→詳細設定(上級者向け)でチャンネル幅を「Auto 20/40 MHZ」に変更するとチャンネルボンディングを有効にすることができる。デフォルトは20MHzだった。20MHzに設定するとPCからは144Mbpsとして認識された。40Mhzの場合は300Mbpsとして認識される。スピードテストで比較すると、20MHzのときは先程のiPhoneのときと同じく80Mbps付近、40MHzのときは95Mbpsぐらいまで出ていた。LANポートが100Mbpsまでなのでもはやこのルータの限界付近まで出てるっぽい。

ちなみにLAN-W300N/Rのファームウェアは最新の2.34にアップグレード済み。このルータはいろいろな脆弱性が報告されていたので最新で使わないと危ないかも。さらに色々設定変更して使用している。まぁ今回はオフラインで遊ぶ予定なのであんまり心配しなくてもいいけど。
無線LAN設定→基本設定→マルチSSIDで「logitegameuser」は無効にしておいたほうがいいかも。(WEPはガバガバなので)
無線LAN設定→セキュリティ設定はWPA2(AES)固定が良いかも。(それ以外だとiPhoneでセキュリティー警告が出てしまう。)

今回は部品が余っていたので作ってみたけど、既製品もあるっぽいのでケースとかを考えると既製品のほうがお得かも。

そういえばLAN-W301NRのACアダプタはLA-5W-5Sで、端子は同じだけど5V1Aなので注意。LAN-W300N/DRはLA-10W5S-09で5V2Aだし。DCコンバータ入ってるとはいえ全部同じ端子でACアダプタ違うのは危ない気がする。5Vタイプのルーターに関してはPSPの給電ケーブルがそのまま使えそうだけど。