Digisparkもどき - 温湿度計付き時計(DHT11 DS1307)を電波時計にしました。aitendoのD606Cを使いました。

私の家は木造です。作業テーブルから高くすると電波をつかみました。
D606Cの動作チェック
出力信号にLEDをつけてチェックします。
最初は、動きませんでした。
- 5Vでは動作しない
- TPは信号を出さない
- Fは反対
- TPをDigisparkもどきにつないでおくと動作しない
- 使った電源が弱かった(PsLab)
など、なかなか難しかったです。
かっぱのひとりごとさんのところが参考になりました。
うまくいくと、TNからの信号でLEDが点滅します。
回路
2kΩと1kΩで分圧しました。レベル変換モジュールでTNをdigisparkもどきに入力しました。回路チェックプログラムでチェックできます。
プログラム
受信処理は長いのでI2C slave化はできずmasterで動かします。
よかひよりさんのを改変して作りました。
電波受信チェックプログラムで電波の強いところを見つけます。スマホのJJYエミュレータでまず試しました。
2分間続けて受信成功するとDS1307(RTCモジュール)に書き込みます。
#include <Wire.h>
#include <DS1307.h>
#include <LCD_I2C.h>
LCD_I2C lcd(0x23, 16, 2);
DS1307 clock;
char curr; //現在の読み込み値
char prev = HIGH; //前回の読み込み値
long c_time = 0; //ループ処理の経過時間
long p_time = 0; //前回のループ処理の経過時間
long s_time = 0; //ループ処理の経過時間の差分
char m01_code = '0'; //コード仕分け値(M、0、1)
int rp; //秒のカウント
int ct = 0; //ループ回数のカウント、積算値
int ct_prev = 0; //前回ループ時の回数
int ct_subt = 0; //ループ回数の差分、正常読み込みであれば毎ループ終了時は「60」
int st_rec = 0; //受信フラグ(初期化、成功、失敗)
char bits[61]; //60間の読み込み値格納、null終端
struct res {
byte min;
byte hour;
byte day;
int year;
byte weekday;
} prev_res, cur_res;
void setup() {
pinMode(1,INPUT);
bits[60] = 0;
lcd.init();
clock.begin();
clock.fillByYMD(2026, 1, 2); //Jan 19,2013
clock.fillByHMS(18, 24, 0); //15:28 30"
clock.fillDayOfWeek(FRI);//Saturday
clock.setTime();//write time to the RTC chip
Wire.begin();
}
void success() {
cur_res = {};
//配列bitsから1bit毎に時刻データ取り出し、重みづけ
//分
if (bits[8] == '1') { cur_res.min += 1; }
if (bits[7] == '1') { cur_res.min += 2; }
if (bits[6] == '1') { cur_res.min += 4; }
if (bits[5] == '1') { cur_res.min += 8; }
if (bits[3] == '1') { cur_res.min += 10; }
if (bits[2] == '1') { cur_res.min += 20; }
if (bits[1] == '1') { cur_res.min += 40; }
//時
if (bits[18] == '1') { cur_res.hour += 1; }
if (bits[17] == '1') { cur_res.hour += 2; }
if (bits[16] == '1') { cur_res.hour += 4; }
if (bits[15] == '1') { cur_res.hour += 8; }
if (bits[13] == '1') { cur_res.hour += 10; }
if (bits[12] == '1') { cur_res.hour += 20; }
//通算日
if (bits[33] == '1') { cur_res.day += 1; }
if (bits[32] == '1') { cur_res.day += 2; }
if (bits[31] == '1') { cur_res.day += 4; }
if (bits[30] == '1') { cur_res.day += 8; }
if (bits[28] == '1') { cur_res.day += 10; }
if (bits[27] == '1') { cur_res.day += 20; }
if (bits[26] == '1') { cur_res.day += 40; }
if (bits[25] == '1') { cur_res.day += 80; }
if (bits[23] == '1') { cur_res.day += 100; }
if (bits[22] == '1') { cur_res.day += 200; }
//西暦(下2桁)
if (bits[48] == '1') { cur_res.year += 1; }
if (bits[47] == '1') { cur_res.year += 2; }
if (bits[46] == '1') { cur_res.year += 4; }
if (bits[45] == '1') { cur_res.year += 8; }
if (bits[44] == '1') { cur_res.year += 10; }
if (bits[43] == '1') { cur_res.year += 20; }
if (bits[42] == '1') { cur_res.year += 40; }
if (bits[41] == '1') { cur_res.year += 80; }
//曜日
if (bits[52] == '1') { cur_res.weekday += 1; }
if (bits[51] == '1') { cur_res.weekday += 2; }
if (bits[50] == '1') { cur_res.weekday += 4; }
boolean fault = false;
if (cur_res.min != prev_res.min || cur_res.hour != prev_res.hour ||
cur_res.day != prev_res.day || cur_res.year != prev_res.year ||
cur_res.weekday != prev_res.weekday) {
fault = true;
}
//1分加算と分、時、日、年、うるう年、曜日への影響反映
//西暦(4桁)
int ayear = ( cur_res.year + 2000 ) ;
int leap_year = ( (1 / (ayear % 4 + 1)) * (1 - 1 / (ayear % 100 + 1)) + (1 / (ayear % 400 + 1)) );
cur_res.min++; //1分加算
if (cur_res.min >= 60) {
cur_res.min=0;
cur_res.hour++;
if (cur_res.hour >= 24) {
cur_res.hour = 0;
cur_res.day++;
int set_year = ( 365 + leap_year );
if (cur_res.day > set_year ) {
cur_res.day = 1;
cur_res.year++;
}
cur_res.weekday++;
if (cur_res.weekday >= 7) {
cur_res.weekday = 0;
}
}
}
//通算日から月、日への変換
int t_day = 0; //通算日の計算用
int mm; //月
int dd; //日
int mdtable[2][12] = { 31,28,31,30,31,30,31,31,30,31,30,31, 31,29,31,30,31,30,31,31,30,31,30,31 }; //通年とうる年の日数
//1月から順に月の日数を引いて月、日を算出
for(int i = 0; i < 12; i++) {
if ((t_day + mdtable[leap_year][i]) >= cur_res.day) {
mm = i + 1;
dd = cur_res.day - t_day;
break;
}
t_day = t_day + mdtable[leap_year][i];
}
if (!fault) {
clock.fillByYMD(ayear, mm, dd);
clock.fillByHMS(cur_res.hour, cur_res.min, rp);
clock.fillDayOfWeek(cur_res.weekday);
clock.setTime();
}
prev_res = cur_res;
}
void loop() {
//D2ポートから受信データの読み込み
curr = digitalRead(1);
//前回読み込み値と現在の読み込み値の比較
if (prev != curr) {
c_time = millis(); //ループ処理の経過時間(ms)
s_time = c_time - p_time;
if (curr != HIGH) {
printTime();
printTemp();
} else {
//コード仕分け(パルス幅:M、P0~P5 200ms < 1:500ms < 0:800ms )
//、P0~P5 (200ms)の判定:マージンを見込んで(300ms)
if (s_time < 280) { //マージンを見込んで300ms
ct++;
//前のコードがMか(Mが2回続いているか)のチェック、初期マーカー位置の判定
//毎時15分と45分のコールサイン(モールス符号)受信時にctとct_prevの値はリセット
if ( m01_code == 'M' ) {
rp = 0; //60回(秒のカウント)のリセット、初期マーカー位置
ct_subt = ct - ct_prev;
byte mp_chk = 0;
if (bits[0] == 'M' && bits[9] == 'M' && bits[19] == 'M' && bits[39] == 'M' && bits[49] == 'M' && bits[59] == 'M') {
mp_chk = 1;
} else{
mp_chk = 2;
}
//60秒間のデータが揃ったか、マーカー位置チェックOKかの判定
if ( ct_subt == 60 && mp_chk == 1) {
st_rec = 1; //受信フラグ:成功
success();
} else {
//配列クリア
for(byte i = 0; i < 60; i++) {
bits[i] = ' ';
}
//受信フラグ:失敗
st_rec = 9;
}
ct_prev = ct;
}
m01_code = 'M';
if (rp < 60)
bits[rp] = 'M'; //rp番目の配列に格納
rp++; //60ループ回のカウントアップ
//1(500ms)の判定:マージンを見込んで(600ms)
} else if (s_time < 590) {
ct++;
// lcd.print( "1" );
if (rp < 60)
bits[rp] = '1'; //rp番目の配列に格納
m01_code = '1';
rp++; //60回ループのカウントアップ
//0(800ms)の判定
} else if (s_time < 1000) {
ct++;
//lcd.print( "0" );
if (rp < 60)
bits[rp] = '0'; //rp番目の配列に格納
m01_code = '0';
rp++; //60回ループのカウントアップ
} else {
lcd.print(s_time);
}
}
}
prev = curr; //現在の読み込み値を保存
p_time = c_time; //現在の経過時間(ms)を保存
}
struct vals {
int result;
int temperature;
int humidity;
};
union uni {
struct vals vals;
byte b[6];
};
void printTime() {
clock.getTime();
lcd.home();
if (clock.hour < 10) lcd.print("0");
lcd.print(clock.hour, DEC);
lcd.print(":");
if (clock.minute < 10) lcd.print("0");
lcd.print(clock.minute, DEC);
lcd.print(":");
if (clock.second < 10) lcd.print("0");
lcd.print(clock.second, DEC);
lcd.setCursor(0, 1);
lcd.print(clock.year, DEC);
lcd.print("/");
if (clock.month < 10) lcd.print("0");
lcd.print(clock.month, DEC);
lcd.print("/");
if (clock.dayOfMonth < 10) lcd.print("0");
lcd.print(clock.dayOfMonth, DEC);
lcd.print(" ");
char dayofweek[][3] = {"Su","Mo","Tu","We","Th","Fr","Sa"} ;
lcd.print(dayofweek[clock.dayOfWeek]);
}
void printTemp() {
uint8_t num_byte = Wire.requestFrom(0x25, 6);
if (num_byte != 6) {
lcd.clear();
lcd.home();
lcd.print("i2c");
lcd.setCursor(0, 1);
lcd.print("error");
return;
}
union uni uni;
uint8_t i = 0;
while (Wire.available() > 0) {
byte b = Wire.read();
uni.b[i++] = b;
}
if (uni.vals.result == 0) {
lcd.setCursor(12, 0);
lcd.print(" ");
lcd.setCursor(12, 0);
lcd.print(uni.vals.temperature);
lcd.write(0xdf);
lcd.print("C");
lcd.setCursor(13, 1);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(uni.vals.humidity);
lcd.print("%");
} else {
lcd.clear();
lcd.home();
lcd.print("ERROR");
lcd.setCursor(0, 1);
lcd.print(uni.vals.result);
}
}
TODO
- 基板を起こす
開発途中で使ったプログラム
回路チェック
レベル変換を通して(P1へ)入力されたTNの信号をLED(P4)に出力します。うまくいくと、D606CにつないだLEDと同じに点滅します。
void setup() {
pinMode(1, INPUT);
pinMode(4, OUTPUT);
}
void loop() {
byte input = digitalRead(1);
digitalWrite(4, input);
}
電波受信チェック
マーカーを受信するとLCDがクリアされ以後0か1が出力されます。1分間のくぎり(マーカーが2連続)で2行目に各マーカーがあるはずのビットの値を表示します。すべて'M'なら日時が表示されます。
#include <LCD_I2C.h>
LCD_I2C lcd(0x23, 16, 2);
char curr; //現在の読み込み値
char prev = HIGH; //前回の読み込み値
long c_time; //ループ処理の経過時間
long p_time; //前回のループ処理の経過時間
long s_time; //ループ処理の経過時間の差分
char m01_code; //コード仕分け値(M、0、1)
int rp; //秒のカウント
int ct; //ループ回数のカウント、積算値
int ct_prev; //前回ループ時の回数
int ct_subt; //ループ回数の差分、正常読み込みであれば毎ループ終了時は「60」
int st_rec; //受信フラグ(初期化、成功、失敗)
char d2bits[61]; //60間の読み込み値格納、null終端
void setup() {
pinMode(4, OUTPUT);
pinMode(1,INPUT); //D2ピンをINPUTモードに設定
c_time = 0;
p_time = 0;
s_time = 0;
m01_code = '0';
ct = 0;
ct_prev = 0;
ct_subt = 0;
st_rec = 0; //受信フラグ:初期化
lcd.init();
}
void success() {
//配列d2bitsから1bit毎に時刻データ取り出し、重みづけ
//分
int min = 0;
if(d2bits[8]=='1'){ min = min + 1; }
if(d2bits[7]=='1'){ min = min + 2; }
if(d2bits[6]=='1'){ min = min + 4; }
if(d2bits[5]=='1'){ min = min + 8; }
if(d2bits[3]=='1'){ min = min + 10; }
if(d2bits[2]=='1'){ min = min + 20; }
if(d2bits[1]=='1'){ min = min + 40; }
//時
int hour = 0;
if(d2bits[18]=='1'){ hour = hour + 1; }
if(d2bits[17]=='1'){ hour = hour + 2; }
if(d2bits[16]=='1'){ hour = hour + 4; }
if(d2bits[15]=='1'){ hour = hour + 8; }
if(d2bits[13]=='1'){ hour = hour + 10; }
if(d2bits[12]=='1'){ hour = hour + 20; }
//通算日
int day = 0;
if(d2bits[33]=='1'){ day = day + 1; }
if(d2bits[32]=='1'){ day = day + 2; }
if(d2bits[31]=='1'){ day = day + 4; }
if(d2bits[30]=='1'){ day = day + 8; }
if(d2bits[28]=='1'){ day = day + 10; }
if(d2bits[27]=='1'){ day = day + 20; }
if(d2bits[26]=='1'){ day = day + 40; }
if(d2bits[25]=='1'){ day = day + 80; }
if(d2bits[23]=='1'){ day = day + 100; }
if(d2bits[22]=='1'){ day = day + 200; }
//西暦(下2桁)
int year = 0;
if(d2bits[48]=='1'){ year = year + 1; }
if(d2bits[47]=='1'){ year = year + 2; }
if(d2bits[46]=='1'){ year = year + 4; }
if(d2bits[45]=='1'){ year = year + 8; }
if(d2bits[44]=='1'){ year = year + 10; }
if(d2bits[43]=='1'){ year = year + 20; }
if(d2bits[42]=='1'){ year = year + 40; }
if(d2bits[41]=='1'){ year = year + 80; }
//西暦(4桁)
int ayear = ( year + 2000 ) ;
//曜日
int youbi = 0;
if(d2bits[52]=='1'){ youbi = youbi + 1; }
if(d2bits[51]=='1'){ youbi = youbi + 2; }
if(d2bits[50]=='1'){ youbi = youbi + 4; }
//1分加算と分、時、日、年、うるう年、曜日への影響反映
min++; //1分加算
if(min>=60){ min=0; hour++;
if(hour>=24){ hour=0; day++;
int leap_year = ( (1 / (ayear % 4 + 1)) * (1 - 1 / (ayear % 100 + 1)) + (1 / (ayear % 400 + 1)) ); //うるう年判定
int set_year = ( 365 + leap_year );
if(day > set_year ){ day=1; year++; }
youbi++;
if( youbi>=7){ youbi=0; }
}
}
//通算日から月、日への変換
int t_day = 0; //通算日の計算用
int mm; //月
int dd; //日
int leap_year = ( (1 / (ayear % 4 + 1)) * (1 - 1 / (ayear % 100 + 1)) + (1 / (ayear % 400 + 1)) ); //うるう年判定
int mdtable[2][12] = { 31,28,31,30,31,30,31,31,30,31,30,31, 31,29,31,30,31,30,31,31,30,31,30,31 }; //通年とうる年の日数
//1月から順に月の日数を引いて月、日を算出
for(int i=0;i<12;i++){
if((t_day + mdtable[leap_year][i])>=day){
mm = i+1;
dd = day - t_day;
break;
}
t_day = t_day + mdtable[leap_year][i];
}
//曜日を3桁英字への変換
char dayofweek[7][4] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"} ;
char f_youbi[4];
strcpy(f_youbi, dayofweek[youbi]);
//日時をシリアルモニタに表示して確認
lcd.home();
lcd.print(ayear);
lcd.print( "/" );
lcd.print(mm);
lcd.print( "/" );
lcd.print(dd);
lcd.print( "(" );
lcd.print(f_youbi);
lcd.print( ")" );
lcd.setCursor(0, 1);
lcd.print(hour);
lcd.print( ":" );
lcd.print(min);
lcd.print( ":" );
lcd.print(rp); //秒
}
void printMarker() {
lcd.setCursor(0, 1);
lcd.print( d2bits[0] );
lcd.print( "-" );
lcd.print( d2bits[9] );
lcd.print( "-" );
lcd.print( d2bits[19] );
lcd.print( "-" );
lcd.print( d2bits[29] );
lcd.print( "-" );
lcd.print( d2bits[39] );
lcd.print( "-" );
lcd.print( d2bits[49] );
lcd.print( "-" );
lcd.print( d2bits[59] );
lcd.home();
}
boolean toggle = false;
void loop() {
curr = digitalRead(1);
//前回読み込み値と現在の読み込み値の比較
if( prev != curr ) {
c_time = millis(); //ループ処理の経過時間(ms)
s_time = c_time - p_time;
if( curr == HIGH ) {
//前回処理と時間差がある場合のみ実行
//コード仕分け(パルス幅:M、P0~P5 200ms < 1:500ms < 0:800ms )
//、P0~P5 (200ms)の判定:マージンを見込んで(300ms)
if(s_time < 280 ) { //マージンを見込んで300ms
ct++;
toggle = !toggle;
digitalWrite(4, toggle);
//lcd.print(s_time);
//lcd.print(" ");
//lcd.print("M");
lcd.home();
lcd.clear();
//前のコードがMか(Mが2回続いているか)のチェック、初期マーカー位置の判定
//毎時15分と45分のコールサイン(モールス符号)受信時にctとct_prevの値はリセット
if( m01_code == 'M' ) {
rp = 0; //60回(秒のカウント)のリセット、初期マーカー位置
ct_subt = ct - ct_prev;
//配列(タイムコード)内のマーカー位置の確認
int mp_chk = 0;
if(d2bits[0]=='M' && d2bits[9]=='M' && d2bits[19]=='M' && d2bits[39]=='M' && d2bits[49]=='M' && d2bits[59]=='M'){
mp_chk = 1;
} else{
mp_chk = 2;
}
if( mp_chk == 1 ){ lcd.print( "OK" );}
//if( mp_chk == 2 ){ lcd.print( "NG" );}
printMarker();
//60秒間のデータが揃ったか、マーカー位置チェックOKかの判定
if( ct_subt == 60 && mp_chk == 1) {
st_rec = 1; //受信フラグ:成功
success();
} else {
//配列クリア
for(int i=0;i<60;i++){
d2bits[i] = ' ';
}
//受信フラグ:失敗
st_rec = 9;
}
ct_prev = ct;
}
m01_code = 'M';
if (rp < 60)
d2bits[rp] = 'M'; //rp番目の配列に格納
rp++; //60ループ回のカウントアップ
//1(500ms)の判定:マージンを見込んで(600ms)
} else if(s_time < 590) {
ct++;
lcd.print( "1" );
if (rp < 60)
d2bits[rp] = '1'; //rp番目の配列に格納
m01_code = '1';
rp++; //60回ループのカウントアップ
//0(800ms)の判定
//} else {
} else if (s_time < 1000) {
ct++;
lcd.print( "0" );
if (rp < 60)
d2bits[rp] = '0'; //rp番目の配列に格納
m01_code = '0';
rp++; //60回ループのカウントアップ
} else {
lcd.print(" ");
lcd.print(s_time);
lcd.print(" ");
}
}
}
prev = curr; //現在の読み込み値を保存
p_time = c_time; //現在の経過時間(ms)を保存
}