Spresenseソースコードその3(水温測定、気温測定/集計)

水温と周辺気温の測定プログラムです。温度センサーと測定方法はその2と同じですが、一週間分の最高気温と最低気温を保存してそれらの平均値からヒーターとファンの設定温度を作り出しています。

以下ソースコード。今回は集計もするので長いです。動作は、

  1. 指定した間隔で気温と水温を測定
  2. 一分間分の気温データを加算して平均を取得
  3. 一分間平均を一時間分ためて平均を取得
  4. 一時間平均を24時間分ためて、24時間おきに最大値と最小値を算出して保存
  5. 最大値、最小値を一週間分ためて平均を取得
  6. 最大値、最小値それぞれの平均値から動作点を決定

という手順で行っています。それにより水温が指定した範囲内に収まるように制御し、気温が急変した場合の水温の急変の抑制を目指しています。ソースコードは全部一遍に書くと混乱するので各部分で分けています。

#include <sdk/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <arch/board/board.h>
#include <arch/chip/pin.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#include "watemp.h"

int d_mon = 0; //時刻情報(月)
float temp_a = 0; //気温生データ
float temp_a0[60] = {}; //1時間まで1分ごとの気温
int pls_wtadd0 = 0; //集計用
int pls_wtadd1 = 0; //集計用
int air_err = 1; //気温測定エラーフラグ1
int air_err1 = 0; //気温センサーエラーフラグ2
int air_crnterr = 1; //気温センサー動作不安定フラグ
int air_err0[60] = {}; //気温センサーエラー履歴

float temp_w = 0; //水温
int wtr_err = 1,wtr_avgerr = 1; //水温測定エラーフラグ

float temp_hvt = 0,temp_fvt = 0; //直近一週間の気温から算出した動作点

//その他集計用変数//
int mflg = 0, wflg = 0;
int i0 = 0,i1 = 0,i2 = 0,i3 = 0,j1 = 0,j2 = 0,m0 = 0,m1 = 0;
int smin = 60, intv = 2;
float j = 0;
float temp_a0sum = 0,temp_a0avg = 0;
float temp_a1sum = 0,temp_a1avg = 0;
float temp_a2[48] = {};
float temp_a2amax = 0,temp_a2amin = 0;
float temp_a3smax[7] = {},temp_a3smin[7] = {};
float temp_a3maxadd = 0,temp_a3minadd = 0,temp_a3maxavg = 0,temp_a3minavg = 0;

//パルス計数端子初期化
pthread_mutex_t mutex_wt = PTHREAD_MUTEX_INITIALIZER;

static int pls_temp2 = 0; //温度センサーパルス計測用2
void plscount2(void) //割り込み有効時にパルス数を計数
{
pls_temp2++;
}


void mes_temp(void) //計測部。指定された時間間隔で水温気温の順に測定//
{
//水温測定//
board_gpio_write(PIN_PWM3, 1); //水温温度センサー有効
usleep(40*1000); //センサー電源ONまで待つ(40ms)
board_gpio_int(PIN_HIF_IRQ_OUT, true); //センサー計数割り込み有効
usleep(260*1000); //計測時間指定(3回分)
board_gpio_int(PIN_HIF_IRQ_OUT, false); //センサー計数割り込み無効
board_gpio_write(PIN_PWM3, 0); //水温温度センサー無効

pls_wtadd0 += pls_temp2; //パルス数を水温用の変数にコピー

#ifndef NOLOCK //メモリロック(タスク間で干渉しないようメモリをロックする)
pthread_mutex_lock(&mutex_wt);
#endif
temp_w = pls_temp2 / 48.48 - 50; //水温計算

if(pls_temp2 < 100) //センサー有無判断(パルス数100以下はセンサー未接続と判断)
{
wtr_err = 1;
}else
{
wtr_err = 0;
}

//センサー不安定判断(直近1分間の平均からパルス数が7000以上異なる場合はセンサー不安定と判断)
if(abs(pls_wtadd1 - pls_temp2 * (smin/intv)) > 7000)
{
wtr_avgerr = 1;
}else
{
wtr_avgerr = 0;
}

#ifndef NOLOCK //変数ロック解除
pthread_mutex_unlock(&mutex_wt);
#endif

usleep(100*1000);
pls_temp2 = 0; //パルス数クリア


//気温測定//
board_gpio_write(PIN_SPI2_MOSI, 1); //気温温度センサー有効
usleep(40*1000); //センサー電源ONまで待つ(40ms)
board_gpio_int(PIN_HIF_IRQ_OUT, true); //センサー計数割り込み有効
usleep(260*1000); //計測時間指定(3回分)
board_gpio_int(PIN_HIF_IRQ_OUT, false); //センサー計数割り込み無効
board_gpio_write(PIN_SPI2_MOSI, 0); //気温温度センサー無効

#ifndef NOLOCK //メモリロック
pthread_mutex_lock(&mutex_wt);
#endif

if(pls_temp2 < 100) //センサー有無判断
{
air_crnterr = 1;
air_err0[i0] = 1;
}
else
{
air_crnterr = 0;
air_err0[i0] = 0;
}

#ifndef NOLOCK //変数ロック解除
pthread_mutex_unlock(&mutex_wt);
#endif

temp_a = pls_temp2 / 48.48 - 50; //温度計算

usleep(100*1000);
pls_temp2 = 0; //パルス数クリア
} //測定終了//




