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; //一時停止フラグ
Posted in プログラミング, ベランダビオトープ, 睡蓮水槽管理装置, 電子工作 | Tagged , , | Leave a comment

気候が落ち着いてきたので

寒くなってヒーター類が稼働する季節になる前に思いついた改良を行います。今回の変更点は

  • 設定温度計算式を通年一律から月ごとに係数を変更できるようにした
  • 動作温度範囲の見直し

ついでに水槽内のレイアウトも見直して、色々入れすぎて混み合ってきたので整理しました。もったいないですがサギソウをなくしてオモダカとミツガシワを背景に移動してヒツジソウとガガブタのための面積を確保しました。手前のホテイアオイは別途用意していたメダカの稚魚用のバケツを片付けた時に出たものです。もったいないので冬までの隠れ家用。

この後の変更案は、以前に使っていた和種ハッカを左側の縁の上(写真のサボテンがあるところ)にプランターに植えて配置、水タンクから汲み上げている水をプランターに通して水耕栽培のようにすることで肥料分を消費させようかと考えています。

Posted in プログラミング, ベランダビオトープ, 睡蓮水槽管理装置, 電子工作 | Tagged , , | Leave a comment

LED照明追加

ここ何日かカエルさんが窓に来る虫を狙って来てるので、水槽に誘導すべく防水ソーラーライトを購入の上で取り付け。庭に突き刺して使うタイプだと日差し的に不利なのと、そのうちマイコンの制御下に置いて夜中になったら消灯できるように改造するつもりでソーラーパネルが別れているやつということで下記を購入。

用途が用途なのでリモコンもいらない。できたらランプ部分にタイラップを引っ掛けることができたらありがたいかな?

とりあえず、手持ちのイボ竹にタイラップで縛り付けてみたのがこれ。LED電球の出力2Wってかなり明るいです。

色味が電球色なので虫の集まりは微妙ですが雰囲気はかなり出ます。

Posted in その他, ベランダビオトープ, 園芸, 電子工作 | Tagged , | Leave a comment

Spresenseソースコードその2(鉢底面ヒーター温度測定部)

今回、温度センサーにテキサスインスツルメンツのLMT01LPG(秋月電気で一個150円。秋月サイトだと生産中止サインがでてるけどTiのサイトだと普通に生産中の扱いなんだよなぁ)を使用しています。このセンサーはTO-92パッケージにリード線2本が出てるタイプで出力は温度に比例したパルス数で出力されます。電源を接続すれば動作しますが、繰り返しで延々と温度信号パルスが出るだけなのでパルス数の検出が面倒っぽい。同じセンサーを使った前例を探したところいろんな方がいろんな工夫でひと塊のパルスが出終わったところを検出していました。今回の用途的に水温や気温は数秒おき、底面ヒーターはもっと短い周期が欲しいもののそれでも1秒周期もあれば良いので割り切ってこうなりました。

#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 "heattemp.h"

float temp_h = 0; //ヒーター温度
int heat_err = 1 ,heat_avgerr = 1; //ヒーター測定エラー
int hold_tmp = 0; //停止ボタン押下信号
pthread_mutex_t mutex_ht = PTHREAD_MUTEX_INITIALIZER;

static int pls_temp1 = 0; //温度センサーパルス計測用1


//PIN_UART2_RXDの割り込みが有効な間、パルスが入力されると
//入力されたパルス数を数えます
void plscount1(void)
{
pls_temp1++;
}

void stpbtn(void)
{
if(hold_flg == 0)
{
hold_tmp = 1;
}else
{
hold_tmp = 0;
}
board_gpio_int(PIN_SPI2_MISO, true); //停止ボタン割り込み無効
}

