2023年10月20日金曜日

デジタルワットメーターをArduinoに繋いでみた。

 PZEM-051っていうDCのデジタルワットメータを解析している人がいて、Arduinoでデータを取り込めるらしいのでやってみた。PZEM-021でも良かったんだけど、PZEM-021だとシャント抵抗内蔵で20Aまで。PZEM-051だと50Aか100Aのシャント抵抗が外付けっぽい。

KT-D1384っていうそっくりなやつがあったんだけど、結局中身はPZEM-051だった。中華製なので出荷時期によって変わったりするかもしれないから注意なんだろうけど…

PZEM-051の裏面の蓋を開けてみるとこの基板はPZEM-051(v5.0)だった。PEACEFAIRっていうメーカー、一応PCに出力できるタイプのワットメータもAC、DC問わずにランナップされているけど液晶付きのやつをよく解析されてる気がする。
このモデルの場合はTM1621BっていうLCDコントローラとMCUの間のSPI通信を解析して実現しているっぽい。

GithubにはPower Meter LoggerとしてArduinoのスケッチも公開してくれているので、ありがたく利用させていただくことに。LoggerってなってるけどESP32のシリアルコンソールから流しているだけっぽい。

PZEM-051はテストパッドが出ているので、T1をCS_PIN、T2をCLK_PIN、T3をDATA_PINに接続すれば良い。間に保護用の抵抗入れたほうが良いかな?
回路を見るとVDDっていうテストパッドに3.3Vが来ているし、それでLEDのバックライトをつけたりしているので低消費電力な3.3Vマイコンなら電源もいただけるのではないかなということでAttiny1614で試してみることに。スケッチのサイズが4kbを超えるのでAttiny402は厳しそうだったので…
void loop() {
  static unsigned int updateRate = 200;//データ出力頻度
  static unsigned long lastUpdate = 0;
  if (lastUpdate + updateRate <= millis()) //updateRate[ms]周期で実行
  {
    lastUpdate = millis();//次の実行のために現在時間格納
    Serial.print(get_voltage());
    Serial.print(" V\t");
    Serial.print(get_current());
    Serial.print(" A\t");
    Serial.print(get_power());
    Serial.print(" W\t");
    Serial.print(get_energy());
    Serial.println(" Wh");
  }
  if (Serial.available() > 0) { // シリアルバッファチェック
    char in = Serial.read();
    if (in == 't') {
      unsigned int input = Serial.parseInt();
      if (input >= 50) {
        updateRate = input; //データ出力頻度変更
      }
    }
  }
}
そんでもってloopのところだけ上のように変更した。電圧、電流、電力、電力量をタブ区切りで出力できるようにして、出力頻度を変更できるようにした。
シリアルでt1000って送ると1秒間隔でデータが送られてくるような感じ。

ロギングしたいのであればこれにOpenLogを接続しても良いし、ESP32を接続してWiFi対応にしても面白いかも。ESP32は電力を食うので別なDCコンバータとかから電源を用意しないといけないかもしれないけど。
ちなみにRS485のModbus対応とかだったらLCDはついていないけどPZEM-017というのが存在するみたい。ただし200msぐらいでしか通信できないみたいなので、RS-232CとかでデータストリームするぐらいだったらPZEM-051を改造したほうがデータ更新頻度は高そう。


2023年9月10日日曜日

ESP32でトランシーバを作ってみた。

 前回、ESP32でI2S DACとI2Sマイクを試してみたのでESP32で無線LANトランシーバを作ってみることに。ESP-NowやWiFiのUDP通信を使ってトランシーバを作っている人がいたのでとりあえずこの方のプログラムをArduino環境でビルドして使ってみることに。ESP-Nowを使えばWiFi APがないところでも気軽につかえるし、WiFi STAモードでWiFiルータに接続すれば中継機やメッシュWiFiルーターで距離を伸ばしたりもできるかも。

esp32-walkie-talkie

原理とか動作デモとかもGithubのYoutubeリンクを見るとわかり安く説明されていた。I2Sマイクのノイズ対策とかも解説してくれている。

このソフトウェアはPlatformIOで作られているようだったんだけど、Arduino IDEでもビルドできそうな気がしたので試しにやってみた。

まずは上のリンクからソースコード一式をZIPでまるっとダウンロード。

Arduino IDEで新しいプロジェクトを作成して(今回はesp32-walkie-talkie.inoにした)、最初に生成されるsetup()やloop()を消してまっさらにする。
srcフォルダ内のmain.cppの中身をテキストエディタで開いてそこにコピペする。そしたら一旦inoファイルを保存してArduino IDEを閉じる。

Arduino環境だとinoファイルと同じ名前のフォルダが作成されてその中にesp32-walkie-talkie.inoが入ってると思うんだけど、そこにsrcフォルダ内のmain.cpp以外のファイル4つをコピーする。

あとはlibフォルダの中の各ライブラリ(5種類)の中のsrcフォルダの中身だけ(cppファイルとhファイルだけ)を全部inoファイルと同じフォルダにコピー。同じフォルダに30個以上のファイルができてちょっと見にくくなるんだけどこの状態でArduino IDEからinoファイルを開くと他のファイルを認識してタブが増えているはず。

