RTC8564モジュールを組み込んだ定点観測シールド。 | ||
| ||
| ||
| ||
温度センサLM35DZの値をSHTセンサの温度計測値と同じになるように調整する。 以下のテスト用スケッチを使用。 Sketch: Honeypot2_sensor_test(センサーテスト用) ▼ /*
* sketch name : Honeypot2_sensor_test
* summary : フィールド設置型 省電力定点計測データロガーセンサーテスト
* Sensors : 計測時刻タイムスタンプ & 温度 & 照度 & 気温 & 湿度 & 露点
* releases : 2011/3/27
*/
#include <Wire.h>
#include <Rtc8564AttachInterrupt.h>
#include <Sensirion.h>
#include <DateTime.h>
#include <Sleep.h>
////////////////////////////////////////////////////
// init default vals ///////////////////////////////
////////////////////////////////////////////////////
/* Arduino 基準電圧 */
#define ARDUINO_VCC 3.3
/* RTC定周期タイマ割り込み設定 */
// RTCタイマ開始日付時刻
#define RTC_SEC 0x00 // 秒
#define RTC_MIN 0x05 // 分
#define RTC_HOUR 0x12 // 時
#define RTC_DAY 0x27 // 日
#define RTC_WEEK 0x00 // 曜日(00:日 〜 06:土)
#define RTC_MON 0x03 // 月
#define RTC_YEAR 0x11 // 西暦
#define RTC_INTERRUPT_MODE 0 // 定周期タイマ間隔設定単位 0:秒/1:分
#define RTC_INTERRUPT_TERM 5 // 計測間隔(定周期タイマ間隔)
#define RTC_INTERRUPT_PIN 2 // 外部割り込みピン(Digital 2 pin)
byte date_time[7] = {
RTC_SEC,
RTC_MIN,
RTC_HOUR,
RTC_DAY,
RTC_WEEK,
RTC_MON,
RTC_YEAR,
};
boolean init_flg = false; // 電源リセット確認フラグ
time_t make_time;
unsigned long log_timestamp; // 計測日付時刻
#define TIMESTAMP_JST_DEF_SEC 32400 // 日本標準時(JST)への修正秒数(9時間固定)
/* 計測設定 */
#define SMOOTHING_DELAY_SEC 50 // スムージング計測間隔
#define SMOOTHING_BUFFER_LENGTH 5 // スムージング計測回数
// 温度センサ type / 0:LM35DZ 1:LM60BIZ 2:LM61CIZ
unsigned int temperature_pins[] = {0, 1, 2}; // アナログピン0,1,2
unsigned int temperature_sensor_type[] = {0, 0, 0}; // 使用センサタイプ(temperature_pins の値に対応)
float temperature_gain[] = {3.3, 3.3, 3.3}; // Op-amp ゲイン(temperature_pins の値に対応)
// 照度センサ type / 0:S9648-100
unsigned int illumination_pins[] = {3}; // アナログピン3
// 気温・湿度・露点 type / SHT71
#define SHT_CLOCK_PIN 7 // CLOCK(デジタルピン)
#define SHT_DATA_PIN 8 // DATA(デジタルピン)
// センサ別計数
#define CNT_TEMPERATURE_PINS sizeof(temperature_pins) / sizeof(temperature_pins[0]) // 温度センサ
#define CNT_ILLUMINATION_PINS sizeof(illumination_pins) / sizeof(illumination_pins[0]) // 照度センサ
#define CNT_SHT 3 // SHTセンサ
// 計測値格納用
float temperature_data[CNT_TEMPERATURE_PINS]; // 温度データ格納
unsigned int illumination_data[CNT_ILLUMINATION_PINS]; // 照度データ格納
float sht_data[CNT_SHT]; // SHTデータ格納
/* ライブラリのインスタンス化 */
Sensirion obj_sht = Sensirion(SHT_DATA_PIN, SHT_CLOCK_PIN);
////////////////////////////////////////////////////
// function ////////////////////////////////////////
////////////////////////////////////////////////////
/*
* func name : calcTemperature
* processing : 温度センサの種類別計算処理
* param : arduino_vcc Arduino基準電圧
* sensor_type 0:LM35DZ / 1:LM60BIZ / 2:LM61CIZ
* analog_val Analog値(0-1023)
* gain Op-amp ゲイン
* return : 計測温度(℃)
*/
float calcTemperature(float arduino_vcc, int sensor_type, int analog_val, float gain)
{
float dc_offset; // DCオフセット電圧(V)
float factor; // 温度係数(V/1℃)
switch (sensor_type) {
case 0: // LM35DZ
dc_offset = 0; // DC offset;0V
factor = 0.01; // +10mV/℃
break;
case 1: // LM60BIZ
dc_offset = 0.424; // DC offset 424mV
factor = 0.00625; // +6.25mV/℃
break;
case 2: // LM61CIZ
dc_offset = 0.6; // DC offset 600mV
factor = 0.01; // +10mV/℃
break;
default:
return 0;
break;
}
return (((arduino_vcc * analog_val) / 1023) - (dc_offset * gain)) / (factor * gain);
}
/*
* func name : calcIllumination
* processing : 照度センサS9648-100計算処理
* param : arduino_vcc Arduino基準電圧
* analog_val Analog値(0-1023)
* return : 照度(lx)
*/
unsigned int calcIllumination(float arduino_vcc, unsigned int analog_val)
{
unsigned int illumination_max_lx = arduino_vcc * 1000; // S9648-100使用時の最大計測照度(lx)
return (analog_val * (illumination_max_lx / 1024));
}
/*
* func name : measuerTemperature
* processing : 温度センサ計測
* param : arduino_vcc Arduino基準電圧
* temperature_pins 使用アナログピン
* temperature_sensor_type 使用センサタイプ
* temperature_gain Op-amp ゲイン
* temperature_data 温度データ格納
* cnt_temperature_pins センサのカウント数
* smoothing_delay_sec スムージング計測間隔
* smoothing_buffer_length スムージング計測回数
*/
void measuerTemperature(
float arduino_vcc,
unsigned int *temperature_pins,
unsigned int *temperature_sensor_type,
float *temperature_gain,
float *temperature_data,
unsigned int cnt_temperature_pins,
unsigned int smoothing_delay_sec,
unsigned int smoothing_buffer_length
){
float sum = 0;
for (int i = 0; i < cnt_temperature_pins; i++) {
// Meanフィルタによるスムージング処理:複数回計測の平均値を算出
for (int j = 0; j < smoothing_buffer_length; j++) {
sum += calcTemperature(
arduino_vcc,
temperature_sensor_type[i],
analogRead(temperature_pins[i]),
temperature_gain[i]
);
delay(smoothing_delay_sec);
}
temperature_data[i] = sum / smoothing_buffer_length;
sum = 0;
}
}
/*
* func name : measuerIllumination
* processing : 照度センサ計測
* param : arduino_vcc Arduino基準電圧
* illumination_pins 使用アナログピン
* illumination_data 温度データ格納
* cnt_illumination_pins センサのカウント数
* smoothing_delay_sec スムージング計測間隔
* smoothing_buffer_length スムージング計測回数
* return :
*/
void measuerIllumination(
float arduino_vcc,
unsigned int *illumination_pins,
unsigned int *illumination_data,
unsigned int cnt_illumination_pins,
unsigned int smoothing_delay_sec,
unsigned int smoothing_buffer_length
){
unsigned long sum = 0;
for (int i = 0; i < cnt_illumination_pins; i++) {
// Meanフィルタによるスムージング処理:複数回計測の平均値を算出
for (int j = 0; j < smoothing_buffer_length; j++) {
sum += calcIllumination(arduino_vcc, analogRead(illumination_pins[i]));
delay(smoothing_delay_sec);
}
illumination_data[i] = sum / smoothing_buffer_length;
sum = 0;
}
}
/*
* func name : measuer
* processing : 計測
*/
void measuer(void)
{
// タイムスタンプ作成
Rtc.available();
make_time = DateTime.makeTime(
(int)Rtc.seconds(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.minutes(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.hours(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.days(Rtc8564AttachInterrupt::Decimal),
(((int)Rtc.months(Rtc8564AttachInterrupt::Decimal)) - 1),
(int)(2000 + Rtc.years(Rtc8564AttachInterrupt::Decimal))
);
DateTime.sync(make_time);
log_timestamp = DateTime.now() - TIMESTAMP_JST_DEF_SEC;
// 温度計測
measuerTemperature(
ARDUINO_VCC,
temperature_pins,
temperature_sensor_type,
temperature_gain,
temperature_data,
CNT_TEMPERATURE_PINS,
SMOOTHING_DELAY_SEC,
SMOOTHING_BUFFER_LENGTH
);
// 照度計測
measuerIllumination(
ARDUINO_VCC,
illumination_pins,
illumination_data,
CNT_ILLUMINATION_PINS,
SMOOTHING_DELAY_SEC,
SMOOTHING_BUFFER_LENGTH
);
// 気温 & 湿度 & 露点計測
obj_sht.measure(&sht_data[0], &sht_data[1], &sht_data[2]);
// データ表示
Serial.println();
Serial.println("********************************************");
Serial.print("log_timestamp : ");
Serial.println(log_timestamp);
Serial.print("temperature_data 1 : ");
Serial.println(temperature_data[0]);
Serial.print("temperature_data 2 : ");
Serial.println(temperature_data[1]);
Serial.print("temperature_data 3 : ");
Serial.println(temperature_data[2]);
Serial.print("illumination_data 1 : ");
Serial.println(illumination_data[0]);
Serial.print("sht_data 1 : ");
Serial.println(sht_data[0]);
Serial.print("sht_data 2 : ");
Serial.println(sht_data[1]);
Serial.print("sht_data 3 : ");
Serial.println(sht_data[2]);
Serial.println("********************************************");
Serial.println();
}
////////////////////////////////////////////////////
// setup ///////////////////////////////////////////
////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
// RTC日付時刻初期化
Rtc.initDatetime(date_time);
// RTC設定初期化(日付時刻の継続確認を含む)
Rtc.begin();
// RTC定周期割り込みタイマ継続確認
if (!Rtc.isInterrupt()) {
// 割り込み周期設定
Rtc.syncInterrupt(RTC_INTERRUPT_MODE, RTC_INTERRUPT_TERM);
}
// RTC割り込み設定
pinMode(RTC_INTERRUPT_PIN, INPUT);
digitalWrite(RTC_INTERRUPT_PIN, HIGH);
}
////////////////////////////////////////////////////
// loop ////////////////////////////////////////////
////////////////////////////////////////////////////
void loop() {
// 初期化(電源OFF=>ON)すぐ後は計測しない
if (init_flg) measuer();
// スリープ (パワーダウン+外部割り込みによる復帰)
SleepClass::powerDownAndWakeupExternalEvent(0);
init_flg = true;
}
| ||
| ||
校正後に、以下のスケッチを順番にアップする。 フィールドセット用スケッチを書き込む前に、RTC8564モジュールが前のアップスケッチでの設定を引き継がないように 強制的に日付時刻とタイマー設定をセットするスケッチ。 Sketch: RTC8564DatetimeInit(日付時刻設定用) ▼ /*
* sketch name : RTC8564DatetimeInit
* summary : RTC初期化
* releases : 2011/3/27
*/
#include <Wire.h>
#include <Rtc8564AttachInterrupt.h>
/* RTCタイマ開始日付時刻 */
#define RTC_SEC 0x00 // 秒
#define RTC_MIN 0x05 // 分
#define RTC_HOUR 0x12 // 時
#define RTC_DAY 0x27 // 日
#define RTC_WEEK 0x00 // 曜日(00:日 〜 06:土)
#define RTC_MON 0x03 // 月
#define RTC_YEAR 0x11 // 西暦
byte date_time[7] = {
RTC_SEC
,RTC_MIN
,RTC_HOUR
,RTC_DAY
,RTC_WEEK
,RTC_MON
,RTC_YEAR
};
/* 定周期タイマ間隔設定単位 0:秒/1:分 */
#define RTC_INTERRUPT_MODE 1
/* 計測間隔(RTC割り込み間隔) */
#define RTC_INTERRUPT_TERM 5
/* 外部割り込みピン */
#define RTC_INTERRUPT_PIN 2
/* 計測許可フラグ */
volatile int flg = LOW;
void setup() {
Serial.begin(9600);
// 日付時刻初期値プロパティにセット
Rtc.initDatetime(date_time);
// RTC日付時刻継続確認なしの日付時刻初期化
Rtc.beginWithoutIsValid();
// RTC定周期割り込み設定
Rtc.syncInterrupt(RTC_INTERRUPT_MODE, RTC_INTERRUPT_TERM);
// 割り込み設定
pinMode(RTC_INTERRUPT_PIN, INPUT);
digitalWrite(RTC_INTERRUPT_PIN, HIGH);
attachInterrupt(0, printDatetime, FALLING);
}
void loop() {
if (flg) {
ReadRTC();
flg = LOW;
}
}
void printDatetime(void)
{
flg = HIGH;
}
void ReadRTC()
{
Rtc.available();
Serial.print(Rtc.years(), HEX);
Serial.print("/");
Serial.print(Rtc.months(), HEX);
Serial.print("/");
Serial.print(Rtc.days(), HEX);
Serial.print(" ");
Serial.print(Rtc.hours(), HEX);
Serial.print(":");
Serial.print(Rtc.minutes(), HEX);
Serial.print(":");
Serial.print(Rtc.seconds(), HEX);
Serial.print(" ");
Serial.println((int)Rtc.weekdays());
}
フィールド設置用スケッチ。 Sketch: Honeypot2_timestamp_sleep(セット用) ▼ /*
* sketch name : Honeypot2_timestamp_sleep
* summary : フィールド設置型 省電力定点計測データロガー
* Sensors : 計測時刻タイムスタンプ & 温度 & 照度 & 気温 & 湿度 & 露点
* releases : 2011/3/27
*/
#include <Wire.h>
#include <Rtc8564AttachInterrupt.h>
#include <Fat16.h>
#include <Fat16util.h>
#include <Sensirion.h>
#include <DateTime.h>
#include <Sleep.h>
////////////////////////////////////////////////////
// init default vals ///////////////////////////////
////////////////////////////////////////////////////
/* Arduino 基準電圧 */
#define ARDUINO_VCC 3.3
/* RTC定周期タイマ割り込み設定 */
// RTCタイマ開始日付時刻
#define RTC_SEC 0x00 // 秒
#define RTC_MIN 0x05 // 分
#define RTC_HOUR 0x12 // 時
#define RTC_DAY 0x27 // 日
#define RTC_WEEK 0x00 // 曜日(00:日 〜 06:土)
#define RTC_MON 0x03 // 月
#define RTC_YEAR 0x11 // 西暦
#define RTC_INTERRUPT_MODE 1 // 定周期タイマ間隔設定単位 0:秒/1:分
#define RTC_INTERRUPT_TERM 5 // 計測間隔(定周期タイマ間隔)
#define RTC_INTERRUPT_PIN 2 // 外部割り込みピン(Digital 2 pin)
byte date_time[7] = {
RTC_SEC,
RTC_MIN,
RTC_HOUR,
RTC_DAY,
RTC_WEEK,
RTC_MON,
RTC_YEAR,
};
boolean init_flg = false; // 電源リセット確認フラグ
time_t make_time;
unsigned long log_timestamp; // 計測日付時刻
#define TIMESTAMP_JST_DEF_SEC 32400 // 日本標準時(JST)への修正秒数(9時間固定)
/* 計測設定 */
#define SMOOTHING_DELAY_SEC 50 // スムージング計測間隔
#define SMOOTHING_BUFFER_LENGTH 5 // スムージング計測回数
// 温度センサ type / 0:LM35DZ 1:LM60BIZ 2:LM61CIZ
unsigned int temperature_pins[] = {0, 1, 2}; // アナログピン0,1,2
unsigned int temperature_sensor_type[] = {0, 0, 0}; // 使用センサタイプ(temperature_pins の値に対応)
float temperature_gain[] = {3.3, 3.3, 3.3}; // Op-amp ゲイン(temperature_pins の値に対応)
// 照度センサ type / 0:S9648-100
unsigned int illumination_pins[] = {3}; // アナログピン3
// 気温・湿度・露点 type / SHT71
#define SHT_CLOCK_PIN 7 // CLOCK(デジタルピン)
#define SHT_DATA_PIN 8 // DATA(デジタルピン)
// センサ別計数
#define CNT_TEMPERATURE_PINS sizeof(temperature_pins) / sizeof(temperature_pins[0]) // 温度センサ
#define CNT_ILLUMINATION_PINS sizeof(illumination_pins) / sizeof(illumination_pins[0]) // 照度センサ
#define CNT_SHT 3 // SHTセンサ
// 計測値格納用
float temperature_data[CNT_TEMPERATURE_PINS]; // 温度データ格納
unsigned int illumination_data[CNT_ILLUMINATION_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
#define SD_LOG_DELIMITER ',' // 記録時のデータ区切り文字 ','(カンマ)
#define LOG_FILE_NAME "log_data.csv" // 記録ファイル名
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.",
};
/* 計測時の確認用点滅LED */
#define CONF_LED_PIN 9
/* ライブラリのインスタンス化 */
SdCard obj_card;
Fat16 obj_file;
Sensirion obj_sht = Sensirion(SHT_DATA_PIN, SHT_CLOCK_PIN);
////////////////////////////////////////////////////
// function ////////////////////////////////////////
////////////////////////////////////////////////////
/*
* func name : calcTemperature
* processing : 温度センサの種類別計算処理
* param : arduino_vcc Arduino基準電圧
* sensor_type 0:LM35DZ / 1:LM60BIZ / 2:LM61CIZ
* analog_val Analog値(0-1023)
* gain Op-amp ゲイン
* return : 計測温度(℃)
*/
float calcTemperature(float arduino_vcc, int sensor_type, int analog_val, float gain)
{
float dc_offset; // DCオフセット電圧(V)
float factor; // 温度係数(V/1℃)
switch (sensor_type) {
case 0: // LM35DZ
dc_offset = 0; // DC offset;0V
factor = 0.01; // +10mV/℃
break;
case 1: // LM60BIZ
dc_offset = 0.424; // DC offset 424mV
factor = 0.00625; // +6.25mV/℃
break;
case 2: // LM61CIZ
dc_offset = 0.6; // DC offset 600mV
factor = 0.01; // +10mV/℃
break;
default:
return 0;
break;
}
return (((arduino_vcc * analog_val) / 1023) - (dc_offset * gain)) / (factor * gain);
}
/*
* func name : calcIllumination
* processing : 照度センサS9648-100計算処理
* param : arduino_vcc Arduino基準電圧
* analog_val Analog値(0-1023)
* return : 照度(lx)
*/
unsigned int calcIllumination(float arduino_vcc, unsigned int analog_val)
{
unsigned int illumination_max_lx = arduino_vcc * 1000; // S9648-100使用時の最大計測照度(lx)
return (analog_val * (illumination_max_lx / 1024));
}
/*
* func name : measuerTemperature
* processing : 温度センサ計測
* param : arduino_vcc Arduino基準電圧
* temperature_pins 使用アナログピン
* temperature_sensor_type 使用センサタイプ
* temperature_gain Op-amp ゲイン
* temperature_data 温度データ格納
* cnt_temperature_pins センサのカウント数
* smoothing_delay_sec スムージング計測間隔
* smoothing_buffer_length スムージング計測回数
*/
void measuerTemperature(
float arduino_vcc,
unsigned int *temperature_pins,
unsigned int *temperature_sensor_type,
float *temperature_gain,
float *temperature_data,
unsigned int cnt_temperature_pins,
unsigned int smoothing_delay_sec,
unsigned int smoothing_buffer_length
){
float sum = 0;
for (int i = 0; i < cnt_temperature_pins; i++) {
// Meanフィルタによるスムージング処理:複数回計測の平均値を算出
for (int j = 0; j < smoothing_buffer_length; j++) {
sum += calcTemperature(
arduino_vcc,
temperature_sensor_type[i],
analogRead(temperature_pins[i]),
temperature_gain[i]
);
delay(smoothing_delay_sec);
}
temperature_data[i] = sum / smoothing_buffer_length;
sum = 0;
}
}
/*
* func name : measuerIllumination
* processing : 照度センサ計測
* param : arduino_vcc Arduino基準電圧
* illumination_pins 使用アナログピン
* illumination_data 温度データ格納
* cnt_illumination_pins センサのカウント数
* smoothing_delay_sec スムージング計測間隔
* smoothing_buffer_length スムージング計測回数
* return :
*/
void measuerIllumination(
float arduino_vcc,
unsigned int *illumination_pins,
unsigned int *illumination_data,
unsigned int cnt_illumination_pins,
unsigned int smoothing_delay_sec,
unsigned int smoothing_buffer_length
){
unsigned long sum = 0;
for (int i = 0; i < cnt_illumination_pins; i++) {
// Meanフィルタによるスムージング処理:複数回計測の平均値を算出
for (int j = 0; j < smoothing_buffer_length; j++) {
sum += calcIllumination(arduino_vcc, analogRead(illumination_pins[i]));
delay(smoothing_delay_sec);
}
illumination_data[i] = sum / smoothing_buffer_length;
sum = 0;
}
}
/*
* func name : dataToSdFileWrite
* processing : SDカード記録
* param : log_timestamp / 日付時刻タイムスタンプ
* temperature_data / 温度データ格納配列
* illumination_data / 照度データ格納配列
* sht_data / SHTデータ格納配列
* cnt_temperature_pins / 温度データ数
* cnt_illumination_pins / 照度データ数
* cnt_sht / SHTデータ数
* return : エラー番号
*/
int dataToSdFileWrite (
unsigned long log_timestamp,
float *temperature_data,
unsigned int *illumination_data,
float *sht_data,
int cnt_temperature_pins,
int cnt_illumination_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_temperature_pins; i++) {
obj_file.print(temperature_data[i]);
obj_file.print(SD_LOG_DELIMITER); // 区切り文字
if (obj_file.writeError) return SD_ERROR_WRITE;
}
// 照度記録
for (int j = 0; j < cnt_illumination_pins; j++) {
obj_file.print(illumination_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 : measuer
* processing : 計測
*/
void measuer(void)
{
// 処理確認用LED ON
digitalWrite(CONF_LED_PIN, HIGH);
delay(100);
digitalWrite(CONF_LED_PIN, LOW);
// タイムスタンプ作成
Rtc.available();
make_time = DateTime.makeTime(
(int)Rtc.seconds(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.minutes(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.hours(Rtc8564AttachInterrupt::Decimal),
(int)Rtc.days(Rtc8564AttachInterrupt::Decimal),
(((int)Rtc.months(Rtc8564AttachInterrupt::Decimal)) - 1),
(int)(2000 + Rtc.years(Rtc8564AttachInterrupt::Decimal))
);
DateTime.sync(make_time);
log_timestamp = DateTime.now() - TIMESTAMP_JST_DEF_SEC;
// 温度計測
measuerTemperature(
ARDUINO_VCC,
temperature_pins,
temperature_sensor_type,
temperature_gain,
temperature_data,
CNT_TEMPERATURE_PINS,
SMOOTHING_DELAY_SEC,
SMOOTHING_BUFFER_LENGTH
);
// 照度計測
measuerIllumination(
ARDUINO_VCC,
illumination_pins,
illumination_data,
CNT_ILLUMINATION_PINS,
SMOOTHING_DELAY_SEC,
SMOOTHING_BUFFER_LENGTH
);
// 気温 & 湿度 & 露点計測
obj_sht.measure(&sht_data[0], &sht_data[1], &sht_data[2]);
// SDメディアに記録
sd_error_no = dataToSdFileWrite (
log_timestamp,
temperature_data,
illumination_data,
sht_data,
CNT_TEMPERATURE_PINS,
CNT_ILLUMINATION_PINS,
CNT_SHT
);
// エラー時はエラーNo.分だけ点滅
if (sd_error_no != SD_ERROR_OK) {
for (int i = 1; i <= sd_error_no; i++) {
digitalWrite(CONF_LED_PIN, HIGH);
delay(100);
digitalWrite(CONF_LED_PIN, LOW);
}
}
// 処理確認用LED OFF
digitalWrite(CONF_LED_PIN, HIGH);
delay(100);
digitalWrite(CONF_LED_PIN, LOW);
}
////////////////////////////////////////////////////
// setup ///////////////////////////////////////////
////////////////////////////////////////////////////
void setup() {
// 処理確認LED
pinMode(CONF_LED_PIN, OUTPUT);
// RTC日付時刻初期化
Rtc.initDatetime(date_time);
// RTC設定初期化(日付時刻の継続確認を含む)
Rtc.begin();
// RTC定周期割り込みタイマ継続確認
if (!Rtc.isInterrupt()) {
// 割り込み周期設定
Rtc.syncInterrupt(RTC_INTERRUPT_MODE, RTC_INTERRUPT_TERM);
}
// RTC割り込み設定
pinMode(RTC_INTERRUPT_PIN, INPUT);
digitalWrite(RTC_INTERRUPT_PIN, HIGH);
}
////////////////////////////////////////////////////
// loop ////////////////////////////////////////////
////////////////////////////////////////////////////
void loop() {
// 初期化(電源OFF=>ON)すぐ後は計測しない
if (init_flg) measuer();
// スリープ (パワーダウン+外部割り込みによる復帰)
SleepClass::powerDownAndWakeupExternalEvent(0);
init_flg = true;
}
|
2011年3月26日土曜日
定点観測データロガー -9 : 観測用シールド RTC Ver.
RTC8564モジュールを組み込んだ定点観測シールド。 | ||
| ||
| ||
| ||
温度センサLM35DZの値をSHTセンサの温度計測値と同じになるように調整する。 以下のテスト用スケッチを使用。 Sketch: Honeypot2_sensor_test(センサーテスト用) ▼ | ||
| ||
校正後に、以下のスケッチを順番にアップする。 フィールドセット用スケッチを書き込む前に、RTC8564モジュールが前のアップスケッチでの設定を引き継がないように 強制的に日付時刻とタイマー設定をセットするスケッチ。 Sketch: RTC8564DatetimeInit(日付時刻設定用) ▼ フィールド設置用スケッチ。 Sketch: Honeypot2_timestamp_sleep(セット用) ▼ |
Arduinoとリアルタイムクロック -3: 定周期タイマー割り込み & スリープ
前回の続きでRTC8564と定周期割り込みライブラリの詳細。 | ||
< 資料 > Download: Rtc8564AttachInterrupt.zipSketch: Rtc8564AttachInterruptSleep ▼ /* * sketch name : Rtc8564AttachInterruptSleep * summary : RTC8564で定周期タイマー割り込み & スリープ * releases : 2011/3/15 */ #include <Wire.h> #include <Rtc8564AttachInterrupt.h> #include <Sleep.h> /* RTCタイマ開始日付時刻 */ #define RTC_SEC 0x00 // 秒 #define RTC_MIN 0x18 // 分 #define RTC_HOUR 0x23 // 時 #define RTC_DAY 0x15 // 日 #define RTC_WEEK 0x02 // 曜日(00:日 〜 06:土) #define RTC_MON 0x03 // 月 #define RTC_YEAR 0x11 // 西暦 byte date_time[7] = { RTC_SEC ,RTC_MIN ,RTC_HOUR ,RTC_DAY ,RTC_WEEK ,RTC_MON ,RTC_YEAR }; /* 電源リセット確認フラグ */ boolean init_flg = false; /* 計測間隔(RTC割り込み間隔) */ #define RTC_INTERRUPT_TERM 10 /* 外部割り込みピン */ #define RTC_INTERRUPT_PIN 2 /* 定周期タイマ間隔設定単位 0:秒/1:分 */ #define RTC_INTERRUPT_MODE 0 void setup() { Serial.begin(9600); // 日付時刻初期化 Rtc.initDatetime(date_time); // RTC開始 Rtc.begin(); // 定周期割り込みタイマ継続確認 if (!Rtc.isInterrupt()) { // 割り込み設定 Rtc.syncInterrupt(RTC_INTERRUPT_MODE, RTC_INTERRUPT_TERM); } // 割り込み設定 pinMode(RTC_INTERRUPT_PIN, INPUT); digitalWrite(RTC_INTERRUPT_PIN, HIGH); } void loop() { // 初期化(電源OFF=>ON)すぐ後は計測しない if (init_flg) ReadRTC(); init_flg = true; // スリープ開始 SleepClass::powerDownAndWakeupExternalEvent(0); } void ReadRTC() { Rtc.available(); Serial.print(0x2000 + Rtc.years(), HEX); Serial.print("/"); Serial.print(Rtc.months(), HEX); Serial.print("/"); Serial.print(Rtc.days(), HEX); Serial.print(" "); Serial.print(Rtc.hours(), HEX); Serial.print(":"); Serial.print(Rtc.minutes(), HEX); Serial.print(":"); Serial.print(Rtc.seconds(), HEX); Serial.print(" "); Serial.println((int)Rtc.weekdays()); Serial.println(); } Rtc8564AttachInterrupt.h ▼ #ifndef Rtc8564AttachInterrupt_h #define Rtc8564AttachInterrupt_h #include <inttypes.h> class Rtc8564AttachInterrupt { private: void init(void); uint8_t _seconds; uint8_t _minutes; uint8_t _hours; uint8_t _days; uint8_t _weekdays; uint8_t _months; uint8_t _years; bool _century; public: enum { BCD = 0, Decimal = 1, }; Rtc8564AttachInterrupt(); void begin(void); void beginWithoutIsValid(void); void initDatetime(uint8_t date_time[]); bool isInitDatetime(void); void sync(uint8_t date_time[],uint8_t size = 7); void syncInterrupt(unsigned int mode, unsigned long term); bool available(void); bool isvalid(void); bool isInterrupt(void); uint8_t seconds(uint8_t format = Rtc8564AttachInterrupt::BCD) const; uint8_t minutes(uint8_t format = Rtc8564AttachInterrupt::BCD) const; uint8_t hours(uint8_t format = Rtc8564AttachInterrupt::BCD) const; uint8_t days(uint8_t format = Rtc8564AttachInterrupt::BCD) const; uint8_t weekdays() const; uint8_t months(uint8_t format = Rtc8564AttachInterrupt::BCD) const; uint8_t years(uint8_t format = Rtc8564AttachInterrupt::BCD) const; bool century() const; }; extern Rtc8564AttachInterrupt Rtc; #endif extern "C" { #include <stdlib.h> #include <string.h> #include <inttypes.h> } #include <WConstants.h> #include <Wire.h> #include "Rtc8564AttachInterrupt.h" #define RTC8564_SLAVE_ADRS (0xA2 >> 1) #define BCD2Decimal(x) (((x>>4)*10)+(x&0xf)) // Constructors //////////////////////////////////////////////////////////////// Rtc8564AttachInterrupt::Rtc8564AttachInterrupt() : _seconds(0), _minutes(0), _hours(0), _days(0), _weekdays(0), _months(0), _years(0), _century(0) { } void Rtc8564AttachInterrupt::init(void) { delay(1000); Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x00); // write reg addr 00 Wire.send(0x20); // 00 Control 1, STOP=1 Wire.send(0x00); // 01 Control 2 Wire.send(0x00); // 02 Seconds Wire.send(0x00); // 03 Minutes Wire.send(0x09); // 04 Hours Wire.send(0x01); // 05 Days Wire.send(0x01); // 06 Weekdays Wire.send(0x01); // 07 Months Wire.send(0x01); // 08 Years Wire.send(0x00); // 09 Minutes Alarm Wire.send(0x00); // 0A Hours Alarm Wire.send(0x00); // 0B Days Alarm Wire.send(0x00); // 0C Weekdays Alarm Wire.send(0x00); // 0D CLKOUT Wire.send(0x00); // 0E Timer control Wire.send(0x00); // 0F Timer Wire.send(0x00); // 00 Control 1, STOP=0 Wire.endTransmission(); } // Public Methods ////////////////////////////////////////////////////////////// void Rtc8564AttachInterrupt::begin(void) { Wire.begin(); if(isvalid() == false) { // RTC初期設定 init(); // 日付時刻プロパティがセットされていたら、その時刻をセット if (isInitDatetime()) { byte date_time[7]; date_time[0] = _seconds; date_time[1] = _minutes; date_time[2] = _hours; date_time[3] = _days; date_time[4] = _weekdays; date_time[5] = _months; date_time[6] = _years; sync(date_time); } } } // RTC日付時刻継続確認なしの日付時刻初期化 void Rtc8564AttachInterrupt::beginWithoutIsValid(void) { Wire.begin(); // RTC初期設定 init(); // 日付時刻プロパティがセットされていたら、その時刻をセット if (isInitDatetime()) { byte date_time[7]; date_time[0] = _seconds; date_time[1] = _minutes; date_time[2] = _hours; date_time[3] = _days; date_time[4] = _weekdays; date_time[5] = _months; date_time[6] = _years; sync(date_time); } } // 日付時刻のプロパティセット void Rtc8564AttachInterrupt::initDatetime(uint8_t date_time[]) { _seconds = (date_time[0]) ? date_time[0] : 0x00; _minutes = (date_time[1]) ? date_time[1] : 0x00; _hours = (date_time[2]) ? date_time[2] : 0x09; _days = (date_time[3]) ? date_time[3] : 0x01; _weekdays = (date_time[4]) ? date_time[4] : 0x01; _months = (date_time[5]) ? date_time[5] : 0x01; _years = (date_time[6]) ? date_time[6] : 0x01; } // 日付時刻プロパティセット確認 bool Rtc8564AttachInterrupt::isInitDatetime(void) { bool flg = false; if ((_seconds & 0x00) != 0x00) flg = true; if ((_minutes & 0x00) != 0x00) flg = true; if ((_hours & 0x09) != 0x09) flg = true; if ((_days & 0x01) != 0x01) flg = true; if ((_weekdays & 0x01) != 0x01) flg = true; if ((_months & 0x01) != 0x01) flg = true; if ((_years & 0x01) != 0x01) flg = true; return flg; } // 日付時刻の初期化 void Rtc8564AttachInterrupt::sync(uint8_t date_time[],uint8_t size) { // 時計機能ストップ Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x00); // Control1 アドレス Wire.send(0x20); // 00 Control 1, STOP=1 Wire.endTransmission(); // 日付時刻設定 Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x02); // Seconds アドレス Wire.send(date_time, size); // 日付時刻セット Wire.endTransmission(); // 時計機能スタート Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x00); // Control1 アドレス Wire.send(0x00); // 00 Control 1, STOP=0 Wire.endTransmission(); } // 定周期割り込みタイマの初期化 // param // mode: 割り込み周期モード選択フラグ / 0: 1秒(1Hz)周期, 1: 1分(60Hz)周期 // term: 割り込み周期(0〜255) mode:0 選択時は単位は「秒」、mode:1 選択時は単位は「分」 void Rtc8564AttachInterrupt::syncInterrupt(unsigned int mode, unsigned long term) { // Control2レジスタ(定周期タイマの繰り返し・割り込み時の信号発信設定) Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x01); // Control2 address Wire.send(0x11); // TI/TP:1(繰り返し割り込みモード),TIE:1(定周期割り込み時、Lレベルの割り込み信号を発生させる) Wire.endTransmission(); // 定周期タイマ設定 byte buf[2]; if (mode == 1) { buf[0] = 0x83; // TE:1(タイマON), TD1:TD0/1;1(カウントダウン周期を60Hz/1分に設定) } else { buf[0] = 0x82; // TE:1(タイマON), TD1:TD0/1;0(カウントダウン周期を1Hz/1秒に設定) } buf[1] = term; // タイマ周期を設定 Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x0E); // Timer Control address Wire.send(buf, 2); Wire.endTransmission(); } bool Rtc8564AttachInterrupt::available(void) { uint8_t buff[7]; Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x02); // write reg addr 02 Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 7); for(int i=0; i<7; i++){ if(Wire.available()){ buff[i] = Wire.receive(); } } _seconds = buff[0] & 0x7f; _minutes = buff[1] & 0x7f; _hours = buff[2] & 0x3f; _days = buff[3] & 0x3f; _weekdays = buff[4] & 0x07; _months = buff[5] & 0x1f; _years = buff[6]; _century = (buff[5] & 0x80) ? 1 : 0; return (buff[0] & 0x80 ? false : true); } bool Rtc8564AttachInterrupt::isvalid(void) { Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x02); // write reg addr 02 Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(Wire.available()){ uint8_t buff = Wire.receive(); return (buff & 0x80 ? false : true); } return false; } // 定周期割り込みタイマの継続確認 bool Rtc8564AttachInterrupt::isInterrupt(void) { Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x01); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(Wire.available()){ return ((Wire.receive() & 0x04) != 0x04 ? false : true); } return false; } uint8_t Rtc8564AttachInterrupt::seconds(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_seconds); return _seconds; } uint8_t Rtc8564AttachInterrupt::minutes(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_minutes); return _minutes; } uint8_t Rtc8564AttachInterrupt::hours(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_hours); return _hours; } uint8_t Rtc8564AttachInterrupt::days(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_days); return _days; } uint8_t Rtc8564AttachInterrupt::weekdays() const { return _weekdays; } uint8_t Rtc8564AttachInterrupt::months(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_months); return _months; } uint8_t Rtc8564AttachInterrupt::years(uint8_t format) const { if(format == Decimal) return BCD2Decimal(_years); return _years; } bool Rtc8564AttachInterrupt::century() const { return _century; } // Preinstantiate Objects ////////////////////////////////////////////////////// Rtc8564AttachInterrupt Rtc = Rtc8564AttachInterrupt(); |
||
< 定周期割り込みのタイムライン > 1. 通常計測時のタイムライン RTC8564を使用して定周期割り込みタイマーを動かした場合、Arduino本体とは異なるタイムラインで 割り込みカウントダウンが実行され続ける。そのため、RTC割り込み信号が発信されてスリープが完了し、 何らかの処理を実行させている間にも設定した周期のカウントダウンは正確に実行される。 |
||
|
||
2. 電池・SDカード交換時のタイムライン 電気二重層コンデンサを使ったバックアップ回路をつなげば、電源をOFFにしてもコンデンサから電力供給が続く限り 定周期割り込みカウントダウンは実行され続ける。 つまりRTC8564のレジスタの日付時刻や設定は生きたままである。 スタンドアロンで組まれたシステムで電源をコンセントなどから供給しない場合の定点観測データロギングでは これが最大の利点となる。 |
||
|
||
< RTC8564のレジスタ設定とライブラリ > 1. 日付時刻の設定 Rtc8564AttachInterrupt.cpp line20 void Rtc8564AttachInterrupt::init(void) { delay(1000); Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x00); // write reg addr 00 Wire.send(0x20); // 00 Control 1, STOP=1 Wire.send(0x00); // 01 Control 2 Wire.send(0x00); // 02 Seconds Wire.send(0x00); // 03 Minutes Wire.send(0x09); // 04 Hours Wire.send(0x01); // 05 Days Wire.send(0x01); // 06 Weekdays Wire.send(0x01); // 07 Months Wire.send(0x01); // 08 Years Wire.send(0x00); // 09 Minutes Alarm Wire.send(0x00); // 0A Hours Alarm Wire.send(0x00); // 0B Days Alarm Wire.send(0x00); // 0C Weekdays Alarm Wire.send(0x00); // 0D CLKOUT Wire.send(0x00); // 0E Timer control Wire.send(0x00); // 0F Timer Wire.send(0x00); // 00 Control 1, STOP=0 Wire.endTransmission(); } とりあえず、全てのレジスタに一通りの設定をする。 レジスタはひとつひとつ設定値を指定してく方法の他に、上記のように設定値の送信キューをまとめて設定する方法がある。 この場合、開始のレジスタアドレスのみ最初に指定する。
|
||
初期設定をする前は 00レジスタのSTOPビットに1、TESTビットに0を指定する。 16進数の『0x20』は、2進数では『00100000』( 2進数、8進数、10進数、16進数相互変換 )。 以下のレジスタへの設定値も同様に指定する。 最後に 00レジスタのSTOPビットに0を指定する。 Sketch では、上記の初期設定の後に、あらかじめ initDatetime()メソッドで指定していた日付時刻プロパティの日付時刻値を 上書きしている。 2. 定周期割り込みタイマー設定 Rtc8564AttachInterrupt.cpp line206 // 定周期割り込みタイマの継続確認 bool Rtc8564AttachInterrupt::isInterrupt(void) { Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x01); Wire.endTransmission(); Wire.requestFrom(RTC8564_SLAVE_ADRS, 1); if(Wire.available()){ return ((Wire.receive() & 0x04) != 0x04 ? false : true); } return false; } 次に、定周期割り込みタイマーの継続確認を行う。 これは、システムの電源を切ってから再びONにした時にバックアップ電源で作動していたRTC8564のタイマー設定が生きていたら 前の設定を継続させるため。 確認は01レジスタのTFビットの値を調べる。 TFビットは定周期割り込みイベントが発生した後では"0" => "1"に変化しているため、 "1"だったら継続してる、"0"だったら設定が生きていないことになる。 isInterrupt()メソッドがFALSEを返した(前の設定が生きていなかった)場合、再びタイマー設定をやりなおす。 syncInterrupt()メソッドで設定する。 Rtc8564AttachInterrupt.cpp line144 // 定周期割り込みタイマの初期化 // param // mode: 割り込み周期モード選択フラグ / 0: 1秒(1Hz)周期, 1: 1分(60Hz)周期 // term: 割り込み周期(0〜255) mode:0 選択時は単位は「秒」、mode:1 選択時は単位は「分」 void Rtc8564AttachInterrupt::syncInterrupt(unsigned int mode, unsigned long term) { // Control2レジスタ(定周期タイマの繰り返し・割り込み時の信号発信設定) Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x01); // Control2 address Wire.send(0x11); // TI/TP:1(繰り返し割り込みモード),TIE:1(定周期割り込み時、Lレベルの割り込み信号を発生させる) Wire.endTransmission(); // 定周期タイマ設定 byte buf[2]; if (mode == 1) { buf[0] = 0x83; // TE:1(タイマON), TD1:TD0/1;1(カウントダウン周期を60Hz/1分に設定) } else { buf[0] = 0x82; // TE:1(タイマON), TD1:TD0/1;0(カウントダウン周期を1Hz/1秒に設定) } buf[1] = term; // タイマ周期を設定 Wire.beginTransmission(RTC8564_SLAVE_ADRS); Wire.send(0x0E); // Timer Control address Wire.send(buf, 2); Wire.endTransmission(); } 定周期タイマー割り込み設定は01レジスタと0Eレジスタで行う。 01レジスタのTI/TPビットは割り込み動作を1回で終わらせるか、それとも繰り返し行うかを設定する。 "1"で繰り返し、"0"で1回限りとなる。 01レジスタのTIEビットは割り込み信号の発生設定である。 "1"で割り込み発生時にINT端子からLレベル信号を出力し、"0"で信号発生を禁止する。 16進数の『0x11』は、2進数では『00010001』( 2進数、8進数、10進数、16進数相互変換 )。 0EレジスタのTEビットは定周期タイマー割り込みのON/OFFを設定する。 "1"で機能ON、"0"で機能OFFとなる。 0EレジスタのTD1・TD0ビットは定周期割り込みタイマーのカウントダウン周期クロックを設定する。 TD1:TD0/"1":"1" でカウントダウン周期を60Hz/1分に設定し、 TD1:TD0/"1":"0" でカウントダウン周期を1Hz/1秒に設定する。 syncInterrupt()メソッドの第一引数は、定周期タイマー割り込み周期の単位を設定するためのフラグである。 "1"ならば0Eレジスタには16進数の『0x83』(2進数で『10000011』)を設定する。 "0"ならば0Eレジスタには16進数の『0x82』(2進数で『10000010』)を設定する。 < Arduino側の設定 > Arduino の外部割り込みピンはデジタル2ピンとデジタル3ピンであり、スケッチではデジタル2ピンを使用している。 RTC8564の定周期カウントダウンが終わって割り込みが発生すると、RTC8564のINTピンからLレベルの信号が発生し、 Arduinoのデジタル2ピンに入力される。 そうするとスリープが終わり、日付時刻がシリアル出力される。 |
登録:
投稿 (Atom)