フィールド設置型の定点観測ロガーを作成する。 < 定点観測器の仕様 >
< 定点観測データロガーシールド >
|
||
前回の回路をシールド化する。 ▶ 材料リスト
シールドの回路図 |
||
シールド裏側の配線 |
||
シールド裏側は画像の通りかなり複雑な配線になる。 最初に配線した時、耐熱ワイヤを使わなかったためにハンダごての熱でビニールが融けて接触不良になり、 結局すべて剥ぎ取ってもう一度配線し直すことになった。 それでも接触不良が不安だったら、エポキシ系樹脂で配線をガチガチに固定する。 コネクタの接続方法はこちらを参考にさせて頂きました。 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、照度、気温、湿度、露点のデータが記録されている。 次回は、屋外に設置して計測をしてみたいと思います。 |
登録:
コメントの投稿 (Atom)
0 コメント:
コメントを投稿