void* heattemp(void *arg) //ヒーター温度測定、動作
{
board_gpio_config(PIN_UART2_RXD, 0, 1, 0, 0); //D00 センサー入力
board_gpio_config(PIN_UART2_TXD, 0, 0, 1, 0); //D01 センサー制御
board_gpio_config(PIN_SPI2_MISO, 0, 1, 0, 1); //D08 非常停止端子
board_gpio_intconfig(PIN_UART2_RXD, INT_FALLING_EDGE, false, plscount1); //D00 パルス計測割り込み設定
board_gpio_intconfig(PIN_SPI2_MISO, INT_FALLING_EDGE, true, stpbtn); //D08 停止ボタン割り込み設定
board_gpio_write(PIN_UART2_TXD, 0); //ヒーター温度センサー停止

int pls_htadd0 = 0, pls_htadd1 = 0; //ヒーター温度測定異常検知用
int pls_avgerr = 0;

usleep(500*1000); //メインの初期設定待ち

while(1){

_strt:

for(int k0 = 0; k0 < 30; k0++)
{

//計測開始//
board_gpio_write(PIN_UART2_TXD, 1); //ヒーター温度センサー有効
usleep(40*000); //センサー電源ONまで待つ(20ms)
board_gpio_int(PIN_UART2_RXD, true); //センサー計数割り込み有効
board_gpio_int(PIN_SPI2_MISO, true); //停止ボタン割り込み有効

usleep(260*1000); //計測時間指定(3回分)

board_gpio_int(PIN_UART2_RXD, false); //センサー計数割り込み無効
board_gpio_write(PIN_UART2_TXD, 0); //ヒーター温度センサー無効

pls_htadd0 += pls_temp1;

#ifndef NOLOCK //変数ロック
pthread_mutex_lock(&mutex_ht);
#endif
if(pls_temp1 < 100) //測定結果フラグ
{
heat_err = 1;
}
else{
heat_err = 0;
}

if(abs(pls_htadd1 - pls_temp1 * 30) > 7000)
{
heat_avgerr = 1;
}else
{
heat_avgerr = 0;
}

temp_h = pls_temp1 / 48.48 - 50.0; //温度計算

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

pls_avgerr = 0;
pls_temp1 = 0; //パルス数クリア
usleep(100*1000); //計測間隔を0.4秒に調整

if(hold_tmp == 1) //停止ボタン対応
{
#ifndef NOLOCK //変数ロック
pthread_mutex_lock(&mutex_ht);
#endif

hold_flg = 1;

#ifndef NOLOCK //変数ロック解除
pthread_mutex_unlock(&mutex_ht);
#endif
usleep(400*1000);

if(board_gpio_read(PIN_SPI2_MISO) == 1)
{
goto _strt;
}

usleep(1100*1000);

if(board_gpio_read(PIN_SPI2_MISO) == 0) //ボタン長押しで非常停止処理
{
board_gpio_int(PIN_SPI2_MISO, false);
usleep(1000);

#ifndef NOLOCK //変数ロック
pthread_mutex_lock(&mutex_ht);
#endif

emstp = 1;

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

} else{
#ifndef NOLOCK //変数ロック
pthread_mutex_lock(&mutex_ht);
#endif

hold_flg = 0;

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

if(emstp == 1) //非常停止処理
{
board_gpio_int(PIN_UART2_RXD, false);
board_gpio_write(PIN_UART2_TXD, 0);
break;
}
}
pls_htadd1 = pls_htadd0;
pls_htadd0 = 0;
}
return NULL;
}

ヘッダファイルの中身

#ifndef _INCLUDE_HEATTEMP_
#define _INCLUDE_HEATTEMP_
void* heattemp(void *arg);
#endif

float temp_h; //ヒーター温度
int heat_err; //ヒーター測定エラー
int heat_avgerr;
int emstp; //非常停止フラグ
int hold_flg; //一時停止フラグ

このコードでは3回分のパルス数をカウントし、その平均で温度を算出しています。計測間隔は0.5秒です。これ以外に

  1. 測定エラー検出(パルス数が100未満の時にフラグ立て)
  2. 平均エラー検出(前30回分の平均に対して約15℃分ズレている時にフラグ立て)
  3. 一時停止機能(停止ボタンを短く押すと計測を一時中断。もう一回押すと再開)
  4. 非常停止機能(停止ボタン長押しで計測を停止し数値等全部クリア)

の機能があります。鉢底ヒーターの温度を管理するためのものなので、計測値自体は特に加工せず0.4秒おきに淡々と数値を更新します。

実際の鉢底ヒーターはこんなふうに作っています。

①ヒーターそのものはシートヒーターをA4サイズのアルミ板(1.5mm厚)で挟んでいます
②LMT01LPGを熱伝導性両面テープで貼り付けた上でエポキシでポッティング
③シールド線を接続し熱収縮チューブで保護しました
このヒーターをトロ箱の底に配置、断熱材で覆いました。

Posted in プログラミング, ベランダビオトープ, 園芸, 睡蓮水槽管理装置, 電子工作 | Tagged , , | Leave a comment

Spresense ソースコードその1(メイン(設定部分))

※2020年10月13日修正(設定温度改良に伴う)

某ブラウザゲームのイベントとかやっていて間が空いてしまいましたが、ソースコードです。開発環境は主流ではないですがSpresense SDKを使用しています。理由は拡張性が高そうに見えたことと、Arduino IDE環境だとチャタリング防止機構を無効にできず使用している温度センサーが上手く使えなかったためです。ただ、主流ではないだけに資料があまりにも少なくLチカまではそこそこできたものの、その先はほぼ完全に手探り。結局モニター用の液晶は最後まで表示できませんでした。一方で、SDK環境で用いるNuttx OSはマルチスレッドに対応しておりSpresenseのマルチコアCPUと合わせて拡張性の高さの片鱗を見ることもできました。

能書きは終わりにして、まずはメインの部分から。ここに肝心な部分はあまりありませんが、マルチスレッドの設定や各プログラムの呼び出しなんかをやっています。

#include <sdk/config.h>
#include <stdio.h>
#include <arch/board/board.h>
#include <arch/chip/pin.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <arch/chip/gnss.h>

//各プログラム呼び出し用ヘッダ//
#include "heattemp.h"
#include "watemp.h"
#include "gnssclk.h"
#include "cntunit.h"
#include "ledout.h"

//測定、更新間隔設定//
int intvlh = 1; //ヒーター測定、判定間隔(秒単位)
int intvla = 3; //水温、気温判定間隔(秒単位)
int sintv = 2; //水温、気温測定間隔(秒単位、2秒以上)
int shour = 60, sday = 24, sweek = 7; //各時間間隔ごとの気温計測数

//温度範囲設定(12ヶ月分+1番目は時刻未取得時の値)//
//月別ヒーター温度上限(ここを越えたら絶対に止める)//
float vtemp_hmax[13] = { 7, 10, 10, 12, 20, 25, 25, 25, 25, 25, 20, 15, 12}; 

//月別ヒーター温度下限(ここを下回ったら絶対に動かす)//
float vtemp_hmin[13] = { 3, 5, 5, 8, 15, 20, 20, 20, 20, 20, 15, 10, 6};
 
//月別ファン制御温度上限(ここを越えたら絶対に動かす)//
float vtemp_wmax[13] = { 25, 15, 15, 20, 25, 26, 26, 26, 26, 26, 24, 20, 18}; 

//月別ファン制御温度下限(ここを下回ったら絶対に止める)//
float vtemp_wmin[13] = { 18, 10, 10, 15, 18, 20, 20, 20, 20, 20, 18, 15, 12}; 

//月別水凍結防止ヒーター温度上限(ここを越えたら絶対に止める)//
float vtemp_h2max[13] = { 5, 7, 7, 10, 15, 15, 18, 18, 18, 15, 15, 10, 7}; 

//月別水凍結防止ヒーター温度上限(ここを下回ったら絶対に動かす)//
float vtemp_h2min[13] = { 2, 2, 2, 5, 10, 15, 15, 15, 15, 15, 10, 3, 2}; 


//温度設定用係数(0〜10で設定。数字が大きいほど温度が高めになる。
//ヒーターとクーラーの設定次第で両方動作する場合がある)

//ヒーターの月別係数設定。
//冬季は冬眠の邪魔をしないように低め、夏季は高めに設定
float ntemp_heat[13] = { 3, 2, 2, 3, 4, 4, 5, 5, 5, 4, 3, 3, 2};

//クーラーの月別係数設定。
//梅雨寒等を想定して6月は高め、猛暑な7〜9月は曝気目的もあって低め
float ntemp_cool[13] = { 7, 5, 5, 6, 7, 7, 7, 6, 6, 6, 6, 5, 5};
//フラグ、変数類// int emstp = 0; //非常停止フラグ int hold_flg = 0; //一時停止フラグ int heat_flg = 0; //ヒーター動作 int cool_flg = 0; //ファン動作 int wthr_flg = 0; //追加ヒーター動作 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // プログラム本体 // #ifdef CONFIG_BUILD_KERNEL int main(int argc, FAR char *argv[]) #else int wtcont_main(int argc, char *argv[]) #endif { //とりあえずLEDの端子属性を指定して全部消灯// board_gpio_config(PIN_I2S1_BCK, 0, false, true, 0); //LED0 board_gpio_config(PIN_I2S1_LRCK, 0, false, true, 0); //LED1 board_gpio_config(PIN_I2S1_DATA_IN, 0, false, true, 0); //LED2 board_gpio_config(PIN_I2S1_DATA_OUT, 0, false, true, 0); //LED3 usleep(200*1000); //LED点滅して起動表示// board_gpio_write(PIN_I2S1_BCK, 1); board_gpio_write(PIN_I2S1_LRCK, 1); board_gpio_write(PIN_I2S1_DATA_IN, 1); board_gpio_write(PIN_I2S1_DATA_OUT, 1); usleep(500*1000); board_gpio_write(PIN_I2S1_BCK, 0); board_gpio_write(PIN_I2S1_LRCK, 0); board_gpio_write(PIN_I2S1_DATA_IN, 0); board_gpio_write(PIN_I2S1_DATA_OUT, 0); usleep(10*1000); //マルチスレッド設定+各プログラム呼び出し//
//スレッド数はコア数(6個)以内にするのが吉っぽい// pthread_t thread_A, thread_B, thread_C, thread_D, thread_E; pthread_create(&thread_A, NULL, heattemp, NULL); pthread_create(&thread_B, NULL, watemp, NULL); pthread_create(&thread_C, NULL, cntunit, NULL); pthread_create(&thread_D, NULL, gnssclk, NULL); pthread_create(&thread_E, NULL, ledout, NULL); pthread_join(thread_A, NULL); pthread_join(thread_B, NULL); pthread_join(thread_C, NULL); pthread_join(thread_D, NULL); pthread_join(thread_E, NULL); usleep(200*1000); //異常終了時LED全部点灯// board_gpio_write(PIN_I2S1_BCK, 1); board_gpio_write(PIN_I2S1_LRCK, 1); board_gpio_write(PIN_I2S1_DATA_IN, 1); board_gpio_write(PIN_I2S1_DATA_OUT, 1); return 0; }

使った資料はSpresense公式ページとどこかは失念しましたがブログサイト様、Nuttx OSを扱ったページいくつかです。マイコンを扱うというのが頭に強くこびりついていて最初苦労しましたが、OSを介して動かすことからPCの扱いに近い部分があることに気づくと幾らか楽になりました。接続するデバイスはドライバが見つかればかなり簡単に扱えるようです。

マルチスレッド対応ですが、スレッド数はコア数以内で抑えないとリアルタイム制御が上手くいかなくなるようです。当初、停止ボタンの動作管理も別スレッドで記述していましたが、センサーの動作時間制御が不安定になり温度をまともに検出できなくなったので各プログラムに潜り込ませて対応しました。停止ボタン押下検出の間隔を変化させても問題が解決せず、スレッド数をコア数以内で収めることで動作が安定しました。

Posted in プログラミング, ベランダビオトープ, 園芸, 睡蓮水槽管理装置, 電子工作 | Tagged , | Leave a comment