ホタルの明滅をデータロギング

f:id:manpukukoji:20160814093208j:plain蛍の光をデータに落とすデータロガーを作ってみた。
データは、ホタルの明滅パターンを取るための、光の強度をサンプリングする光センサと、ホタルの光の色をRGBでサンプリングする、カラーセンサの2つのセンサからなっています。
これら2つのセンサからのデータをArduinoでA/D変換、サンプリングしてSDカードに記録するというもの。
回路図は、こんな感じ。
f:id:manpukukoji:20160809205608j:plain

光センサは、S9648にLM358で増幅してArduinoでA/D変換、 カラーセンサは、S9706を使ってRGBのデータを取り込み、ロジックレベル変換モジュールを通して、SDカードに記録している。
センサは、ピンポン玉を半分に切って、底部にセンサをセットして、この中にホタルを入れて、少しの間ピンポン玉の中で光ってもらう。 一応、積分球のようにホタルが中で移動しても平均的な反射光をサンプリングするような効果を期待したけど、センサがむき出しなので、あまり意味はないかもしれない。まぁ、これでいいのだ!

これまでも同様の光センサで、明滅パターンを取っていたのだけど、これまでは、カラーセンサがなく、シリアル通信でPCと直結していたので、機動性に問題があった。また、ホタルの飛ぶところは、暗いので、PCのバックライトの明かりが明るすぎて、何かと問題が多いので、今回のものに改良してみた。

使い方は、電源をONにすると、LEDが3回点滅して、光の強度測定モードで始動します。
ロギング開始ボタンを押すと、光センサからのデータがAD変換されてSDカードにdatalog.txtというファイルに記録されます。
電源をONする際に、ロギング停止ボタンを押したまま電源を入れると、カラー測定モードで始動します。
同様にロギング開始ボタンを押すと、カラーセンサからのデータがcolorlog.txt というファイルに記録されます。
入力データが上限値をこえると、LEDが点灯して入力レベルの調整が必要なことを教えてくれます。
まぁ、詳細は、スケッチを追っていただければわかると思います。
カラー測定データをシリアルモニタで見る場合は、シリアルモニタを立ち上げる際にロギング停止ボタンを押しておかないと、強度測定モードになってしまうので注意が必要です。

Arduinoのスケッチは、これまた、あちこちのサイトを参考にさせてもらって、つぎはぎして、以下のようなものになった。
自分でも、どこのスケッチを引っ張ってきたのかわからなくなっちゃっているけど、カラーセンサに関しては、こちらのサイトから、主な部分は、ほぼ、そのまま引用させていただいている。
建築発明工作ゼミ2008: Arduino デジタルカラーセンサ S9706

他にもSDカードのライブラリとかA/D変換とかセンサの扱いとかいろいろとコピペさせていただいています。
まぁ、まさにスパゲッティのようなスケッチできれいに作れていないし、改良すべきところも沢山あるけど、ひとつの参考までに、以下、スケッチです。

#include <SD.h>

#define LED_PIN 4	// 4番ピンをデーターLogging 開始LEDに設定       Light Intensity
#define STARTBUTTON 2	// 2番ピンをデータLogging開始に設定		Light Intensity
#define STOPBUTTON 3	// 3番ピンをデータLogging開始に設定		Light Intensity
#define PEAK_LED 7	// 7番ピンをピークLEDに設定		        Light Intensity

#define RANGE 6		//  8番ピンをRange端子に設定		        Color Sensor
#define GATE  16	//  9番ピンをGate端子に設定			Color Sensor
#define CK   5		// 10番ピンをCK端子に設定			Color Sensor
#define DOUT 15		// 11番ピンをDout端子に設定			Color Sensor

volatile int loggmode=0;		//Logging mode 設定のための変数 (1=Light Intensity mode / 0=Color mode)

int red,green,blue;   //RGB三色の変数を用意 for Color Sensor

// この値は使用しているシールドや基板に合わせて変更すること。たとえば、
// イーサーネットシールドは 4
// Adafruit のSDシールドは 10
// Sparkfun のSDシールドは 8
const int chipSelect = 10;	//自作のSDシールドは10番ピンで作成
unsigned long tm,ptm;		//期間(millis)の計算のための変数
int mm;
volatile int loggsts=0;	  //Logging Startのフラグ
volatile int datapos=0;  //data start endのマーク
File dataFile;

