方法論が定まったので、これでとりあえずArduino側のSketchに取り掛かることができそうだ。大雑把な方針として、
・サンプリング周期は1msとする。もっと縮める事も可能だけど、それは実装した後で考えることにする。
・周波数には依存しない形でサンプリングを行う。50Hz/60Hz決め打ちにして「17回サンプリング」でもいいのだが、たとえば前回のグラフ6を見ると、実際の1周期あたりのサンプル数は17とかその位になっている。筆者の居るのは50Hz地域だから、もう少しサンプル数が多くても良いのだが、これは実際には1回のサンプリングの間に2つのanalogRead()とdelay()を1つ、それとforループの制御が入った結果として所要時間が1msを超えている事に起因する。なのでdelay()ではなくdelayMicroseconds()を使ってもう少し待機時間を減らせばいいのだろうが、今後は更に処理が増える事や、もう少しサンプリング間隔を縮めたい場合などには正確な数字が変わってくる。
そこで信号の正負の反転のタイミングを見ることにした。図1の様に信号波形(黒線)は定期的に変動しており、これを一定間隔でサンプリング(赤線)するわけだが、どこかで値の正負が反転する場所(緑の破線)がある。これを検出してやれば、1周期分のデータが取り出せるだろう、という訳だ。
実際には図2の様に、1周期分のサンプリング後で、それを処理して通信する時間を設け、それが終わった次の周期から再びサンプリングを開始するといった仕組みにすることにした。処理が十分高速なら1周期で処理と通信が終わるからサンプリング頻度を多く取れる事になる。
・平均値の算出と実際の電圧/電流値への換算はArduino側では行わない。これは単純な話である。平均値の算出は、一周期分のサンプリングしたデータの絶対値を全部加算した上で、最後にサンプリング数で割れば済む話なのだが、除算(割り算)に時間がかかる上、整数のままでやると精度がかなり落ちるからだ。勿論、たとえば数値を100倍して割るとか色々方法はあるが、それならむしろ割り算は行わずにそのままサンプリング数を送ったほうが早い、という結論に達した。副次的効果として、毎秒あたり何回サンプリングしているかも一目で判るのが便利である。
実際の電圧/電流値への換算は更に困難である。これを真面目にやると、どうしても浮動小数点演算を使わざるをえないが、これがむちゃくちゃ遅いのは396回の結果で既に判っている。勿論、Arduinoに液晶などを取り付け、「手元で消費電力を見たい」という要望があれば何かしら考えざるを得ないが、今回の場合はワットチェッカーの画面をそのまま見れば済む話で、だったらPC側でやれば済む話である。
というわけで、方針が定まったところで実装をしてみたが、説明する前にもうひとつ修正点を。ここまでは398回で紹介した回路(http://news.mynavi.jp/photo/column/sopinion/398/images/fig01l.jpg)をベースに実装したが、これは120V/60Hzのアメリカにおける測定を前提としたもので、日本の100V/50・60Hzだとちょっと無駄が多い。どこが無駄か? というと、電圧出力(LM2902のPin14)に分圧回路を挟んでいることだ。元々の回路はPeak-to-Peakが340V(平均して120Vなので、電圧そのものは120V×√2で±170Vになる)であることを想定して、1:2の分圧回路を構成している。これだと、最大で340Vの電位差が231.3Vほどになる。ところが日本だと100Vだから、Peak-to-Peakで280V程度で、この分圧回路がなくてもいけそうだ。実際前回の結果でもMaxは523程度でしかない。ArduinoはA/Dが10bit精度で最大1023まで取れるから、半分程度しか使っていない計算になる。実際このまま測定を行ったところ、101.2VがArduinoの値で86~87程度にしかならず、ちょっと精度が荒すぎる。そこであっさり分圧回路を抜いて図3の様な構成にしてみたところ、前回のList 1の結果でMax:773、Min:189でDC Biasは481、101.5VがArduinoの値で135前後と、振幅が1.5倍程度になった。これ以上にするのは(技はあるが)ちょっと難しいので、まぁ妥当な線だろう。
ということで、これをArduinoに実装したのが下のList 1である。これを実行するとPhoto01の様な結果がシリアル側に送出される。サンプル数も22~23個とまずまずであった。
(続く)
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(volSum);
Serial.print(":");
Serial.print(ampSum);
Serial.print(":");
Serial.println(numSample);
while(1)
{
vol = analogRead(VOLPIN);
if (vol-VOLBIAS<0) break; /* 次のシーケンスの取得待ち */
delay(1);
}
flag++; /* flag = 2:次のシーケンス待ち */
}