さて、本来だと今回からWindows側プログラムに掛かる筈であったが、話はもう少し続く。前回紹介したSketchをちょっとだけ書き換えた(List 1)上で、前に紹介した温度センサー用プログラム(389回)をちょっと書き換え、今回のArduino側の相手をさせるようにしたものを作ってみた。とりあえずは結果が表示できればいいや、ということで割と細かいところはまだいい加減なのだが、それでも一応結果の取り込みは可能になった(Photo01)。ちなみにList 1の変更点は、データを送る前に1Byte、"z"を入れるようにしただけである。
一見成功、といいたいところだが、この状態におけるワットメーターの消費電力は11W(Photo02)。Photo01の15.4Wに近いのはこちらの数字(Photo03)である。この測定には、100円ショップで売っていた消費電力12W相当の蛍光灯を繋いでおり、結果力率はこんな具合(Photo04)。Photo01の15.4Wにこの65%を掛けると、実効消費電力は10.1Wということになる。Photo02の数字とはやや異なるが、まだArduinoの返す値から実際の電圧/電流値に変換する係数を煮詰めきっていないので、ここまで一致すれば結構良いと言うべきだろう。
ま、それはそれとして、力率をどうするかはちょっと大きな課題である。極めて個人的な話をすれば、筆者が測定したいのはPCの消費電力で、なので完成したらPCの電源を繋いで使うのが殆どの用途になる。この範囲で言えば、最近の電源は殆どがActive PFC(力率改善回路)を搭載しており、実際測定してみると力率が99.9%とか表示されたりするので、皮相電力の測定であってもそれほど問題はない。また、電気料金は実効消費電力ではなく皮相電力で請求されるので、電気料金を可視化するという観点で言えば皮相電力の測定が出来れば十分である(実際、Tweet-a-wattもこうしたコンセプトで作られている)。ただ、どうせならば一緒に実効消費電力を測定したいところである。
このためにはリアルタイムで力率を知る必要があるのだが、これがちょっと面倒である。基本的には電圧の振幅と電流の振幅の位相差をθとした時、COSθが力率となる。なので、電流と電圧の振幅の差をArduinoで簡単に測定できれば、これは計算できる。で、ふと思いついたのは、ワットメーターのオペアンプである。399回で、LM2902から信号を引っ張りだしたわけだが、元々LM2902には4つのオペアンプ回路が入っており、出力は1,7,8,14番ピンから取得できる。で、基板を眺めていると、1,14番以外に7,8番の出力もワットメーターのASICは取り込んでおり、ここにも何かしら信号が出ているようである。なので、ここから信号を取ればひょっとして力率が簡単に取得できるかもしれない、と思いついた。
というわけで、さっそく実験である。信号ピンを引っ張り出した上で(Photo05)、図1の様な配線を行ってみた。今回は電流出力に抵抗分圧回路を入れていないが、これは入れないとどのような値になるかを知りたかったからである。Pin 7/8の出力も同じである。
配線が終わったら、List 2の様なスケッチを実行し、シリアルコンソールを立ち上げて結果を見てみた。まずワットメーター自身をACに繋がないと、こんな具合にすべて0である(Photo06)。で、ワットメーターをAC100Vに繋ぐ(ワットメーターには何も繋がない)状態がこちら(Photo07)。で、蛍光灯を繋ぐと、こんな感じになる(Photo08)。
Photo06: ワットメーター自身が動いてないから、当然GNDレベルに落ちる。 |
Photo07: 電圧の変動は前に説明した通り。電流はまぁ一定値(DC Bias)。 |
Photo08: もう少し消費電力の多いデバイスを繋いで確認すべきかもしれない。 |
で、このままでは判らないので、数字を取り込んでExcelでグラフにしてみた。グラフ1が無負荷状態、グラフ2が蛍光灯を繋いだ状態である。とりあえずVolとAmpはいいとして、まずPin 7はどうもよく判らない。どちらのグラフでも、毎回測定開始直後のみ、Ampの出力と差があるのだが、その後は急速に差が減ってゆき、概ね5ほど異なる値のまま推移する。面白いのは、Ampに元々の10KΩと4.7KΩの分圧回路を入れると、Ampの値が300程度に減るのだが(これは正常)、これにあわせてPin 7の値も一緒に下がることである。色々回路を追っかけてみたりもしたが、どうもこれは力率とは違う用途に使っている様で、今回は役に立たなそうだ。
さて、問題はPin 8。グラフ1と2から、VolとPin 7の2つを抜き出し、どちらの値も-0.5~0.5に正規化したのがグラフ3とグラフ4である。見ると、どちらも微妙に位相があっていないようで、実はちゃんとあっていたりする。特に0付近に関しては値の変化が急だから、List 2のSketchでVolのデータ(Pin14)をサンプリングしてからPin 8のサンプリングをするまでに時間経過がありすぎでやや値がずれている可能性がある。そう考えると、これは単に電源の周波数を測定するための信号で、基本的にはVolの値に同期していると思われる。というわけで、力率は電圧値と電流値の位相差から推定するしかなさそうだ。
というわけで最後にお知らせ。来る12月3日・4日に東京工業大学大岡山キャンパスで開催されるMTM07に筆者も(また)出展いたします。ここで連載してる電力計を、もう少し完成させた状態で持ってくほか、もう一つ変なものを出展予定(今から作ります)。あと、以前レポートしたmbedのネットワークカメラも展示予定です。本当はもう一つ、編集部から頼まれてるネタがあるんですが、たぶん間に合いません(T_T)。という事で、興味有る方はおいで頂ければ色々説明などさせていただきます。
List 1:
#define VOLPIN 0
#define AMPPIN 1
#define VOLBIAS 481
#define AMPBIAS 491
int flag; /* 電圧の反転状態を定義 */
void setup()
{
Serial.begin(9600);
flag = 0; /* Flag初期値 */
}
void loop()
{
long numSample; /* サンプリング数 */
long vol, amp; /* 取得した電圧/電流の値 */
long volSum, ampSum; /* 合算した電圧/電流の値 */
numSample = 0; /* サンプリング数取得 */
volSum = 0;
ampSum = 0;
while(1)
{
/* 電圧と電流をサンプリング */
vol = analogRead(VOLPIN);
amp = analogRead(AMPPIN);
numSample++;
/* 両方0なら0としてBreak; */
if (!(vol||amp))
{
numSample = 1;
volSum = 0;
ampSum = 0;
break;
}
/* flagの値を判断 */
if ((flag==2)&&(vol-VOLBIAS>0))
flag=0; /* データサンプリング開始 */
else if ((flag==0)&&(vol-VOLBIAS<0))
flag++; /* 一度目の値の符号反転 */
else if((flag==1)&&(vol-VOLBIAS>0))
break; /* 二度目の値の符号反転 */
/* 取得した値を加算 */
if(flag!=2)
{
volSum += abs(vol-VOLBIAS);
ampSum += abs(amp-AMPBIAS);
}
delay(1);
}
/* 結果を出力 */
Serial.print("z");
Serial.print(volSum);
Serial.print("\t");
Serial.print(ampSum);
Serial.print("\t");
Serial.println(numSample);
while(1)
{
vol = analogRead(VOLPIN);
if (vol-VOLBIAS<0) break; /* 次のシーケンスの取得待ち */
delayMicroseconds(500);
}
flag++; /* flag = 2:次のシーケンス待ち */
}
List 2:
#define VOLTPIN 0
#define AMPPIN 1
#define PIN7 2
#define PIN8 3
int volt[100], amp[100], pin7[100], pin8[100];
void setup()
{
Serial.begin(9600);
}
void loop()
{
int lpCnt;
for(lpCnt=0;lpCnt<100;lpCnt++)
{
volt[lpCnt] = analogRead(VOLTPIN);
amp[lpCnt] = analogRead(AMPPIN);
pin7[lpCnt] = analogRead(PIN7);
pin8[lpCnt] = analogRead(PIN8);
delay(1);
}
for(lpCnt=0;lpCnt<100;lpCnt++)
{
Serial.print(lpCnt);
Serial.print(":");
Serial.print(volt[lpCnt]);
Serial.print(":");
Serial.print(amp[lpCnt]);
Serial.print(":");
Serial.print(pin7[lpCnt]);
Serial.print(":");
Serial.println(pin8[lpCnt]);
}
delay(1000);
}