//start buttonを押したときの割り込み処理
void loggstrt()
{
  loggsts=1;
  digitalWrite(LED_PIN,HIGH);
  datapos = 1;
}

//Stop Buttonを押したときの割り込み処理
void loggstp()
{
  loggsts=0;
  digitalWrite(LED_PIN,LOW);
  digitalWrite(PEAK_LED,LOW);
  datapos = 2;  
}

void setup()
{
  int i;
  
  pinMode(LED_PIN,OUTPUT);
  pinMode(PEAK_LED,OUTPUT);

    //Range,Gate,CK端子をデジタル出力に設定
  pinMode(RANGE,OUTPUT);
  pinMode(GATE,OUTPUT);
  pinMode(CK,OUTPUT);
  //Dout端子をデジタル入力に設定
  pinMode(DOUT,INPUT);


  // シリアルポート初期化
  Serial.begin(9600);

  Serial.print(F("Initializing SD card..."));

  // SSピン(Unoは10番、Megaは53番)は使わない場合でも出力にする必要があります。
  // そうしないと、SPIがスレーブモードに移行し、SDライブラリが動作しなくなります。
  pinMode(SS, OUTPUT);

  // SDライブラリを初期化
  if (!SD.begin(chipSelect)) {
    Serial.println(F("Card failed, or not present"));
    // 失敗、何もしない
    while(1);
  }
  Serial.println(F("ok."));

  // 日付と時刻を返す関数を登録
  SdFile::dateTimeCallback( &dateTime );
  
   tm = millis();
   mm=0;
  
    //Pull Up 設定
    pinMode(STARTBUTTON, INPUT_PULLUP);
    pinMode(STOPBUTTON, INPUT_PULLUP);

	//Logg mode の設定(0=Light Intensity / 1=Color sensor)…Logging停止ボタンを押して電源をいれるとカラーモードになる。
	loggmode = digitalRead(STOPBUTTON);

	// カラーモードのときには、LEDを5回点滅
	if (loggmode==0){
		for(i=1;i<6;i++){
			digitalWrite(LED_PIN,HIGH); digitalWrite(PEAK_LED,HIGH); delay(500);
			digitalWrite(LED_PIN,LOW); digitalWrite(PEAK_LED,LOW); delay(500);
		}
        }
 	// 光強度モードのときには、LEDを3回点滅
	if (loggmode==1){
		for(i=1;i<4;i++){
			digitalWrite(LED_PIN,HIGH); digitalWrite(PEAK_LED,HIGH); delay(500);
			digitalWrite(LED_PIN,LOW); digitalWrite(PEAK_LED,LOW); delay(500);
		}
        }
   
    //割り込み設定
    attachInterrupt(0,loggstrt,FALLING);
    attachInterrupt(1,loggstp,FALLING);
    
    loggsts=0;
}