あとはconfig.hタブを開いて<< YOUR_SSID >>とか書いてあるところを直してやればビルドできるはず。(SSIDとパスワードは""で囲んで記入する)
ESP-Nowを使う場合もこの部分を直しておかないと変な文字<<>>が入っているせいでビルドできないのでここに適当な文字を""で囲んで入れておくと良いかも。そして
#define USE_ESP_NOW
のところのコメントアウトを外すとESP-Nowでダイレクト接続される。(WiFiルータなどのアクセスポイントは不要なので外とかで使いやすいかも。)

ちなみに今回はArduino IDE 2.1.1にESP32-Arduino-Core 2.0.6の組み合わせでビルド成功した。

ESP32トランシーバー

ピンアサインはとりあえずこのプログラムのデフォルトを使用した。
INMP441のほうは
GND→GND
VDD→3.3V
SD→21
SCK→18
WS→19
L/R→3.3V(スケッチにはGNDに繋げば良さそうに書いてあったんだけどGNDだと鳴らなかったのでHIGHに)

MAX98357Aモジュールの方は
Vin→5V
GND→GND
SD→22
GAIN→何も繋がない(ゲインはデフォルト設定)
DIN→5
BCLK→18
LRC→19

こんな感じでI2Sの一部のピンはマイクとDACで共用である。

あと送信ボタン(プレスボタン)は23番Pinと3.3Vの間にタクトスイッチなどのプッシュボタンを接続。23がHighになると音声をもう片方に送信。Lowになると受信。

あとはこれを二台用意して送信ボタンを押したほうの音声がもう片方から再生される。

音声は結構きれい。SAMPLE_RATEは変えられるようになってるので距離が遠くなってデータレートが落ちそうな場合は下げてみると途切れずに送れるようになるかも。遅延も一旦バッファしている割には少ない感じ。

PCのUSBに繋いでデバッグしてるときはマイクはかなりノイズを拾ってる感じだった。プログラム書き換え後とりあえずモバイルバッテリに繋いで試してみたらだいぶ良くなったのでとりあえずはモバイルバッテリでテストした。(本家のマイク用ノイズ対策回路は入れていないため。)

バッテリを搭載してケースに入れたら結構使えるかも。通知音とか鳴らせるようにしたら面白そう。フラッシュ容量はまだ余裕がありそうだし。いまはWAVEデータそのまま送っているので、Codec 2とかで圧縮して使用する帯域を減らしてやれば距離が離れても音声は聞きやすくなるかも。

スピーカーはジャンクノートPCのスピーカーを使ったけど秋月の小さいスピーカーとかDACとスピーカーが一体になっているM5StickC PLUS Speaker 2 HATとかを使ってもいいかも。音質は大きい方が良いのかもしれないけど…

ちなみにこのスケッチ、RISC-VのESP32-C3でも使用できた。ピン配は変更する必要があるけど、I2S DACとI2Sマイクを使用する設定であればESP32-C3でも普通に使える。ESP-NowでESP32とESP32-C3間で通話もできた。バッテリ駆動で作るならESP32-C3のほうが電池持ち良さそうだし。


2023年9月2日土曜日

ESP32でI2S接続のマイクを使ってみた

 前回はESP32に外付けDACをつけてみたけど、今回はI2S接続のマイクを試してみた。

使用したのはINMP441を搭載したモジュール。Aliexpressで180円だった。ESP32にI2Sで接続して音声データを取得できる。

IC自体はこっち面についているんだけど、音声取り込み口は反対側に空いてる基板の穴から取り込むみたい。

このマイクモジュールとしてはこっち側は表面ということに。中央の穴がマイクの穴。

早速ESP32に接続して試してみた。

ライブラリを使用せずにファイルシステムにPCMデータを保存したりするサンプルが多いんだけど、そのままPCで聞けるとテストが楽で良いなぁとおもって探していたら便利なライブラリを発見。

arduino-audio-tools

このライブラリは音声関係のエンコーダやデコーダも入っていて、サンプルにI2Sのマイクから取り出したWAVEデータをブラウザでストリーミングできるサンプルも入っていた。

このサンプルはstreams-i2s-webserver_wav.inoのWiFi接続用SSIDとパスワードを変更するだけで、あとは解説ページでも同じくINMP441で解説されているので図のように接続するだけ。書き込み終わったらシリアルコンソールに出たIPアドレスに接続するとブラウザにストリーミングの画面が出るので再生ボタンを押すだけ。

結構プツプツとノイズが入るんだけどこれは電源ラインなのかなぁ?ちなみにファイルシステムに録音するサンプルも試してみたけどそちらは結構いい感じだった。調べてみるとINMP441はこのサンプルのように16Bitで使うとノイズが目立つとかで、ファイルシステムに録音するサンプルでは32bitで取ってきて処理を入れているようだった。
ストリーミングでもそんな事ができるのかもう少し検証してみるかな…


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のデータシートを見ながら考えようかな。

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