AmbientでIoTをはじめよう
-
Full text forwarding http://pages.switch-science.com/letsiot/powermonitor/
English version : http://forum.m5stack.com/topic/717/let-s-start-iot-with-ambient-english-versionM5Stackとクランプ式電流センサで交流の電流値を測る
「AmbientでIoTをはじめよう」、第17回は、M5Stackとクランプ式電流センサを使って、機器が使用する電流値を測ります。
家庭やオフィス、工場などでは非常に多くの機器が電気で動いています。機器の電気の使用状態を測り、データを蓄積して見える化することは、省エネやコスト削減に効果的です。さらに、見守りサービスや設備の稼働率改善などのサービスに発展させることもできます。
クランプ式電流センサ
クランプ式電流センサは電流が流れる線を磁気コアではさんで電流を測定します。電線を流れる電流によって磁気コア内に磁束が発生し、それに応じて2次巻線に2次電流が流れます。そこに抵抗をつなぎ、抵抗の両端の電圧を測ります。この方法は交流しか測れませんが、二つに分かれた磁気コアで電線をはさむことで、測定対象の回路を切らずに電流が測定できます。
上の写真のセンサは今回使用する「クランプ式AC電流センサ30A」です。データシートによれば、測定対象の電流に対して2000:1の2次電流が得られます。20Aの電流であれば10mAの2次電流が流れます。
測定回路
クランプ式電流センサを使って電流を測定する回路は次のようになります。
クランプ式電流センサの2次巻線に51Ωの負荷抵抗をつなぎ、その両端の電圧をADコンバーターMCP3004で測ります。交流を測定するので、負荷抵抗の両端の電圧はプラスとマイナスになります。4.7kΩの抵抗二つで電源電圧3.3Vの半分の電圧(1.65V)を作り、1.65Vを中心にして電圧が変化するようにしました。
MCP3004は10ビットのADコンバーターで、入力が0VからVrefV(=3.3V)のときに0から1023の値が読めます。ADコンバーターで読んだ値をiとすると、負荷抵抗の両端の電圧Vrは次のようになります。
Vr = (i - 512) / 1024 * 3.3
負荷抵抗が51Ω、クランプ式電流センサの1次電流と2次電流の比率が2000:1なので、1次電流Iは次のように計算できます。
I = Vr / 51 * 2000
ブレッドボード上に作った測定回路です。
センサ端末に必要な部品をまとめました。
部品 個数 M5Stack Basic 1個 クランプ式AC電流センサ30A 2個 オーディオジャック+ピッチ変換基板のセット 2個 ジャンパワイヤ(オス~オス) 1セット 普通のブレッドボード 1個 MCP3004 1個 抵抗(51Ω) 2個 抵抗(4.7kΩ) 2個 交流の電流を測定する
日本の家庭で使われている電気は100Vの交流で、東日本は50Hz、西日本は60Hzです。交流の電流値を測るために、まず1m秒間隔で100回、電流値を測定してみます。このように周期的に値を測定する処理をサンプリングといいます。1m秒 x 100回 = 100m秒なので、50Hzだと5周期分、60Hzだと6周期分測定します。
測定プログラムは次のようになります。
/* * M5Stackの電流センサーテスト * MCP3004/3008で1m秒毎に100回サンプリング * 3秒毎にサンプリング値や電流値をシリアルに表示 * 値が正しそうか、ノイズがのっていないかをシリアルプロッタで確認 */ #include <M5Stack.h> #include <SPI.h> #include "MCP3004.h" #define TIMER0 0 #define SAMPLE_PERIOD 1 // サンプリング間隔(ミリ秒) #define SAMPLE_SIZE 100 // 1ms x 100 = 100ms hw_timer_t * samplingTimer = NULL; const int MCP3004_CS = 2; MCP3004 mcp3004(MCP3004_CS); const float rl = 51.0; // Load Resistance struct amp { short amp_ch[4]; } amps[SAMPLE_SIZE]; volatile int t0flag; void IRAM_ATTR onTimer0() { t0flag = 1; } // chのチャネルをサンプリングする void ampRead(uint8_t ch) { timerAlarmEnable(samplingTimer); for (int i = 0; i < SAMPLE_SIZE; i++) { t0flag = 0; while (t0flag == 0) { delay(0); } amps[i].amp_ch[ch] = mcp3004.read(ch); } timerAlarmDisable(samplingTimer); } void setup(){ M5.begin(); Serial.begin(115200); while (!Serial); SPI.begin(); mcp3004.begin(); samplingTimer = timerBegin(TIMER0, 80, true); timerAttachInterrupt(samplingTimer, &onTimer0, true); timerAlarmWrite(samplingTimer, SAMPLE_PERIOD * 1000, true); } void loop() { ampRead(0); for (int i = 0; i < SAMPLE_SIZE; i++) { Serial.println(amps[i].amp_ch[0]); } delay(3000); } //curlog2.ino hosted with ❤ by GitHub
1m秒間隔で関数onTimer0()を呼び出すタイマーを用意します。forループの先頭でフラグが0の間待つようにして、関数onTimer0()でフラグを1にすることで、1m秒間隔でforループを実行するようにします。forループの中でADコンバーターMCP3004を読み、値をバッファーに格納することで、1m秒間隔で電流値を測定します。
最初のプログラムは交流のサンプリングが上手くできているかを確認するために、サンプリングしたデータをプリントアウトします。
MCP3004をアクセスする部分はライブラリー化しています。ライブラリーも含めたプログラム全体はGithubに置きました。
動作確認
電源ケーブルは2本の線でできています。2本の線をクランプで挟んでも、行きと帰りの電流が打ち消しあって磁束が発生せず、2次電流は流れません。写真のように2本の線の一方だけを挟んで測定します。
プログラムをビルドしてM5Stackに転送し、いつものシリアルモニタではなく、シリアルプロッタを起動します。
電源ケーブルの先に適当な電気製品をつなげます。実験では1200Wのドライヤーをつなげました。ドライヤーを動かしたり止めたりすると、シリアルプロッタに次のような波形が描かれます。波形はつないだ機器によって異なりますが、交流波形が測定できているのが確認できました。
交流の電流値を求める
プラスマイナスに振れる交流の電流値を求めるには、何周期か測定したサンプリング値をそれぞれ2乗して平均値を求め、平方根を取ります。
その部分の処理は次のようになります。最初のプログラムはサンプリングした値を確認するために値を全てバッファーに記録しましたが、電流値を計算するだけならその必要はありません。サンプリングするたびにサンプル値を2乗して足し込んでいき、最後に平均して平方根を求めます。この処理を例えば1分ごとに呼び出せば、1分ごとの電流値を測れます。
float ampRead(uint8_t ch) { int vt; float amp, ampsum; ampsum = 0; timerAlarmEnable(samplingTimer); for (int i = 0; i < SAMPLE_SIZE; i++) { t0flag = 0; while (t0flag == 0) { delay(0); } vt = mcp3004.read(ch); amp = (float)(vt - 512) / 1024.0 * 3.3 / rl * 2000.0; ampsum += amp * amp; } timerAlarmDisable(samplingTimer); return ((float)sqrt((double)(ampsum / SAMPLE_SIZE))); } //curlog2.ino hosted with ❤ by GitHub
交流の電力値
電力は電圧×電流です。家庭の交流の電圧は100Vなので、電流値に100Vをかけると電力値になるかというと、そうなりません。
交流の場合は電圧と電流の位相のずれがあるため、実際に使われた電力(有効電力)は有効電力=電圧×電流×力率
になります。今回は正確な電力値を求めるよりも、機器の電気の利用パターンを調べることを目的に、電流値を使って電力使用の傾向を把握することにしました。
電流値をLCDに表示する
1分ごとの電流値が得られたので、これをM5StackのLCDに表示します。
100件のリングバッファーを用意して、1分ごとに電流値を格納し、リングバッファー中の値をLCDに表示します。LCDに表示する部分のプログラムは次のようになります。
// リングバッファ #define NDATA 100 struct d { bool valid; float d1; float d2; } data[NDATA]; int dataIndex = 0; // リングバッファにデータを挿入する void putData(float d1, float d2) { if (++dataIndex >= NDATA) { dataIndex = 0; } data[dataIndex].valid = true; data[dataIndex].d1 = d1; data[dataIndex].d2 = d2; } #define X0 10 // データの値からy軸の値を計算する int data2y(float d, float minY, float maxY, int HEIGHT) { return HEIGHT - ((int)((d - minY) / (maxY - minY) * (float)HEIGHT) + 1); } // リングバッファからデータを読み、グラフ表示する void drawChart() { int HEIGHT = M5.Lcd.height() - 10; float mind = 0.0, maxd = 10.0; for (int i = 0; i < NDATA; i++) { if (data[i].valid == false) continue; if (data[i].d1 > maxd) maxd = data[i].d1; if (data[i].d2 > maxd) maxd = data[i].d2; } maxd *= 1.1; for (int i = 0, j = dataIndex + 1; i < (NDATA - 1); i++, j++) { if (data[j % NDATA].valid == false) continue; int d10 = data2y(data[j % NDATA].d1, mind, maxd, HEIGHT); int d11 = data2y(data[(j + 1) % NDATA].d1, mind, maxd, HEIGHT); M5.Lcd.drawLine(i * 3 + X0, d10, (i + 1) * 3 + X0, d11, BLUE); int d20 = data2y(data[j % NDATA].d2, mind, maxd, HEIGHT); int d21 = data2y(data[(j + 1) % NDATA].d2, mind, maxd, HEIGHT); M5.Lcd.drawLine(i * 3 + 1 + X0, d20, (i + 1) * 3 + 1 + X0, d21, RED); } } void loop() { unsigned long t = millis(); float a0, a1; a0 = ampRead(0); a1 = ampRead(1); putData(a0, a1); M5.Lcd.fillScreen(BLACK); drawChart(); while ((millis() - t) < PERIOD * 1000) { delay(0); } //curlog3.ino hosted with ❤ by GitHub
プログラム全体はGithubを御覧ください。
Ambientに送信して可視化する
1分ごとの電流値をAmbientに送って可視化します。
Ambientへのデータ送信は簡単です。setup関数でWi-Fiに接続し、Ambientオブジェクトをambient.begin()で初期化します。loop関数の中で電流値を測定し、ambient.set()でデータをセットし、ambient.send()に送信します。
Ambientに送信するのに関係する部分のプログラムは次のようになります。
void setup(){ // 諸々の初期化 WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } ambient.begin(channelId, writeKey, &client); // チャネルIDとライトキーを指定してAmbientの初期化 } void loop() { unsigned long t = millis(); float a0, a1; a0 = ampRead(0); a1 = ampRead(1); ambient.set(1, a0); ambient.set(2, a1); ambient.send(); while ((millis() - t) < PERIOD * 1000) { delay(0); } //curlog3.ino hosted with ❤ by GitHub
プログラム全体はGithubを御覧ください。
家庭の電流消費量を測る
日本の多くの家庭は単相3線式といって2系統で電力が供給されています。2系統の両方の電流値を測ることで家全体の電流の使用状態を測定できます。
実際に分電盤にクランプ式電流センサを取り付けた様子です。
取り付けはブレーカーを落とした状態で、十分気をつけておこなってください。
測定した電流値をAmbientで見ると、次のようなグラフが見られます。
チャート設定でグラフサイズを「large」にして、d1とd2を同じチャートで表示し、日付指定を設定しています。
グラフは著者の自宅の電流値です。系統1(青い線)に冷蔵庫が接続されていて、オン/オフを繰り返しながら常時動いているのが分かります。系統2にオイルヒーターがついていて、0時から6時ぐらいまで動いています。6時半ぐらいの大きなピークはオーブンレンジです。
M5Stack用プロトモジュールを使う
M5Stackにはオリジナルの拡張モジュールが開発できる「プロトモジュール」があります。ブレッドボードで動作確認した回路をプロトモジュールを使って作りました。電流センサーは4個まで接続できるようにしました。
M5StackはGPSモジュールなどの拡張モジュールで機能拡張できますが、プロトモジュールを使うと独自の機能拡張もできて、さらに応用範囲が広がります。
まとめ
電流計を家庭に設置すれば、家庭での電気の使用状況が可視化でき、
省エネ意識も高まるでしょう。毎日の傾向を調べれば、見守りサービスのデータとしても使えそうです。工場の工作機械の電気使用状況を調べれば、機械の稼働状況を可視化し、稼働率の改善などに役立てられそうです。
この記事はアンビエントデーターの下島が担当しました。
免責事項
記事は実際に実験をおこなった上で書いていますが、動作を保証するものではありません。また本記事を利用したことにより生じる損害についてスイッチサイエンスおよびアンビエントデーターは一切の責任を負いません。