//気温集計//
//一分データ生成部//
void calmint(void)
{
//測定部//
mes_temp();

if(mflg == 0) //起動一分以内は生データをそのまま使用
{
temp_a0avg = temp_a;
}

temp_a0sum += temp_a; //一分間分の気温合算

//一分データ生成//
if(i0 == ((smin / intv) - 1) )
{
temp_a0avg = temp_a0sum / (smin/intv); //一分平均気温算出
temp_a0sum = 0;

//一分平均値の履歴作成//
#ifndef NOLOCK //メモリロック
pthread_mutex_lock(&mutex_wt);
#endif

for(j1 = (shour - 2); j1 >= 0 ; j1--) //気温データを格納する配列を一個動かす
{
j2 = j1 + 1;
j = temp_a0[j1];
temp_a0[j2] = j;
}

//一時間気温データ格納
if(air_err < 1)
{
temp_a0[0] = temp_a0avg; //計測エラーがなければ一分平均を格納
}else
{
temp_a0[0] = temp_a0[1]; //計測エラーがある場合は前回のデータを格納
}

#ifndef NOLOCK //変数ロック解除
pthread_mutex_unlock(&mutex_wt);
#endif

if(mflg != 1) //起動一分フラグ立て(生データそのまま使用を終了)
{
mflg = 1;
}

pls_wtadd1 = pls_wtadd0; //水温一分履歴エラー検出用
pls_wtadd0 = 0;

for(int k = 0; k < smin ; k++) //気温計測エラーフラグ履歴蓄積
{
air_err1 += air_err0[k];
}
if(air_err1 > 2) //気温測定一分間エラーフラグ(エラー2回以上で測定失敗処理)
{
air_err = 1;
}else
{
air_err = 0;
}
air_err1 = 0; //測定区間内気温測定エラーフラグリセット
}
}


//一時間データ算出部//
void calhour(void)
{

temp_a1sum += temp_a0avg; //一時間平均算出

if(i1 == (shour - 1)) //一時間おきに過去60分間の平均気温を計算
{
temp_a1avg = temp_a1sum / shour;
temp_a1sum = 0;
}

temp_a2[i2] = temp_a1avg; //履歴を格納(24時間分)

}

//一時間平均履歴より一日の最高最低気温算出//
void calday(void)
{
temp_a2amax = temp_a2[0];
temp_a2amin = temp_a2[0];

for (int i = 1; i < sday; ++i) //格納された気温データを比較し最大、最小を探す
{
if (temp_a2amax < temp_a2[i])
{
temp_a2amax = temp_a2[i];
}

if (temp_a2amin > temp_a2[i])
{
temp_a2amin = temp_a2[i];
}
}
}


