前回、電圧と電流の計測(A/D変換)で250us毎にサンプリングをする部分を実装しました。
これだけでは、電力は不明なので、サンプリングした結果から電力計算をしたいと思います
A/D変換によりサンプリングされた結果は以下の配列に格納されます。
//サンプリングテーブルint myArrayA[SAMPLE_CNT]; //電流サンプリング値int myArrayV[SAMPLE_CNT]; //電圧サンプリング値この配列に格納されたデータを「void loop() 」関数内で計算します。
計算をするタイミングは、1CH~4CHの電圧と電流をサンプリングし終わったタイミングです。
前回のソースで言うと
case STATE_WAIT1: //しばらく待ち case STATE_WAIT2: //しばらく待ち case STATE_WAIT3: //しばらく待ち case STATE_WAIT4: //しばらく待ちの4か所。つまり、「しばらく待ち」状態になっている時に、計算をします。
コメントをわかりやすく書くと、、、
case STATE_WAIT1: //CH1用の電流と電圧のサンプリング完了後の待ち case STATE_WAIT2: //CH2用の電流と電圧のサンプリング完了後の待ち case STATE_WAIT3: //CH3用の電流と電圧のサンプリング完了後の待ち case STATE_WAIT4: //CH4用の電流と電圧のサンプリング完了後の待ちです。ちなみに、この待ち時間は、216.5ms(60Hz時)となります。
≪ソースコード≫
#include <TimerOne.h>//=====================================================//電圧67回 + 電流67回 + 待ち866回 = 1000回//1000回 + タイマー周期250us = 250ms//250ms * 4ch = 1秒で1巡する//この設定は60Hz用なので50Hzの場合は、カウントを修正する必要あり//#define SAMPLE_CNT (80) //50Hz用//#define WAIT_CNT (840) //50Hz用//=====================================================#define SAMPLE_CNT (67) //サンプリング回数(1周期)#define WAIT_CNT (866) //1ch分の電圧電流をサンプリングした後の待ちカウント//ステート制御用変数int state_smp;int state_calc;//サンプリング回数(秒)int sec_cnt;//サンプリングテーブルint myArrayA[SAMPLE_CNT]; //電流サンプリング値int myArrayV[SAMPLE_CNT]; //電圧サンプリング値int arrCnt; //サンプリング位置//電力量保持用変数(1秒間だけ保持する)float watt1;float watt2;float watt3;float watt4;//タイマー割り込みのステート制御用定数#define STATE_START 0 #define STATE_ANP1 1#define STATE_VLT1 2#define STATE_WAIT1 3#define STATE_ANP2 4#define STATE_VLT2 5#define STATE_WAIT2 6#define STATE_ANP3 7#define STATE_VLT3 8#define STATE_WAIT3 9#define STATE_ANP4 10#define STATE_VLT4 11#define STATE_WAIT4 12//メインループのステート制御用定数#define STATE_CALC1 1#define STATE_CALC2 2#define STATE_CALC3 3#define STATE_CALC4 4//####################################################//タイマー割り込み関数//4ch分のサンプリングを定期的に行う//####################################################void callback(){ switch(state_smp){ case STATE_START: arrCnt = 0; state_smp = STATE_ANP1; break; //---------------------------------------------- // CH1 //---------------------------------------------- case STATE_ANP1: //電流のサンプリング myArrayA[arrCnt] = analogRead(1); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_VLT1; } break; case STATE_VLT1: //電圧のサンプリング myArrayV[arrCnt] = analogRead(0); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_WAIT1; state_calc = STATE_CALC1; //メインステートをCH1計算をするように変更 } break; case STATE_WAIT1: //しばらく待ち arrCnt++; if(arrCnt == WAIT_CNT){ arrCnt = 0; state_smp = STATE_ANP2; } break; //---------------------------------------------- // CH2 //---------------------------------------------- case STATE_ANP2: //電流のサンプリング myArrayA[arrCnt] = analogRead(2); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_VLT2; } break; case STATE_VLT2: //電圧のサンプリング myArrayV[arrCnt] = analogRead(0); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_WAIT2; state_calc = STATE_CALC2; //メインステートをCH2計算をするように変更 } break; case STATE_WAIT2: //しばらく待ち arrCnt++; if(arrCnt == WAIT_CNT){ arrCnt = 0; state_smp = STATE_ANP3; } break; //---------------------------------------------- // CH3 //---------------------------------------------- case STATE_ANP3: //電流のサンプリング myArrayA[arrCnt] = analogRead(3); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_VLT3; } break; case STATE_VLT3: //電圧のサンプリング myArrayV[arrCnt] = analogRead(0); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_WAIT3; state_calc = STATE_CALC3; //メインステートをCH3計算をするように変更 } break; case STATE_WAIT3: //しばらく待ち arrCnt++; if(arrCnt == WAIT_CNT){ arrCnt = 0; state_smp = STATE_ANP4; } break; //---------------------------------------------- // CH4 //---------------------------------------------- case STATE_ANP4: //電流のサンプリング myArrayA[arrCnt] = analogRead(4); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_VLT4; } break; case STATE_VLT4: //電圧のサンプリング myArrayV[arrCnt] = analogRead(0); arrCnt++; if(arrCnt == SAMPLE_CNT){ arrCnt = 0; state_smp = STATE_WAIT4; state_calc = STATE_CALC4; //メインステートをCH4計算をするように変更 } break; case STATE_WAIT4: //しばらく待ち arrCnt++; if(arrCnt == WAIT_CNT){ arrCnt = 0; state_smp = STATE_ANP1; } }}//####################################################//起動時の処理//####################################################void setup() { Serial.begin(9600) ; // パソコンとシリアル通信の準備を行う //---------------------------------------------- //変数の初期化 //---------------------------------------------- state_smp = STATE_START; arrCnt = 0; state_calc = STATE_START; sec_cnt = 0; //---------------------------------------------- //タイマー割り込みの設定(250us周期) //250usで67回サンプリングすると、ちょうど1周波(60Hz) //---------------------------------------------- Timer1.initialize(250); Timer1.pwm(9, 512); Timer1.attachInterrupt(callback);}//####################################################//空き時間処理//####################################################void loop() { //---------------------------------------------- //割り込みでサンプリングした結果を集計するメインステート制御 //---------------------------------------------- switch(state_calc){ case STATE_START: //何もしないときは1ms待つ delay(1); break; case STATE_CALC1: //======= CH1 ========= Serial.println("--1--"); watt1 = calc_watt(myArrayA, myArrayV); state_calc = STATE_START; Serial.println(watt1); break; case STATE_CALC2: //======= CH2 ========= Serial.println("--2--"); watt2 = calc_watt(myArrayA, myArrayV); state_calc = STATE_START; Serial.println(watt2); break; case STATE_CALC3: //======= CH3 ========= Serial.println("--3--"); watt3 = calc_watt(myArrayA, myArrayV); state_calc = STATE_START; Serial.println(watt3); break; case STATE_CALC4: //======= CH4 ========= Serial.println("--4--"); watt4 = calc_watt(myArrayA, myArrayV); state_calc = STATE_START; Serial.println(watt4); break; } } //#################################################### //電圧と電流のサンプリング値から消費電力(W)を計算 //#################################################### float calc_watt(int *a, int *v){ float tmp; float sum; //---------------------------------------------- //サンプリングした電圧波形の「-」側を「+」波形で補完 //---------------------------------------------- for(int c = 0; c < SAMPLE_CNT; c++){ if(v[c] == 0){ if(c < (SAMPLE_CNT/2)){ v[c] = -v[c + (SAMPLE_CNT/2)]; } else { v[c] = -v[c - (SAMPLE_CNT/2)]; } } //Serial.println(v[c]); } //---------------------------------------------- //サンプリングした電流波形の「-」側を「+」波形で補完 //---------------------------------------------- //Serial.println("--"); for(int c = 0; c < SAMPLE_CNT; c++){ if(a[c] == 0){ if(c < (SAMPLE_CNT/2)){ a[c] = -a[c + (SAMPLE_CNT/2)]; } else { a[c] = -a[c - (SAMPLE_CNT/2)]; } } //電流計測値が小さいため、A/D値補正のために+-1オフセットさせる //(OPアンプ搭載するまでは暫定的にやる) if(a[c] < 0) a[c]--; if(a[c] > 0) a[c]++; //Serial.println(a[c]); } //---------------------------------------------- //1周期のワット数の合計を計算 //---------------------------------------------- tmp = 0; sum = 0; for(int c = 0; c < SAMPLE_CNT; c++){ //電圧:V = (v[c] * 141.4 / 950) //電流:A = (a[c] * 0.977) 50mv = 1A tmp = ((a[c]) * 0.977) * (v[c] * 141.4 / 950); //合計ワット数 sum+=tmp; } //---------------------------------------------- //電流と電圧の波形が反転しているとマイナスWになるため、+に反転する //---------------------------------------------- if(sum < 0){ sum = sum * (-1); } //---------------------------------------------- //サンプル数で割って平均ワット数を返す //---------------------------------------------- return((sum)/SAMPLE_CNT); }ソースコード内にある以下の赤い部分の計算式は、今回作成した電圧計(ACアダプタ改)と、CTセンサに合わせているので、各センサー類に合わせた計算式に変更する必要があります。
//---------------------------------------------- //1周期のワット数の合計を計算 //---------------------------------------------- tmp = 0; sum = 0; for(int c = 0; c < SAMPLE_CNT; c++){ //電圧:V = (v[c] * 141.4 / 950) //電流:A = (a[c] * 0.977) 5mv = 1A tmp = ((a[c]) * 0.977) * (v[c] * 141.4 / 950); //合計ワット数 sum+=tmp; }割り込みと、メインループのステートの関係は、以下の通りとなります。
電圧と電流のサンプリングが完了したとき、メインステートを計算開始用のステートに変更し、メインループ「loop()」で電力の計算が実行されるようにしています。
メインループ側で、電力の計算が終わると再び「STATE_MAIN」に戻り、待ち状態となります。
センサーをつなげて実行すると、シリアルモニタに各チャンネルの電力が出力されます。
シリアルモニタは、以下のメニューを選択すれば表示されます。