フィールド設置型の定点観測ロガーを作成する。 < 定点観測器の仕様 >
< 定点観測データロガーシールド >
|
||
前回の回路をシールド化する。 ▶ 材料リスト
シールドの回路図 |
||
シールド裏側の配線 |
||
シールド裏側は画像の通りかなり複雑な配線になる。 最初に配線した時、耐熱ワイヤを使わなかったためにハンダごての熱でビニールが融けて接触不良になり、 結局すべて剥ぎ取ってもう一度配線し直すことになった。 それでも接触不良が不安だったら、エポキシ系樹脂で配線をガチガチに固定する。 コネクタの接続方法はこちらを参考にさせて頂きました。 GNDへはなるべく一点アースにし、回路どうしが干渉しないようにする。 SDソケットは剥がれやすいのでワイヤで固定してある。 多回転半固定ボリューム抵抗はハンダ付けする前にピンを回転させて21KΩにセットしておく。 それと、Arduinoとシールドをつなぐピンのうち、RESETピンは不要なので取り付けないようにする。 取り付けたままでスケッチをアップロードしようとすると、下記のようなエラーが出てアップできない。 avrdude: stk500_getsync(): not in sync: resp=0x00 avrdude: stk500_disable(): protocol error, expect=0x14, resp=0x00 最終的なスケッチは以下のようになった。 Source code: Honeypot ▼ /* * sketch name : Honeypot * summary : フィールド設置型 省電力定点計測データロガー * Sensors : 温度 & 照度 & 気温 & 湿度 & 露点 * releases : 2010/12/27 */ #include <Wire.h> #include <DateTime.h> #include <Fat16.h> #include <Fat16util.h> #include <Watchdog.h> #include <Sleep.h> #include <Sensirion.h> //////////////////////////////////////////////////// // default values ////////////////////////////////// //////////////////////////////////////////////////// /* Arduino 基準電圧 */ #define ARDUINO_VCC 3.3 /* 時刻関連 */ // 計測タイムスタンプ初期値(初回セッティング時のみ) unsigned int year = 2010; unsigned int month = 12; unsigned int day = 27; unsigned int hour = 23; unsigned int minute = 55; unsigned int second = 0; #define LOGGING_INTERVAL_SEC 300 // 計測間隔(秒) #define RETOUCH_INTERVAL_SEC 3600 // 計測間隔によるタイムスタンプのずれの修正間隔(秒) ※必ず LOGGING_INTERVAL_SEC < RETOUCH_INTERVAL_SEC #define WATCHDOG_TIMER_SLEEP_INTERVAL 1 // スリープ間隔(1秒固定) #define TIMESTAMP_JST_DEF_SEC 32400 // 日本標準時(JST)への修正秒数(9時間固定) #define SMOOTHING_DELAY_M_SEC 50 // スムージング計測間隔 #define SMOOTHING_DELAY_NUM 5 // スムージング計測回数 unsigned long log_timestamp; // 計測時刻タイムスタンプ unsigned long time1; // 計測開始時刻(ms) unsigned long time2; // 計測終了時刻(ms) unsigned long integrated_time; // 計測間隔の集積 unsigned int cnt_interval; // スリープカウント unsigned int cnt_interval_full; // 計測までのスリープカウント数 unsigned long retouch_cnt_interval_full; // タイムスタンプ修正までのカウント(秒) /* 外部EEPROMアドレス */ #define EEPROM_DEV_ADDRESS 0x50 /* 温度センサ type / 0:LM35DZ 1:LM60BIZ 2:LM61CIZ */ unsigned int temp_pins[] = {0, 1, 2}; // アナログピン0,1,2 unsigned int temp_sensor_type[] = {1, 1, 1}; // 使用センサタイプ(temp_pins の値に対応) float temp_gain[] = {3.1, 3.1, 3.1}; // Op-amp ゲイン(temp_pins の値に対応) /* 照度センサ type / 0:S9648-100 */ unsigned int illumi_pins[] = {3}; // アナログピン3 unsigned int illumi_sensor_type[] = {0}; // 使用センサタイプ(illumi_pins の値に対応) #define ILLMI_MAX_LX (ARDUINO_VCC * 1000) // S9648-100使用時の最大計測照度(lx) /* 気温・湿度・露点 type / SHT71 */ #define SHT_CLOCK_PIN 7 // CLOCK(デジタルピン) #define SHT_DATA_PIN 8 // DATA(デジタルピン) /* センサのカウント回数 */ #define CNT_TEMP_PINS sizeof(temp_pins) / sizeof(temp_pins[0]) // 温度センサ #define CNT_ILLMI_PINS sizeof(illumi_pins) / sizeof(illumi_pins[0]) // 照度センサ #define CNT_SHT 3 // SHTセンサ(センサ数) /* 計測値格納用 */ float temp_data[CNT_TEMP_PINS]; // 温度データ格納 int illmi_data[CNT_ILLMI_PINS]; // 照度データ格納 float sht_data[CNT_SHT]; // SHTデータ格納 /* SDカード関連 */ #define SD_ERROR_OK 0 #define SD_ERROR_CARD_INIT 2 #define SD_ERROR_FAT16_INIT 3 #define SD_ERROR_OPEN 4 #define SD_ERROR_WRITE 5 #define SD_ERROR_CLOSE 6 unsigned int sd_error_no; // SDエラー番号格納 // エラー文字列 char *sd_error[] = { "no error.", " ", "error about sd card init.", "error about sd fat16 init.", "error about sd open.", "error about sd write.", "error about sd close.", }; #define SD_LOG_DELIMITER ',' // 記録時のデータ区切り文字 ','(カンマ) #define LOG_FILE_NAME "log_data.csv" // 記録ファイル名 /* 計測時の確認用点滅LED */ #define LED_PIN 9 /* ライブラリのインスタンス化 */ Sensirion obj_sht = Sensirion(SHT_DATA_PIN, SHT_CLOCK_PIN); WatchdogClass obj_watchdog = WatchdogClass(); SdCard obj_card; Fat16 obj_file; //////////////////////////////////////////////////// // setup /////////////////////////////////////////// //////////////////////////////////////////////////// void setup() { // デバッグ時のみ //Serial.begin(9600); // 処理確認LED pinMode(LED_PIN, OUTPUT); // 外部EEPROM(I2Cデバイス)に接続 Wire.begin(EEPROM_DEV_ADDRESS); // Watchdogとスリープの設定 obj_watchdog.systemResetEnable(false); // スリープからの自動復帰時にスケッチがリセットされないように設定 obj_watchdog.enable(WatchdogClass::TimeOut1s); // スリープ間隔を1秒間にセット cnt_interval_full = LOGGING_INTERVAL_SEC / WATCHDOG_TIMER_SLEEP_INTERVAL; // 1秒間に設定されたスリープの繰り返し回数 // 変数初期化 time1 = 0; time2 = 0; integrated_time = 0; cnt_interval = 0; retouch_cnt_interval_full = 0; // タイムスタンプ初期化 log_timestamp = i2cEeprom4BRead(EEPROM_DEV_ADDRESS, 0); // 外部EEPROMにタイムスタンプが記録されていたら読み込む if (log_timestamp == 0) { // そうでないならばDatetime設定値(最初のセッティング時のみ) DateTime.sync(DateTime.makeTime(second, minute, hour, day, month, year)); // DateTime初期化 log_timestamp = DateTime.now() - TIMESTAMP_JST_DEF_SEC; // 日本標準時(JST) = グリニッジ標準時(GMT) - 9時間 } } //////////////////////////////////////////////////// // loop //////////////////////////////////////////// //////////////////////////////////////////////////// void loop() { // スリープ開始から設定間隔で自動復帰 time1 = micros(); // 計測と記録 if ((cnt_interval >= cnt_interval_full)) { /* 処理確認用LED ON */ digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); /* 時刻関連処理 */ // タイムスタンプ追加 log_timestamp += LOGGING_INTERVAL_SEC; // タイムスタンプを外部EEPROMに記録 i2cEeprom4BWrite(EEPROM_DEV_ADDRESS, 0, log_timestamp); // スリープカウント初期化 cnt_interval = 0; // タイムスタンプ修正までのカウントを加える retouch_cnt_interval_full += LOGGING_INTERVAL_SEC; /* 計測処理 */ // 温度 float integrated_temp = 0; for (int i = 0; i < CNT_TEMP_PINS; i++) { // スムージング処理:複数回計測の平均値を算出 for (int j = 0; j < SMOOTHING_DELAY_NUM; j++) { integrated_temp += tempCalc(ARDUINO_VCC, temp_sensor_type[i], analogRead(i), temp_gain[i]); delay(SMOOTHING_DELAY_M_SEC); } temp_data[i] = integrated_temp / SMOOTHING_DELAY_NUM; integrated_temp = 0; } // 照度 float integrated_illmi = 0; for (int k = 0; k < CNT_ILLMI_PINS; k++) { int l = CNT_TEMP_PINS + k; // スムージング処理:複数回計測の平均値を算出 for (int m = 0; m < SMOOTHING_DELAY_NUM; m++) { integrated_illmi += (analogRead(l) * ILLMI_MAX_LX) / 1024; delay(SMOOTHING_DELAY_M_SEC); } illmi_data[k] = integrated_illmi / SMOOTHING_DELAY_NUM; integrated_illmi = 0; } // 気温 & 湿度 & 露点 obj_sht.measure(&sht_data[0], &sht_data[1], &sht_data[2]); /* SDカードに記録 */ sd_error_no = dataToSdFileWrite (log_timestamp, temp_data, illmi_data, sht_data, CNT_TEMP_PINS, CNT_ILLMI_PINS, CNT_SHT); // エラー時はエラーNo.分だけ点滅 if (sd_error_no != SD_ERROR_OK) { for (int n = 1; n <= sd_error_no; n++) { digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); } // デバッグ //Serial.print(" error! : "); //Serial.println(sd_error[sd_error_no]); } /* 処理確認用LED OFF */ digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); // デバッグ /* Serial.println("=========="); Serial.print("timestamp : "); Serial.println(log_timestamp); Serial.print("temp1 : "); Serial.println(temp_data[0]); Serial.print("temp2 : "); Serial.println(temp_data[1]); Serial.print("temp3 : "); Serial.println(temp_data[2]); Serial.print("illmi1 : "); Serial.println(illmi_data[0]); Serial.print("sht temp : "); Serial.println(sht_data[0]); Serial.print("sht humidity : "); Serial.println(sht_data[1]); Serial.print("sht dewpoint: " ); Serial.println(sht_data[2]); Serial.println("=========="); Serial.println(); */ // カウントアップ } else { cnt_interval++; } // 計測間隔による時刻カウントのずれの修正 time2 = micros(); integrated_time += (time2 - time1); if (retouch_cnt_interval_full >= RETOUCH_INTERVAL_SEC) { cnt_interval += (int)(integrated_time / 1000000) / WATCHDOG_TIMER_SLEEP_INTERVAL; retouch_cnt_interval_full = 0; integrated_time = 0; } // デバッグ /* Serial.println("=========="); Serial.print("integrated_time : "); Serial.println(integrated_time); Serial.print("retouch_cnt_interval_full : "); Serial.println(retouch_cnt_interval_full, DEC); Serial.println("=========="); Serial.println(); */ // スリープ開始 WatchdogClass::timerReset(); SleepClass::powerDown(); } //////////////////////////////////////////////////// // function //////////////////////////////////////// //////////////////////////////////////////////////// /* * func name : dataToSdFileWrite * processing : SDカード記録 * param : log_timestamp / タイムスタンプ * temp_data / 温度データ格納配列 * illmi_data / 照度データ格納配列 * sht_data / SHTデータ格納配列 * cnt_temp_pins / 温度データ数 * cnt_illmi_pins / 照度データ数 * cnt_sht / SHTデータ数 * return : エラー番号 */ int dataToSdFileWrite (unsigned long log_timestamp, float *temp_data, int *illmi_data, float *sht_data, int cnt_temp_pins, int cnt_illmi_pins, int cnt_sht) { if (!obj_card.init()) return SD_ERROR_CARD_INIT; if (!Fat16::init(&obj_card)) return SD_ERROR_FAT16_INIT; // O_CREAT : ファイルが存在しなかったら作成する // O_APPEND : 書き込み前に、ファイル内のデータの最後尾を探す(追記) // O_WRITE : 書き込みのためにファイルを開く if (!obj_file.open(LOG_FILE_NAME, O_CREAT | O_APPEND | O_WRITE)) return SD_ERROR_OPEN; obj_file.writeError = false; // タイムスタンプ記録 obj_file.print(log_timestamp); obj_file.print(SD_LOG_DELIMITER); // 温度記録 for (int i = 0; i < cnt_temp_pins; i++) { obj_file.print(temp_data[i]); obj_file.print(SD_LOG_DELIMITER); // 区切り文字 if (obj_file.writeError) return SD_ERROR_WRITE; } // 照度記録 for (int j = 0; j < cnt_illmi_pins; j++) { obj_file.print(illmi_data[j]); obj_file.print(SD_LOG_DELIMITER); // 区切り文字 if (obj_file.writeError) return SD_ERROR_WRITE; } // SHTデータ記録 for (int k = 0; k < cnt_sht; k++) { obj_file.print(sht_data[k]); // 最後のデータだったら改行 if (k == (cnt_sht - 1)) { obj_file.println(" "); // それ以外は区切り文字 } else { obj_file.print(SD_LOG_DELIMITER); } if (obj_file.writeError) return SD_ERROR_WRITE; } if (!obj_file.close()) return SD_ERROR_CLOSE; return SD_ERROR_OK; } /* * func name : i2cEeprom4BWrite * processing : 4byte値をI2C外部EEPROMに書き出す * param : deviceAddress / I2C外部EEPROMのデバイスアドレス * startMemoryAddress / EEPROM上の書き込み開始アドレス * longParam / 書き込む4byteのデータ * summary : デバイスの0番目の番地 / 32〜25bit * 1番目の番地 / 24〜17bit * 2番目の番地 / 16〜9bit * 3番目の番地 / 8〜1bit * return : */ void i2cEeprom4BWrite(int deviceAddress, unsigned long startMemoryAddress, long longParam) { Wire.beginTransmission(deviceAddress); Wire.send((int)(startMemoryAddress >> 8)); Wire.send((int)(startMemoryAddress & 0xFF)); byte byteArray[sizeof(long)] = { (byte)(longParam >> 24), (byte)(longParam >> 16), (byte)(longParam >> 8), (byte)(longParam >> 0) }; for (int i = 0; i < sizeof(long); i++) { Wire.send(byteArray[i]); } Wire.endTransmission(); delay(5); } /* * func name : i2cEeprom4BRead * processing : 4byte値をI2C外部EEPROMから読み出す * param : deviceAddress / I2C外部EEPROMのデバイスアドレス * startMemoryAddress / EEPROM上の読み込み開始アドレス * return : return_long / 読み込んだ4byteデータ */ long i2cEeprom4BRead(int deviceAddress, unsigned long startMemoryAddress) { Wire.beginTransmission(deviceAddress); Wire.send((int)(startMemoryAddress >> 8)); Wire.send((int)(startMemoryAddress & 0xFF)); Wire.endTransmission(); Wire.requestFrom(deviceAddress, sizeof(long)); delay(5); long received_long; long return_long = 0; int cnt = sizeof(long) - 1; for (int i = 0; i <= cnt; i++) { if (Wire.available()) { received_long = Wire.receive(); return_long |= (received_long << (8 * (cnt - i))); } } return return_long; } /* * func name : tempCalc * processing : 温度センサ処理 * param : arduinoVcc / Arduino 基準電圧 * sensor / 0 : LM35DZ * 1 : LM60BIZ * 2 : LM61CIZ * analogVal / Analog値(0-1023) * gain / Op-amp ゲイン * return : temp / 計測温度(℃) */ float tempCalc (float arduinoVcc, int sensor, int analogVal, float gain) { float dc_offset; // DCオフセット電圧(V) float factor; // 温度係数(V/1℃) switch (sensor) { // LM35DZ // DCオフセット 0V // +10mV/℃ case 0: dc_offset = 0; factor = 0.01; break; // LM60BIZ // DCオフセット 424mV // +6.25mV/℃ case 1: dc_offset = 0.424; factor = 0.00625; break; // LM61CIZ // DCオフセット 600mV // +10mV/℃ case 2: dc_offset = 0.6; factor = 0.01; break; define: return 0; break; } return (((arduinoVcc * analogVal) / 1024) - (dc_offset * gain)) / (factor * gain); } 5分間隔で計測し、SDカードにデータを記録する。 温度と照度は一度の計測につき5回センシングしてその平均値を採用する。 1時間に一度、タイムスタンプのブレを修正する。 SDカード記録時のエラーはLEDの点滅回数で表す。 Watchdogとスリープ制御処理も記述する。 上記スケッチをアップロードする前に外部EEPROMをクリアしなければならない。 以下のスケッチをアップロードすると、D13のLEDが点灯する。 そうしたらクリア完了。 Source code: eeprom_I2C_clear ▼ /* * sketch name : eeprom_I2C_clear * summary : 外部EEPROMのクリア */ #include <Wire.h> #define EEPROM_DEV_ADDRESS 0x50 // I2CEEPROM 24LC64 #define CLEAR_BYTE_LEN 10 // クリアするbyte数 void setup(void) { Wire.begin(EEPROM_DEV_ADDRESS); i2cEepromClear(EEPROM_DEV_ADDRESS, 0, CLEAR_BYTE_LEN); // turn the LED on when we're done digitalWrite(13, HIGH); } void loop() {} /* * func name : i2cEeprom4BWrite * processing : 4byte値をI2C外部EEPROMに書き出す * param : device_address / I2C外部EEPROMのデバイスアドレス * start_memory_address / EEPROM上の書き込み開始アドレス * clear_length / クリアするbyte数 * return : */ void i2cEepromClear(int device_address, unsigned long start_memory_address, long clear_length) { Wire.beginTransmission(device_address); Wire.send((int)(start_memory_address >> 8)); Wire.send((int)(start_memory_address & 0xFF)); for (int i = 0; i < clear_length; i++) { Wire.send(0); } Wire.endTransmission(); } |
||
20分後にSDカードを取り出してデータを見てみた。 ","(カンマ)区切りで、左からタイムスタンプ、温度1、温度2、温度3、照度、気温、湿度、露点のデータが記録されている。 次回は、屋外に設置して計測をしてみたいと思います。 |
2010年12月27日月曜日
定点観測データロガー -7 : 観測用シールド
フィールド設置型の定点観測ロガーを作成する。 < 定点観測器の仕様 >
< 定点観測データロガーシールド >
|
||
前回の回路をシールド化する。 ▶ 材料リスト シールドの回路図 |
||
シールド裏側の配線 |
||
シールド裏側は画像の通りかなり複雑な配線になる。 最初に配線した時、耐熱ワイヤを使わなかったためにハンダごての熱でビニールが融けて接触不良になり、 結局すべて剥ぎ取ってもう一度配線し直すことになった。 それでも接触不良が不安だったら、エポキシ系樹脂で配線をガチガチに固定する。 コネクタの接続方法はこちらを参考にさせて頂きました。 GNDへはなるべく一点アースにし、回路どうしが干渉しないようにする。 SDソケットは剥がれやすいのでワイヤで固定してある。 多回転半固定ボリューム抵抗はハンダ付けする前にピンを回転させて21KΩにセットしておく。 それと、Arduinoとシールドをつなぐピンのうち、RESETピンは不要なので取り付けないようにする。 取り付けたままでスケッチをアップロードしようとすると、下記のようなエラーが出てアップできない。 avrdude: stk500_getsync(): not in sync: resp=0x00 avrdude: stk500_disable(): protocol error, expect=0x14, resp=0x00 最終的なスケッチは以下のようになった。 Source code: Honeypot ▼ 5分間隔で計測し、SDカードにデータを記録する。 温度と照度は一度の計測につき5回センシングしてその平均値を採用する。 1時間に一度、タイムスタンプのブレを修正する。 SDカード記録時のエラーはLEDの点滅回数で表す。 Watchdogとスリープ制御処理も記述する。 上記スケッチをアップロードする前に外部EEPROMをクリアしなければならない。 以下のスケッチをアップロードすると、D13のLEDが点灯する。 そうしたらクリア完了。 Source code: eeprom_I2C_clear ▼ |
||
20分後にSDカードを取り出してデータを見てみた。 ","(カンマ)区切りで、左からタイムスタンプ、温度1、温度2、温度3、照度、気温、湿度、露点のデータが記録されている。 次回は、屋外に設置して計測をしてみたいと思います。 |
2010年12月26日日曜日
定点観測データロガー -6 : 省電力テスト
フィールド設置型の定点観測ロガーを作成する。 < 定点観測器の仕様 >
< Watchdogとスリープ機能を省いて省電力テストをする > |
とりあえずWatchdogとスリープを使用しないでどれくらい稼働するのかを試してみた。 外部電源はArduinoボードのVinとGNDに繋ぐ。 エネループ単三電池はフル充電で1.4V 130mAなので4個直列に繋ぐと5.6V 520mAとなるが、Vinから供給された電圧は3.3Vに降圧される。 スケッチからもわかる通り、計測と記録の開始と終了時に、処理確認用のLEDを一度光らせるようにしてある。 |
/* * sketch name : Honeypot_test * summary : フィールド設置型 省電力定点計測データロガー スリープなしテスト * Sensors : 温度 & 照度 & 気温 & 湿度 & 露点 * releases : 2010/11/6 */ #include <Wire.h> #include <DateTime.h> #include <Fat16.h> #include <Fat16util.h> #include <Sensirion.h> //////////////////////////////////////////////////// // default values ////////////////////////////////// //////////////////////////////////////////////////// /* Arduino 基準電圧 */ #define ARDUINO_VCC 3.3 /* 時刻関連 */ // 計測タイムスタンプ初期値(初回セッティング時のみ) unsigned int year = 2010; unsigned int month = 12; unsigned int day = 3; unsigned int hour = 0; unsigned int minute = 0; unsigned int second = 0; #define LOGGING_INTERVAL_SEC 300 // 計測間隔(秒) #define TIMESTAMP_JST_DEF_SEC 32400 // 日本標準時(JST)への修正秒数(9時間固定) unsigned long log_timestamp; // 計測時刻タイムスタンプ /* 外部EEPROMアドレス */ #define EEPROM_DEV_ADDRESS 0x50 /* 温度センサ type / 0:LM35DZ 1:LM60BIZ 2:LM61CIZ */ unsigned int temp_pins[] = {0, 1, 2}; // アナログピン0,1,2 unsigned int temp_sensor_type[] = {2, 2, 2}; // 使用センサタイプ(temp_pins の値に対応) float temp_gain[] = {2, 2, 2}; // Op-amp ゲイン(temp_pins の値に対応) /* 照度センサ type / 0:S9648-100 */ unsigned int illumi_pins[] = {3}; // アナログピン3 unsigned int illumi_sensor_type[] = {0}; // 使用センサタイプ(illumi_pins の値に対応) #define ILLMI_MAX_LX ARDUINO_VCC * 1000 // S9648-100使用時の最大計測照度(lx) /* 気温・湿度・露点 type / SHT71 */ #define SHT_CLOCK_PIN 7 // CLOCK(デジタルピン) #define SHT_DATA_PIN 8 // DATA(デジタルピン) /* センサのカウント回数 */ #define CNT_TEMP_PINS sizeof(temp_pins) / sizeof(temp_pins[0]) // 温度センサ #define CNT_ILLMI_PINS sizeof(illumi_pins) / sizeof(illumi_pins[0]) // 照度センサ #define CNT_SHT 3 // SHTセンサ(センサ数) /* 計測値格納用 */ float temp_data[CNT_TEMP_PINS]; // 温度データ格納 int illmi_data[CNT_ILLMI_PINS]; // 照度データ格納 float sht_data[CNT_SHT]; // SHTデータ格納 /* SDカード関連 */ #define SD_ERROR_OK 0 #define SD_ERROR_CARD_INIT 1 #define SD_ERROR_FAT16_INIT 2 #define SD_ERROR_OPEN 3 #define SD_ERROR_WRITE 4 #define SD_ERROR_CLOSE 5 unsigned int sd_error_no; // SDエラー番号格納 // エラー文字列 char *sd_error[] = { "no error.", "error about sd card init.", "error about sd fat16 init.", "error about sd open.", "error about sd write.", "error about sd close.", }; #define SD_LOG_DELIMITER ',' // 記録時のデータ区切り文字 ','(カンマ) #define LOG_FILE_NAME "log_data.csv" // 記録ファイル名 /* 計測時の確認用点滅LED */ #define LED_PIN 9 /* ライブラリのインスタンス化 */ Sensirion obj_sht = Sensirion(SHT_DATA_PIN, SHT_CLOCK_PIN); SdCard obj_card; Fat16 obj_file; // 初回フラグ boolean setup_flg = true; //////////////////////////////////////////////////// // setup /////////////////////////////////////////// //////////////////////////////////////////////////// void setup() { pinMode(LED_PIN, OUTPUT); // 外部EEPROM(I2Cデバイス)に接続 Wire.begin(EEPROM_DEV_ADDRESS); // タイムスタンプ初期化 log_timestamp = i2cEeprom4BRead(EEPROM_DEV_ADDRESS, 0); // 外部EEPROMにタイムスタンプが記録されていたら読み込む if (log_timestamp == 0) { // そうでないならばDatetime設定値(最初のセッティング時のみ) DateTime.sync(DateTime.makeTime(second, minute, hour, day, month, year)); // DateTime初期化 log_timestamp = DateTime.now() - TIMESTAMP_JST_DEF_SEC; // 日本標準時(JST) = グリニッジ標準時(GMT) - 9時間 setup_flg = false; } } //////////////////////////////////////////////////// // loop //////////////////////////////////////////// //////////////////////////////////////////////////// void loop() { /* 処理確認用LED ON */ digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); /* 時刻関連処理 */ // タイムスタンプ追加 if (setup_flg) { setup_flg = false; } else { log_timestamp += LOGGING_INTERVAL_SEC; } // タイムスタンプを外部EEPROMに記録 i2cEeprom4BWrite(EEPROM_DEV_ADDRESS, 0, log_timestamp); /* 計測処理 */ // 温度 for (int i = 0; i < CNT_TEMP_PINS; i++) { temp_data[i] = tempCalc(ARDUINO_VCC, temp_sensor_type[i], analogRead(i), temp_gain[i]); //Serial.println(temp_data[i]); } // 照度 for (int j = 0; j < CNT_ILLMI_PINS; j++) { int k = CNT_TEMP_PINS + j; illmi_data[j] = (analogRead(k) * ILLMI_MAX_LX) / 1024; //Serial.println(illmi_data[j]); } // 気温 & 湿度 & 露点 obj_sht.measure(&sht_data[0], &sht_data[1], &sht_data[2]); //Serial.println(sht_data[0]); //Serial.println(sht_data[1]); //Serial.println(sht_data[2]); //Serial.println(); /* SDカードに記録 */ sd_error_no = dataToSdFileWrite (log_timestamp, temp_data, illmi_data, sht_data, CNT_TEMP_PINS, CNT_ILLMI_PINS, CNT_SHT); // エラー時はエラーNo.分だけ点滅 if (sd_error_no != SD_ERROR_OK) { for (int l = 1; l <= sd_error_no; l++) { digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); } //Serial.println(sd_error[sd_error_no]); } /* 処理確認用LED OFF */ digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); delay(300000UL); // 五分間隔 } //////////////////////////////////////////////////// // function //////////////////////////////////////// //////////////////////////////////////////////////// /* * func name : dataToSdFileWrite * processing : SDカード記録 * param : log_timestamp / タイムスタンプ * temp_data / 温度データ格納配列 * illmi_data / 照度データ格納配列 * sht_data / SHTデータ格納配列 * cnt_temp_pins / 温度データ数 * cnt_illmi_pins / 照度データ数 * cnt_sht / SHTデータ数 * return : エラー番号 */ int dataToSdFileWrite (unsigned long log_timestamp, float *temp_data, int *illmi_data, float *sht_data, int cnt_temp_pins, int cnt_illmi_pins, int cnt_sht) { if (!obj_card.init()) return SD_ERROR_CARD_INIT; if (!Fat16::init(&obj_card)) return SD_ERROR_FAT16_INIT; // O_CREAT : ファイルが存在しなかったら作成する // O_APPEND : 書き込み前に、ファイル内のデータの最後尾を探す(追記) // O_WRITE : 書き込みのためにファイルを開く if (!obj_file.open(LOG_FILE_NAME, O_CREAT | O_APPEND | O_WRITE)) return SD_ERROR_OPEN; obj_file.writeError = false; // タイムスタンプ記録 obj_file.print(log_timestamp); obj_file.print(SD_LOG_DELIMITER); //Serial.print("log timestamp : "); //Serial.println(log_timestamp); // 温度記録 for (int i = 0; i < cnt_temp_pins; i++) { obj_file.print(temp_data[i]); obj_file.print(SD_LOG_DELIMITER); // 区切り文字 if (obj_file.writeError) return SD_ERROR_WRITE; //Serial.print("temp"); //Serial.print(i); //Serial.print(" : "); //Serial.println(temp_data[i]); } // 照度記録 for (int j = 0; j < cnt_illmi_pins; j++) { obj_file.print(illmi_data[j]); obj_file.print(SD_LOG_DELIMITER); // 区切り文字 if (obj_file.writeError) return SD_ERROR_WRITE; //Serial.print("illmi"); //Serial.print(j); //Serial.print(" : "); //Serial.println(illmi_data[j]); } // SHTデータ記録 for (int k = 0; k < cnt_sht; k++) { obj_file.print(sht_data[k]); // 最後のデータだったら改行 if (k == (cnt_sht - 1)) { obj_file.println(); // それ以外は区切り文字 } else { obj_file.print(SD_LOG_DELIMITER); } if (obj_file.writeError) return SD_ERROR_WRITE; //Serial.print("sht"); //Serial.print(k); //Serial.print(" : "); //Serial.println(sht_data[k]); } //Serial.println(); if (!obj_file.close()) return SD_ERROR_CLOSE; return SD_ERROR_OK; } /* * func name : i2cEeprom4BWrite * processing : 4byte値をI2C外部EEPROMに書き出す * param : deviceAddress / I2C外部EEPROMのデバイスアドレス * startMemoryAddress / EEPROM上の書き込み開始アドレス * longParam / 書き込む4byteのデータ * summary : デバイスの0番目の番地 / 32〜25bit * 1番目の番地 / 24〜17bit * 2番目の番地 / 16〜9bit * 3番目の番地 / 8〜1bit * return : */ void i2cEeprom4BWrite(int deviceAddress, unsigned long startMemoryAddress, long longParam) { Wire.beginTransmission(deviceAddress); Wire.send((int)(startMemoryAddress >> 8)); Wire.send((int)(startMemoryAddress & 0xFF)); byte byteArray[sizeof(long)] = { (byte)(longParam >> 24), (byte)(longParam >> 16), (byte)(longParam >> 8), (byte)(longParam >> 0) }; for (int i = 0; i < sizeof(long); i++) { Wire.send(byteArray[i]); } Wire.endTransmission(); delay(5); } /* * func name : i2cEeprom4BRead * processing : 4byte値をI2C外部EEPROMから読み出す * param : deviceAddress / I2C外部EEPROMのデバイスアドレス * startMemoryAddress / EEPROM上の読み込み開始アドレス * return : return_long / 読み込んだ4byteデータ */ long i2cEeprom4BRead(int deviceAddress, unsigned long startMemoryAddress) { Wire.beginTransmission(deviceAddress); Wire.send((int)(startMemoryAddress >> 8)); Wire.send((int)(startMemoryAddress & 0xFF)); Wire.endTransmission(); Wire.requestFrom(deviceAddress, sizeof(long)); delay(5); long received_long; long return_long = 0; int cnt = sizeof(long) - 1; for (int i = 0; i <= cnt; i++) { if (Wire.available()) { received_long = Wire.receive(); return_long |= (received_long << (8 * (cnt - i))); } } return return_long; } /* * func name : tempCalc * processing : 温度センサ処理 * param : arduinoVcc / Arduino 基準電圧 * sensor / 0 : LM35DZ * 1 : LM60BIZ * 2 : LM61CIZ * analogVal / Analog値(0-1023) * gain / Op-amp ゲイン * return : temp / 計測温度(℃) */ float tempCalc (float arduinoVcc, int sensor, int analogVal, float gain) { float dc_offset; // DCオフセット電圧(V) float factor; // 温度係数(V/1℃) switch (sensor) { // LM35DZ // DCオフセット 0V // +10mV/℃ case 0: dc_offset = 0; factor = 0.01; break; // LM60BIZ // DCオフセット 424mV // +6.25mV/℃ case 1: dc_offset = 0.424; factor = 0.00625; break; // LM61CIZ // DCオフセット 600mV // +10mV/℃ case 2: dc_offset = 0.6; factor = 0.01; break; define: return 0; break; } return (((arduinoVcc * analogVal) / 1023) - (dc_offset * gain)) / (factor * gain); } スケッチをアップロードし電池ボックスの電源をONにすると、稼働し始める。 うっかりしていて外部EEPROMにミス入力したままのタイムスタンプ情報が残っていたのか、 10日後に取り出したSDカードのデータを見てみると1行目のタイムスタンプが「1293359672(2010/12/26 19:34:32)」となっていた。 スケッチをアップする前に外部EEPROMを空にすることを忘れていたためだが、特に問題はない。 データは一回の計測につき","(カンマ)区切り1行で記録される。 左から「タイムスタンプ」「温度1」「温度2」「温度3」「照度」「気温」「湿度」「露点」となっている。 |
こちらはログファイルの末尾である。 「1294213772(2011/01/05 16:49:32)」となっている。 5分で一度、つまり1日288行のデータが記録されるので、2851行分のデータは妥当な量である。 |
待つのに飽きたし目的の1週間を過ぎたので10日で取り出したけれど、まだまだいけそうである。 100円ショップで購入した電池残量チェッカーによると、まだ3/4ほど電力が残っている。 |
10日は確実に稼働することがわかったけれど、 Watchdogとスリープ制御をすることによってどれだけの期間稼働するのかは想像がつかないし実験などしていられない。 次回は回路をシールド化してみます。 |
登録:
投稿 (Atom)