// 温度・湿度測定+RTC+LCD( RTC設定 )+SD記録 #include #include #include #define HDC_Adr 0x40 // HDC1050アドレス #define LCD_Adr 0x3E // LCDのアドレス #define RTC_Adr 0x51 // RTCのアドレス #define SDLed_Pin 7 // SDアクセスLEDピン #define StBtn_Pin 3 // 時刻セットボタンピン #define MdBtn_Pin 8 // モード選択ボタンピン #define THBtn_Pin 9 // 時計・温度切替ボタンピン const int chipSelect = 10; // SDI用ピン設定 // **** MOSI - pin 11 // **** MISO - pin 12 // **** CLK - pin 13 char s_buf[16]= ""; // 文字列データ用バッファ //********* BCD <-> Binary ( 1 byte用 ) ******* unsigned int bcd2bin(char dt){ return (( dt >> 4 ) * 10 ) + ( dt & 0x0F); } unsigned int bin2bcd(unsigned num){ num = num % 100; // numは0~99 return (( num / 10 ) << 4 )| ( num % 10 ); } //***** リアルタイムクロック ******* #define R_CMND_1 0x00 #define R_CMND_2 0x01 #define R_SECONDS 0x02 #define R_MINUTES 0x03 #define R_HOURS 0x04 #define R_DAYS 0x05 #define R_WEEKS 0x06 #define R_MONTHS 0x07 #define R_YEARS 0x08 #define R_CLKOUT 0x0D #define R_TCNTRL 0x0E #define R_TIMER 0x0F volatile int dsp_md = 0; // 表示モード volatile int rtc_int_flg = false; volatile int seconds = 50; // RTC初期設定値 volatile int minutes = 59; volatile int hours = 23; volatile int days = 31; volatile int weeks = 6; // 6:土曜日 volatile int months = 12; volatile int years = 16; char *wk_dt[] ={ "sun","mon","tue","wed","thu","fri","sat" }; volatile int RedCnt = 0; // 赤ボタン待ちタイマー用 void rtc_write(byte adr, byte data){ Wire.beginTransmission(RTC_Adr); Wire.write(adr); // アドレス指定 Wire.write(data); // データ書込み Wire.endTransmission(); delay(1); } int rtc_read(byte adr){ Wire.beginTransmission(RTC_Adr); Wire.write(adr); // アドレス指定 Wire.endTransmission(); Wire.requestFrom(RTC_Adr,1); // データ1バイト送信要求 return Wire.read(); // データ取得 } void rtc_write_data(){ rtc_write(R_SECONDS,bin2bcd(seconds)); // 秒 rtc_write(R_MINUTES,bin2bcd(minutes)); // 分 rtc_write(R_HOURS, bin2bcd(hours)); // 時 rtc_write(R_DAYS, bin2bcd(days)); // 日 rtc_write(R_WEEKS, bin2bcd(weeks)); // 曜日 rtc_write(R_MONTHS, bin2bcd(months)); // 月 rtc_write(R_YEARS, bin2bcd(years)); // 年 } void rtc_read_data(void){ seconds = bcd2bin( rtc_read(R_SECONDS) & 0x7F ); // 秒 minutes = bcd2bin( rtc_read(R_MINUTES) & 0x7F ); // 分 hours = bcd2bin( rtc_read(R_HOURS) & 0x3F ); // 時 days = bcd2bin( rtc_read(R_DAYS) & 0x3F ); // 日 weeks = bcd2bin( rtc_read(R_WEEKS) & 0x07 ); // 曜日 months = bcd2bin( rtc_read(R_MONTHS) & 0x1F ); // 月 years = bcd2bin( rtc_read(R_YEARS) ); // 年 } void rtc_begin(void){ // クロック初期値設定 Serial.println("RTC_begin"); rtc_write(R_CMND_1, 0x00); // 時計機能ON rtc_write(R_CMND_2, 0x00); // タイマーOFF weeks = subZeller( 2000 + years, months, days ); //Serial.println( wk_dt[weeks]); rtc_write_data(); rtc_write(R_CLKOUT, 0x83); // Clock出力 = 1sec rtc_write(R_TCNTRL, 0x81); // タイマーON、CLK=64Hz rtc_write(R_TIMER, 64 ); // タイマー = 1sec rtc_write(R_CMND_2, 0x11); // 定周期割り込みON delay(1); } int subZeller(int y, int m, int d ){ if(m <3) { y--; m += 12; } return (y + y/4 - y/100 + y/400 + (13*m + 8 )/5 + d )%7; } //***** 液晶表示 ********(使用電源によりどちらか選択) //#define BOOST 0x04 // 3.3V用 AQM0802 #define BOOST 0x00 // 5.0V用 AQM0802 int contrast = 0x20; // コントラスト初期値 void lcd_cmd(byte cmd){ Wire.beginTransmission(LCD_Adr); Wire.write(0x00); // コマンド指定 Wire.write(cmd); // コマンド出力 Wire.endTransmission(); // ストップ if((cmd == 0x01)||(cmd == 0x02)) // ClearかHomeか delay(2); // 2msec待ち else delayMicroseconds(30); // 30μsec待ち } void lcd_data(byte data){ Wire.beginTransmission(LCD_Adr); // スタート Wire.write(0x40); // 表示データ指定 Wire.write(data); // 表示データ出力 Wire.endTransmission(); // ストップ delayMicroseconds(30); // 遅延 } void lcd_setCursor(byte clm,byte row){ if(row==0) lcd_cmd(0x80+clm); // カーソル位置 if(row==1) lcd_cmd(0xc0+clm); } void lcd_begin(void){ // LCDの初期化 Serial.println("LCD_begin"); delay(10); lcd_cmd(0x38); // 8ビット2行 標準モード lcd_cmd(0x39); // 8ビット2行 拡張モード lcd_cmd(0x14); // OSC 183Hz BIAS 1/5 lcd_cmd((byte)(0x70 + (contrast & 0x0F))); lcd_cmd((byte)(0x58 + BOOST + (contrast >> 4))); lcd_cmd((byte)0x6B); // Follwer for 3.3V delay(1); lcd_cmd((byte)0x38); // 標準モードへ lcd_cmd((byte)0x0C); // 表示開始 lcd_cmd((byte)0x01); // 画面消去 delay(1); } void lcd_clear(void){ lcd_cmd(0x01); // 全消去コマンド出力 delay(2); } void lcd_print(char* ptr){ // 文字列表示関数 while(*ptr != 0) // 文字取り出し lcd_data(*ptr++); // 文字表示 } void lcd_print(int num){ // 整数表示関数 sprintf(s_buf,"%d",num); lcd_print(s_buf); } //***** HDC1050 ******** #define HDC_TEMP 0 // HDCレジスタアドレス #define HDC_HUME 1 #define HDC_CONFIG 2 void hdc_write(byte adr, word data){ Wire.beginTransmission(HDC_Adr); Wire.write(adr); // アドレス指定 Wire.write((byte)(data >> 8)); // データ書込み(MSD8) Wire.write((byte)(data % 0xff)); // データ書込み(LSD8) Wire.endTransmission(); delay(1); } unsigned int hdc_read(byte adr){ word rd_dat; Wire.beginTransmission(HDC_Adr); Wire.write(adr); // アドレス指定 Wire.endTransmission(); Wire.requestFrom(HDC_Adr,2); // データ1バイト送信要求 while ( Wire.available()== 0 ); rd_dat = Wire.read() << 8 ; // データ取得(MSD8) rd_dat = rd_dat | Wire.read(); // データ取得(LSD8) return rd_dat; } void hdc_trigger(byte adr){ Wire.beginTransmission(HDC_Adr); Wire.write(adr); // アドレス指定 Wire.endTransmission(); delay(1); } unsigned int hdc_read_data(){ word rd_dat; Wire.requestFrom(HDC_Adr,2); // データ1バイト送信要求 rd_dat = Wire.read() << 8 ; // データ取得(MSD8) rd_dat = rd_dat | Wire.read(); // データ取得(LSD8) return rd_dat; } void hdc_begin(){ //****** 条件により選択 Serial.println("HDC_begin"); //hdc_write(HDC_CONFIG, 0x0000); // TH単独、14bit、ヒートoff //hdc_write(HDC_CONFIG, 0x1000); // TH同時、14bit、ヒートoff hdc_write(HDC_CONFIG, 0x2000); // TH単独、14bit、ヒートon //hdc_write(HDC_CONFIG, 0x3000); // TH同時、14bit、ヒートon delay(1); sprintf(s_buf,"HDC device: %x", hdc_read(0xff) ); Serial.println(s_buf); } //********** interrupt routin ***** void rtc_intr(){ // 割込処理ルーチン rtc_int_flg = true; // 割込フラグをセット // Serial.println("interrupt!"); // 周期割込確認用 } //********** Time Set Mode ****** void TimSet(){ lcd_setCursor(0,0); // カーソルを0行目の先頭に lcd_print("Time Set"); // LCDに年月日を表示 sprintf( s_buf,"Yer:20%02d",years ); lcd_setCursor(0,1); // カーソルを1行目の先頭に lcd_print(s_buf); // LCDに年を表示 while( digitalRead(StBtn_Pin) == LOW ); // highまで待つ RedCnt = 0; for ( int i=0; i<5; i++ ){ sprintf(s_buf,"set %d", i ); Serial.println(s_buf); do{ if ( digitalRead(MdBtn_Pin) == LOW ){ switch(i){ case 0: years++; if (years>99) years=0; break; case 1: months++; if (months>12) months=1; break; case 2: days++; if (days>31) days=1; break; case 3: hours++; if (hours>23) hours=0; break; case 4: minutes++; if (minutes>59) minutes=0; break; } while( digitalRead(MdBtn_Pin) == LOW ); // highまで待つ } if ( digitalRead(THBtn_Pin) == LOW ){ switch(i){ case 0: years--; if (years<0) years=99; break; case 1: months--; if (months<1) months=12; break; case 2: days--; if (days<1) days=31; break; case 3: hours--; if (hours<0) hours=23; break; case 4: minutes--; if (minutes<0) minutes=59; break; } while( digitalRead(THBtn_Pin) == LOW ); // highまで待つ } switch(i){ case 0: sprintf( s_buf,"Yer:20%02d",years ); break; case 1: sprintf( s_buf,"Month:%02d",months ); break; case 2: sprintf( s_buf,"Day: %02d",days ); break; case 3: sprintf( s_buf,"Hour: %02d",hours ); break; case 4: sprintf( s_buf,"Minut:%02d",minutes ); break; } lcd_setCursor(0,1); // カーソルを1行目の先頭に lcd_print(s_buf); // LCDに変更データを表示 delay(100); // チャタリング対策(不完全) } while( digitalRead(StBtn_Pin) == HIGH ); } seconds = 0; weeks = subZeller( 2000 + years, months, days ); rtc_write_data(); while( digitalRead(StBtn_Pin) == LOW ); // highまで待つ } //********* setup *********** void setup(){ Serial.begin(9600); // シリアルモニタON pinMode(MdBtn_Pin, INPUT_PULLUP); // モード選択ボタン pinMode(THBtn_Pin, INPUT_PULLUP); // 時間・温度選択ボタン pinMode(StBtn_Pin, INPUT_PULLUP); // 時間セットボタン pinMode(SDLed_Pin, OUTPUT); pinMode(SS_PIN, OUTPUT); // これは必須 // digitalWrite(SS_PIN, HIGH); // なくても? Wire.begin(); // I2C通信開始 delay(1); if ( digitalRead(StBtn_Pin) == LOW ) rtc_begin(); // 起動時に赤ボタンが押されていたら、RTCを初期化 lcd_begin(); // LCDの初期化 hdc_begin(); attachInterrupt( 0, rtc_intr, FALLING ); //割込設定 if (!SD.begin( chipSelect )) { Serial.println("Card failed, or not present"); // 特にSDエラー処理をしていないので注意 }else{ Serial.println("SD Card Ready"); } Serial.println("-- setup end"); } //********* main loop *********** unsigned int temp; // 温度データ取得用 unsigned int hum; // 湿度データ取得用 float f_temp; // 浮動小数点温度データ float f_hum; // 浮動小数点湿度データ char buf[10] = ""; // 一時文字変換用 boolean sd_act = false; // SD書き込みモード File dataFile; // SD用FILE構造体 void loop(){ if ( digitalRead(StBtn_Pin) == LOW ){ if ( RedCnt > 100 ) TimSet(); // 長押しで時刻設定モードへ else RedCnt++; // 設定時までカウンタを+1 } if ( digitalRead(MdBtn_Pin) == LOW ){ dsp_md = dsp_md ^ 0x01; // ビット0を反転 if (dsp_md == 3){ sd_act = true; dataFile = SD.open( "datalog.txt",FILE_WRITE ); digitalWrite(SDLed_Pin, HIGH); // SDに書込準備完了 }else if (dsp_md == 2){ sd_act = false; dataFile.close(); // SDに書込終了 digitalWrite(SDLed_Pin, LOW); } while( digitalRead(MdBtn_Pin) == LOW ); // highまで待つ } if ( digitalRead(THBtn_Pin) == LOW ){ dsp_md = dsp_md ^ 0x02; // ビット1を反転 while( digitalRead(THBtn_Pin) == LOW ); // highまで待つ } hdc_trigger( HDC_TEMP ); // 温度測定開始 delay(10); temp = hdc_read_data(); //temp = 0xffff; // 表示テスト用 f_temp = (float)temp / 65536.0 * 165.0 - 40.0; hdc_trigger( HDC_HUME ); // 湿度測定開始 delay(10); hum = hdc_read_data(); f_hum = (float)hum / 65536.0 * 100.0; if ( rtc_int_flg == true ){ // 割込フラグが立っているか? rtc_read_data(); // RTCからデータを読む switch ( dsp_md ){ case 0: case 1: sprintf( s_buf,"%02d/%02d/%02d,%3s,",years,months,days,wk_dt[weeks] ); break; case 2: case 3: if (( f_temp > 99.9 )||( f_temp < 0.0 )){ dtostrf( (double)f_temp,5,1,buf); // "%f"の代わり sprintf( s_buf,"T%5s\337C,",buf ); }else{ dtostrf( (double)f_temp,4,1,buf); // "%f"の代わり sprintf( s_buf,"T:%4s\337C,",buf ); } break; } lcd_setCursor(0,0); // カーソルを0行目の先頭に lcd_print(s_buf); // LCDに年月日を表示 Serial.print( s_buf ); // 表示テスト・確認用 switch ( dsp_md ){ case 0: sprintf( s_buf,"%02d:%02d:%02d",hours,minutes,seconds ); break; case 1: sprintf( s_buf,"%02d:%02d%3s",hours,minutes,wk_dt[weeks] ); break; case 2: case 3: dtostrf( (double)f_hum,4,1,buf); // "%f"の代わり sprintf( s_buf,"H:%4s %%",buf ); break; } lcd_setCursor(0,1); // カーソルを1行目の先頭に lcd_print(s_buf); // LCDに時分秒を表示 Serial.println( s_buf ); // 表示テスト・確認用 if (sd_act==true){ // SDにデータを書き込み sprintf( s_buf,"20%02d/%02d/%02d, %3s,",years,months,days,wk_dt[weeks] ); dataFile.print(s_buf); sprintf( s_buf," %02d:%02d:%02d,",hours,minutes,seconds ); dataFile.print(s_buf); dtostrf( (double)f_temp,5,1,buf); // "%f"の代わり sprintf( s_buf," %5s,",buf ); dataFile.print(s_buf); dtostrf( (double)f_hum,4,1,buf); // "%f"の代わり sprintf( s_buf," %4s",buf ); dataFile.println(s_buf); } rtc_int_flg = false; // 割込フラグ消去 } delay(10); }