はじめに
ども。6000円に釣られて2000円の材料買ってきました。
欲しかったものは、そんなに電流を食わない工作の電流を測れるやつ。よくあるUSB電力計でいいんじゃね?って声も聞こえるが、例えばESP8266と電池でどうこうするおもちゃを作ったとして、その電流が計れるようなやつが欲しかった。
電流を計るにあたって、
- 内部にホール素子があるICを噛ませて出力を得る
- シャント抵抗を噛ませてその電圧を計る
の2通りがあるが、今回はACS712というセンサを目にして、絶縁・低抵抗に惹かれてそのまま使うことにした。そしてそれを取り込むにはArduinoでもPICでも何でも良かったが、そのままワイヤレスで記録できたらオシャレということでナウいESP8266にした(しかも単体だとArduinoより安いしな)。これらの選択はすぐに後悔することになる。というか今回使用した部品それぞれが尽く相性が悪くて辛い。
ACS712について
メーカーの製品ページで得られる情報の引用。ACS712の電源は5V(3.3V非対応)。出力は無負荷時Vcc/2V、±1Aあたり±185mv変動する。FilterとGndにぶら下がってるコンデンサは内部増幅回路のローパス部分につながっており、これで立ち上がり時間を設定できる。
一方ESP8266は3.3V(5V非対応)、内蔵ADCは上限1.0Vである。困る。
試作
どんな問題が起こるかわからないので、組む前にブレッドボードでかんたんに試作することにした。
単純な分圧
1kと4.7kで分圧してESP8266のADCにブチ込むとおかしな値が出た。「Output Resistive Load : 4.7 kΩ」なので仕方がない。
オペアンプでボルテージフォロア
NJU7044D使用。50mA程度の負荷をかけたりかけなかったりして実験。
ESP8266が電力を食う瞬間に大きなノイズが乗っている気がする。コンデンサをたくさん挟んでもだめ。レギュレーターは500mAなので問題はないと思ったが、USBシリアル変換キットからの給電、ブレッドボード上の接触抵抗、給電機器の問題等色々心当たりがあるので困る。
ESP8266を排除
とりあえず転がっていたArduinoで試す。
・・・ACS712はそういうものと割り切ることにする。
ADC外付け化&どうせオペアンプ使うなら差動しようぜ
最終的な回路図。
- ICを書くの面倒くさいのでJPで代用
- ACS712の出力を2.5V弱と差動して10倍している(1kと1kの間に100くらいの可変抵抗挟むのが行儀いいんだろうが多回転VRなのでいいということにしている)
- 2.5V弱としているのはGND付近のオフセット電圧意識
- MCP3002まで5Vで動かして、そこから先はレベルシフトモジュールを使って3.3Vにする
- ESP8266のSPIとI2Cは両立できる
主要部品 | 個数 |
---|---|
ACS712 | 1 |
SOP変換基板 | 1 |
MCP3002 | 1 |
ロジック変換モジュール | 1 |
ESP-WROOM-02ピッチ変換キット | 1 |
NJU7044D | 1 |
ターミナルブロック2ピン青縦小 | 2 |
USBminiBブレッドボードキット | 1 |
I2C LCDキット | 1 |
5Vの入力がUSB給電そのままというのでここから先ハマる。
基板に移す
ガーッとやる。
なんか基板が別れているが、ターミナルが片面基板(穴径1.0mm)に刺さらなかったので、刺さる基板を用意した。オペアンプまで別基板になっているのは、ACS712が結局うまく使えなかった場合、最悪ADコンバーター以降は別に独立して電圧ロガーとしてなにかに使えるかもしれないという期待から。秋月のページでは紙エポと両面タイプ(穴径1.3mm)にしれっと刺さっている写真があったので騙された感すごい。
プログラム。無線での記録は今後の課題とする。
#include <SPI.h>
#include <Wire.h>
#include <stdio.h>
#include <string.h>
#include "LCD.h"
const int CSpin = 16;
const int pushSW = 0;
double Vcc_50 = 0;
void setup()
{
WiFi.mode(WIFI_OFF);
pinMode(pushSW, INPUT);
pinMode(CSpin, OUTPUT);
Serial.begin(115200);
Serial.println();
//MCP3002
SPI.begin();
SPI.setBitOrder(MSBFIRST);
//LCD
Wire.begin(4, 5);
LCDInit();
//Calibration
LCDSendMsg("cal...", 6);
int ch0 = 0;
int ch1 = 0;
for (int i = 0; i < 100; i++)
{
ch0 += analogReadSPI(0);
ch1 += analogReadSPI(1);
delay(10);
}
ch0 /= 100;
ch1 /= 100;
Vcc_50 = ADC2volt(ch0) * 2.0 + ADC2volt(ch1) / 5.0;
char msg[17];
snprintf(msg, sizeof(msg), "Vcc: %4.2fV", Vcc_50);
LCDSendMsg(msg, strlen(msg));
Serial.println(msg);
delay(1000);
}
int analogReadSPI(int ch)
{
digitalWrite(CSpin, LOW);
char byteMSB = SPI.transfer(0b01101000 | ch << 4) & 0b00000011;
char byteLSB = SPI.transfer(0b00000000);
digitalWrite(CSpin, HIGH);
return ((byteMSB << 8) | byteLSB);
}
inline double ADC2volt(int ADC)
{
return ADC * 3.3 / 1024.0;
}
inline double volt2amp(double sense, double ref)
{
return (sense - Vcc_50 * 5.0 + 10.0 * ref) / 1.85;
}
void loop()
{
static bool LCD_mode = false;
int ch0 = analogReadSPI(0);
int ch1 = analogReadSPI(1);
if (digitalRead(pushSW) == LOW)
LCD_mode = !LCD_mode;
char msg[17];
if (LCD_mode)
snprintf(msg, sizeof(msg), "current: %4dmA", (int)(volt2amp(ADC2volt(ch1), ADC2volt(ch0)) * 1000));
else
snprintf(msg, sizeof(msg), "1:%5.2fV2:%5.2fV", ADC2volt(ch0), ADC2volt(ch1));
LCDSendMsg(msg, strlen(msg));
Serial.println((int)(volt2amp(ADC2volt(ch1), ADC2volt(ch0)) * 1000));
delay(100);
}
で、ハマる。まず、USB給電なので、5Vが5Vではない。PCもモバブもACアダプタもだいたい5.15Vくらい出す。一応ESP8266でUSBの電圧を計れるようにしたのだが、ついている抵抗が割と5%ギリギリくらいまで誤差があるらしく、正確な値が取れなかった。そしてUSB-ブレッドボードのキットのポリスイッチが牙をむく。なんとたったの200mAしか許容されていない。普段は200mA行かないが、リセット時には行く。ポリスイッチは一瞬なら見逃してくれるのだが、プログラムをミスってリセットが多発するような自体になると普通に作動してくる。一度作動すると割と長いこと抵抗値が戻らない。
そういうことで色々手直しすることに。
手直しする
- USBコネクタ給電からQiコネクタ給電へ
- ADコンバーターへの給電を5.0Vから3.3Vへ(レベル変換が無意味化)
最終的には比較的実用に耐えそうな出力を得ることができた。ただ10mAオーダーのものを図るにはだいぶきつそうである。後日電源を6V以上で5Vをレギュレーター経由の出力にして、全体をケースにきちっと収めたものを完成形としたい。
教訓
- ちゃんとした電源を使おう。
- USB給電はちゃんとした電源ではない。
おわりに
これで計りたかったおもちゃの方を先に作って投稿すればよかったと後悔している。
参考
投稿者の人気記事
-
Asi_a
さんが
2021/02/27
に
編集
をしました。
(メッセージ: 初版)
-
Asi_a
さんが
2021/02/27
に
編集
をしました。
-
Asi_a
さんが
2021/12/25
に
編集
をしました。
ログインしてコメントを投稿する