フィールド設置型の定点観測ロガーを作成する。 < 定点観測器の仕様 >
< 定点観測データロガーシールド >
|
||
前回の回路をシールド化する。 ▶ 材料リスト
シールドの回路図 |
||
シールド裏側の配線 |
||
シールド裏側は画像の通りかなり複雑な配線になる。 最初に配線した時、耐熱ワイヤを使わなかったためにハンダごての熱でビニールが融けて接触不良になり、 結局すべて剥ぎ取ってもう一度配線し直すことになった。 それでも接触不良が不安だったら、エポキシ系樹脂で配線をガチガチに固定する。 コネクタの接続方法はこちらを参考にさせて頂きました。 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とスリープ制御をすることによってどれだけの期間稼働するのかは想像がつかないし実験などしていられない。 次回は回路をシールド化してみます。 |
2010年11月28日日曜日
パーマカルチャー
パーマカルチャーとは、人間にとっての永続的な循環型生活環境を作り出す為のデザイン体系のことである。 環境負荷をなるべく減らして自給自足生活を営むための方法?だという。 こちらの本『パーマカルチャー―農的暮らしの永久デザイン』を読んでみると、農業を始めとして、 家屋のレイアウト、周囲の建築物・自然物の配置、資材の選別方法、エネルギー循環、水の確保、狩猟採集、食料の加工と保存、 有用生物の飼育、排泄物のリサイクル、微生物利用...はては地形の造成など、取り扱う事象は多岐にわたるようである。 たんなる田舎暮らしの指南ではなく、科学的に循環型自給自足を追求しているところが面白そう。 ニワトリの機能分析。 必要とするもの
生産性と行動
品種固有の特徴
この他にも、
上記を例として、観察することによって得たモノの構成要素を、他のモノの構成要素とうまく関連づけることが基本である。 そしてあるモノのアウトプットが他のモノのインプットとなるようにものの配置を変え、既存のものを作り替えていくという感じ。 特徴的なのは、人間の生活全般に関わる事すべてのアウトプット〜インプットの流れを循環させようとしていることである。 その中には、当然のこととして廃棄物・排泄物のリサイクル利用が含まれている。 特定の分野の技術や能力を生かせる場所(会社等)で同系統の仕事をやっていくことが都会的な合理性であると定義すると、 パーマカルチャーは逆の意味で合理的な思想を持っているようである。 創立者はタスマニア島出身の漁師であるビル・モリソン氏とデビット・ホルムグレン氏。 オーストラリアでは学校の授業の単位にパーマカルチャーが組まれている。 しかし、もとはといえばビル・モリソン氏のパーマカルチャー発想の原点となったのは、西洋型の非循環型単一農業に疑問を感じて 日本を訪れた時に見た里山文化であったという。 週末の二日間、パーマカルチャーセンタージャパンの体験講座に参加した。 JR中央線の藤野駅から車で10数分の山の中にある古民家で、ここで年間を通じてパーマカルチャーの講座が催されている。 高度400mにあるので、とても気温が低い。 PCCJ代表理事の設楽清和さんとスタッフのYさん、Gさんが迎えてくれた。 案内していただいた施設には様々な工夫が凝らしてある。 土釜の上に作られた温室、コンポストトイレ、薪で熱する暖炉、廃熱循環の床暖房、など。 農園の散歩と夕食をごちそうになった後、ニュージーランドのレインボーバレーファームのドキュメンタリ映像が上映される。 | |||
朝の散歩の時にみかけた神社。 空気が透きとおっていて気持ちがよい。 この神社の舞台は地元のアーティストのライブにも利用されるという。 歌手のUAさんも藤野在住で、たまに出演するとかしないとか。 | |||
PCCJのキッチンには保存用に加工された食物を入れたビンが壁一面に並んでいた。 | |||
紅葉が美しい。 藤野は湖と里山の街。 この時期には街は紅葉の山々に囲まれる。 | |||
PCCJのパーマカルチャー農園。 変った形をした畑が並ぶ。 一般的な直線の形をした畝は見当たらない。 パーマカルチャーの農園では、畝に曲線を多様する。 これにより、畝と通路に細かい境界線が多数生まれ、微気象の変化に寄って様々な植生が生まれる。 すべての通路に段ボールを敷き、近所の木工所でもらってきたオガクズを盛ってある。 歩きやすくなるとともに、段ボールを好んで食べるミミズの働きで通路そのものが堆肥となっていく。 | |||
移動式チキントラクター用の囲い。 といっても電動ではない。 この中にニワトリ数羽を二日間入れておくと、囲いの中のすべての雑草を食べ尽くし、餌をあさって土をほじくり返し、耕してくれる。 糞はそのまま肥料として利用する。 | |||
写真では解りにくいが、畝の周りには一定間隔でネギを植えてある。 地中のセンチュウを抑止する働きがある。 マルチとして畝全体に10数センチほどの厚さで落葉が盛ってある。 土は微生物の塊なので、基本的に紫外線にはさらさない方がよいらしい。 また、地中の水分は陽が昇るとともに水蒸気となってすごい勢いで蒸発していく。 落葉マルチは水分蒸発を防ぐ役割もある。 ふかふかの土を手にとって匂いを嗅いでみると、まさしく森の匂いがした。 これは土の中の有効菌が発する匂いである。 もとは粘土質の土だったそうだけど、7年ほど工夫を凝らして土つくりを続けた結果、このような上質の土に変っていったという。 | |||
講座の塾生が作った保存室。 材料はとても安価。 土嚢とモルタルでできていて、室内の温度を一定に保つ。 天井には白い屋根板が張ってあり、日中の太陽光を跳ね返す。 入り口は北を向いており、日光が室内に入りにくくなっている。 | |||
夕食の後にパーマカルチャーについてのディスカッション。 理事の設楽さんとスタッフのYさんはビル・モリソン氏に師事していたこともあるそうだ。 とてもきさくな方達で、酒を呑みつついろんな事を話した。 パーマカルチャーをどのように生活に取り入れるかはやる人次第で、決まった回答は用意されていない。 地方の山奥に籠って本格的にやる人。 マンションのベランダで完全有機栽培を目指す人。 常にモノを循環させていく生活の中で唯一の永続的なるものは太陽エネルギーであり、 それによって、地球に気象が生まれ、大気が循環し、様々な植生が生まれ、多様性が維持される。 ソ連解体時にロシアがパニックに陥らなかった理由は、首相から一般家庭の家のほとんどに農地があったからだとか。 外部経済に依存した生活形態は安定には遠いことであるとか。 自分の生活を振り返ってみると、なにかしらの外部要素に依存していないものを探す方が困難である。 太陽と空気ぐらいだろうか。 ビル・モリソン氏についての話も出た。 ビル氏いわく、 「パーマカルチャーの究極の目標は世界中を森にすることだ。」 とのこと。 その言葉はとてもアナーキーである。 ビル氏本人はそれを体現しているような方で、いつも裸足で歩いているので足の裏の表皮が数センチにもなってしまって、 人間の腕くらいの太さの木の棒をひと蹴りで割ってしまうらしい。 野人のようなお方である。 * 人工物に囲まれた生活空間の中では、自給自足などイメージするには余りにも遠い。 農業技術などなにも知らないのに、広い農地の前で立ち尽くしている自分。 畑を荒らすイノシシと奮闘している自分。 コンポストトイレから堆肥化した排泄物をかきだしている自分。 食べるものといえばコンビニ、ではなくて畑からという発想をしている自分。 月10万円で暮らしている自分。 毎日なにかしらの予想できない新しいトラブルが発生し、なんとか乗り切ろうとしていると、 1日があっという間に過ぎてしまうらしい。 しかし必要なこと自分のためだけに使っているので、精神的なストレスはあまりなさそうである。 もし自分がやるとしたら、そのような困難を乗り越える事ができるだろうか。 やる人は、可能かどうかの結論を出す前に踏み出すんだろうな。 |
登録:
投稿 (Atom)