void loop()
{
// ------------ Light Intnsity Logging  ----------------------------------------
	if(loggmode==1)
	{	
	  if(loggsts==1)
	  {
		// ファイルを開く
		   dataFile = SD.open("datalog.txt", FILE_WRITE);

		 // もしファイルが開けたら値を書き込む
		  if (dataFile) {
		    ptm=tm;
		    tm=millis();
		    int value = analogRead(0);
    
                    if(datapos == 1){dataFile.println("S");}
		    dataFile.print(tm-ptm);
		    dataFile.print(",");
		    dataFile.println(value);
                    if(datapos == 2){dataFile.println("E");}
                    datapos = 0;
		    dataFile.close();
		    // シリアルポートにも出力
		    Serial.print(tm-ptm);
		    Serial.print(",");
		    Serial.println(value);
    
			//データが最大値に達したら、LEDを点灯
		    if (value>730){digitalWrite(PEAK_LED,HIGH);}
		    else {digitalWrite(PEAK_LED,LOW);}

    
		  }
		  // ファイルが開けなかったらエラーを出力
		  else {
		    Serial.println(F("error opening datalog.txt"));
		  } 

		  // 一秒待つ
		//  delay(1000);

	  } // ---------- End of loggsts==1 ... Logging Start ------------------------------
	}	// ---------- End of loggmode==1 ... Light Intensity Logging ----------------------------------


// ---------------Color Logging --------------------------------------------
	if(loggmode==0)
	{
		if(loggsts==1)
		{
				// ファイルを開く
		   dataFile = SD.open("colorlog.txt", FILE_WRITE);

		 // もしファイルが開けたら値を書き込む
		  if (dataFile) {
//		    int value = analogRead(0);
			// >>>>>>>>>>>>>>>>>>>>>>>>>>
			  //測光時間用の可変抵抗器の読み込み(アナログ入力:0番ピン)
			int val=1000;

			//Gate,CK端子をLowに設定
			digitalWrite(GATE,LOW);
			digitalWrite(CK,LOW);
			delayMicroseconds(2000);//2000マイクロ秒待機

			//感度設定(HIGH:高感度に設定)  
			digitalWrite(RANGE,HIGH);

			//測光開始(光量の積算を開始) 
			digitalWrite(GATE,HIGH);
			//測光時間(valを代入し可変的に設定)
			delay(val+1);
  
			//測光終了(光量の積算を終了) 
			digitalWrite(GATE,LOW);
			delayMicroseconds(4);//4マイクロ秒待機
  
			red=shiftIn();//赤の処理
			green=shiftIn();//緑の処理
			blue=shiftIn();//青の処理

			//Gate端子をHighに戻す  
			digitalWrite(GATE,HIGH);

		    ptm=tm;
		    tm=millis();

                    if(datapos == 1){dataFile.println("S");}
		    dataFile.print(tm-ptm);
		    dataFile.print(",");
		    dataFile.print(val,DEC);
		    dataFile.print(",");
		    dataFile.print(red,DEC);
			dataFile.print(",");
			dataFile.print(green,DEC);
			dataFile.print(",");
			dataFile.println(blue,DEC);
                    if(datapos == 2){dataFile.println("E");}
                    datapos = 0;
		    dataFile.close();

		    // シリアルポートにも出力
		    Serial.print(tm-ptm);
		    Serial.print(",");
		    Serial.print(val,DEC);
		    Serial.print(",");
			Serial.print(red,DEC);
			Serial.print(",");
			Serial.print(green,DEC);
			Serial.print(",");
			Serial.println(blue,DEC);
 

			// >>>>>>>>>>>>>>>>>>>>>>>>>
    
  
			//データが最大値に達したら、LEDを点灯
		    if ((red>4000)||(green>4000)||(blue>4000)) {digitalWrite(PEAK_LED,HIGH);}
		    else {digitalWrite(PEAK_LED,LOW);}

    
		  }
		  // ファイルが開けなかったらエラーを出力
		  else {
		    Serial.println(F("error opening datalog.txt"));
		  } 

		  // 一秒待つ
		//  delay(1000);

		}	// End of loggsts==1 ... Logging Start ------------------------
	}	// ---------- End of Loggmode==0 ... Color Logging ----------------

}	// End of loop

void dateTime(uint16_t* date, uint16_t* time)
{
  uint16_t year = 2015;
  uint8_t month = 7, day = 4, hour = 12, minute = 0, second = 0;

  // GPSやRTCから日付と時間を取得
  // FAT_DATEマクロでフィールドを埋めて日付を返す
  *date = FAT_DATE(year, month, day);

  // FAT_TIMEマクロでフィールドを埋めて時間を返す
  *time = FAT_TIME(hour, minute, second);
}

//12ビット分のパルス送信と読み込み処理
int shiftIn(){
  int result=0;//検出結果用の変数を用意(0:初期化)
  for(int i=0;i<12;i++){//12ビット分の繰り返し処理
    digitalWrite(CK,HIGH);//1ビット分のクロックパルス出力(HIGH)
    delayMicroseconds(1);//1マイクロ秒待機
    if(digitalRead(DOUT)==HIGH){//Dout端子からの出力がHighの場合
      result+=(1<<i);//12ビットのi桁目に1を代入(i桁分だけ左にシフト)
    }
    digitalWrite(CK,LOW);//1ビット分のクロックパルス出力(LOW)
    delayMicroseconds(1);//1マイクロ秒待機
  }
  delayMicroseconds(3);//3マイクロ秒待機
  return result;//結果を出力
}