【第09回】 電力計算部を実装する

前回、電圧と電流の計測(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」に戻り、待ち状態となります。

センサーをつなげて実行すると、シリアルモニタに各チャンネルの電力が出力されます。

シリアルモニタは、以下のメニューを選択すれば表示されます。