//最高気温、最低気温の一週間平均算出
void calweek(void)
{
temp_a3smax[i3] = temp_a2amax; //最高気温、最低気温の一週間履歴
temp_a3smin[i3] = temp_a2amin;

if(i3 == (sweek - 1))
{
if(wflg != 1) //起動一週間フラグ立て(履歴保存が一週間分のため)
{
wflg = 1;
}
}

for(int l = 0 ; l < sweek ; l ++) //過去の最高気温と最低気温の履歴をそれぞれ合算
{
temp_a3maxadd += temp_a3smax[l];
temp_a3minadd += temp_a3smin[l];
}

if(wflg == 1) //起動後一週間経っている場合は一週間分を平均、そうでない場合は稼働日数で平均
{
temp_a3maxavg = temp_a3maxadd / sweek;
temp_a3minavg = temp_a3minadd / sweek;
}
else{
temp_a3maxavg = temp_a3maxadd / (i3 + 1);
temp_a3minavg = temp_a3minadd / (i3 + 1);
}

temp_a3maxadd = 0; //変数クリア
temp_a3minadd = 0;

#ifndef NOLOCK //メモリロック
pthread_mutex_lock(&mutex_wt);
#endif

if(gnss_flg < 1){
d_mon = 0;
}
else{
d_mon = gn_month;
}


//動作点算出。一週間分の平均最高気温と平均最低気温の間で設定することで水温の急変を抑制を目指す//
temp_hvt = temp_a3minavg + (temp_a3maxavg - temp_a3minavg) * ntemp_heat[d_mon] / 10; //ヒーター動作点情報
temp_fvt = temp_a3minavg + (temp_a3maxavg - temp_a3minavg) * ntemp_cool[d_mon] / 10; //ファン動作点情報

#ifndef NOLOCK //変数ロック解除
pthread_mutex_unlock(&mutex_wt);
#endif

}

void calwatemp(void) //集計プログラム本体(各部分は上にあるとおり)//
{
for(i3 = 0; i3 < sweek; ++i3) //一日ごとにカウント
{
if(emstp == 1) //非常停止処理(非常停止フラグ発生時の動作)
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}

for(i2 = 0; i2 < sday; i2++) //一時間ごとにカウント
{
if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}

for(i1 = 0; i1 < shour; i1++) //一分ごとにカウント
{
if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}

for(i0 = 0; i0 < (smin/intv); i0++) //3秒ごとにカウント
{
if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}

if(gn_sec % sintv != 1)
{
while(gn_sec % sintv != 1)
{
if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}
usleep(300*1000);
}
}
calmint();

}
calhour();

}
calday();

}
calweek();

}

}

//集計プログラム本体(全部ここに書くと分けわからなくなるので各モジュールに分けた)
void* watemp(void *arg) //気温、水温測定(2秒間隔)
{
board_gpio_config(PIN_HIF_IRQ_OUT, 0, 1, 0, 0); //D02 温度センサー2:水温 、気温
board_gpio_config(PIN_PWM3, 0, 0, 1, 0); //D03 水温センサー動作用
board_gpio_config(PIN_SPI2_MOSI, 0, 0, 1, 0); //D04 気温センサー動作用
board_gpio_intconfig(PIN_HIF_IRQ_OUT, INT_FALLING_EDGE, false, plscount2); //D02 パルス計測割り込み設定

if (sintv >= 2)
{
intv = sintv;
}
else
{
intv = 2;
}
usleep(500*1000); //メインの初期設定待ち

//計測間隔設定(途中に測定部)//

while(1)
{
usleep(1000);
if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_HIF_IRQ_OUT, false);
board_gpio_write(PIN_PWM3, 0);
board_gpio_write(PIN_SPI2_MOSI, 0);
break;
}

calwatemp(); //測定、集計部呼び出し

}
return NULL;
}

テキストエディタで整形して貼り付けたけど上手く反映できないorz。次はヘッダファイル

#ifndef _INCLUDE_WATEMP_
#define _INCLUDE_WATEMP_
void* watemp(void *arg);
#endif

//時刻情報//
int gn_year;
int gn_month;
int gn_day;
int gn_hour;
int gn_minute;
int gn_sec;
int gnss_flg;

int sintv; //水温、気温測定間隔(秒単位、2秒以上)
int shour, sday, sweek; //各時間間隔ごとの気温計測数

float temp_a; //確認用気温生データ
int wtr_err; //水温測定エラー
int wtr_avgerr; //水温1分値エラー
int air_err; //気温測定エラー
int air_crnterr; //気温センサー状態
float temp_w; //水温
float temp_a0[60]; //1時間まで1分ごとの気温
float ntemp_heat[13]; //ヒーター動作点算出用係数
float ntemp_cool[13]; //クーラー動作点算出用係数
float temp_hvt;
float temp_fvt; //直近一週間から算出した動作点
int emstp; //非常停止フラグ
int hold_flg; //一時停止フラグ
This entry was posted in プログラミング, ベランダビオトープ, 睡蓮水槽管理装置, 電子工作 and tagged , , . Bookmark the permalink.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA