デコードテスト(Arduinoで電波時計を作る その3) | かっぱのひとりごと

かっぱのひとりごと

日々の思いをつぶやいています。

----- 2014/ 7/20 追記 -----
aitendo の現行機種「電波時計モジュール(40/60KHz) [D606C]」は TP から信号が出ていない可能性があります。
完成版ではその辺のことを記事にしていますので、表示が更新されないときはこちらで確認してください。

また、動作状態の動画のリクエストがありましたので追加しました。
左から、パルス幅、信号の種類( 0:LOW、1:HIGH、2:markar、3:Error)、エラーの回数、一番右が信号を受信した回数です。


---------------------------

受信した電波からビット列を取り出し、
分、時、日、月、年、曜日を復号化します。
(パリティとうるう秒は無視しています。)

標準電波 JJY についてはこちらを参照してください。
denpa_test2
(2014/05/01 回路図修正しました。電波時計モジュールのピンの順番を逆にしました。LCD の接続を間違えていたのを修正しました。(DB0~3 → DB4~7))

Arduino の回路図は「ラジオペンチ」さんからいただきました。

Arduino(うちは Japanino )とキャラクタ液晶、電波時計モジュールをつないだだけ。
キャラクタ液晶は「ラジオペンチ」さんのダイオード一本で済ます方法でコントラストを決めています。
電波時計モジュールは D13 につなぐと、受信したパルスで LED が光ってわかりやすいです。

<わかったこと>
・電子レンジが動作すると結構ノイズがのる。
・モバイルバッテリーの電源を入れると、パルスが High になりっぱなしになる。
 (ちょうど 40KHz くらいでスイッチングしているのかな?)
・アンテナの向きは受信にあまり関係がない。(うちから送信所まで 100Km 弱は近距離?)

なので、パルスだけに頼って時刻を刻み続けることはできないし、デコード一回の結果だけでは信頼できないことがわかった。

ソースはこちら。「decode_test.zip」
「電波時計」を選んで「decode_test.zip」をクリック。
上の矢印をクリックするとダウンロードできます

<スケッチ>


#include <LiquidCrystal.h>

#define Input_Pin 13

// 接続ピンをして指定してライブラリを初期化
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

// 変数定義
int year;
byte month;
byte week_no;
byte day;
byte hour;
byte minutes;
byte sec;
const byte month_day[]={0,31,28,31,30,31,30,31,31,30,31,30,31};

unsigned int c_year;
unsigned int c_week;
unsigned int c_day;
unsigned int c_hour;
unsigned int c_minutes;
unsigned long pulse;
long pulse_width;

byte code;
byte marker;
byte i;
byte err = 0;

byte get_code(void){
byte ret_code = 3; // エラー

while (digitalRead(Input_Pin) == 0){
}
// High になったら計測開始
lcd.setCursor(14, 1);
if (sec < 10)
lcd.print(" ");
lcd.print(sec++);

pulse = millis() >> 1;
while (digitalRead(Input_Pin) == 1){
}

// パルス幅を計算( mills() は 49.7日でオーバーフローして0に戻る)
pulse_width = (millis() >> 1) - pulse;
if (pulse_width < 0){ // pulse がオーバーフローした場合
pulse_width = (pulse_width + 0x7FFFFFFF) << 1;
}else{
pulse_width = pulse_width << 1;
}
//ret_code = 3; // エラー
if ((pulse_width > 150) && (pulse_width < 250)) // 0.2sec
ret_code = 2; // marker
if ((pulse_width > 450) && (pulse_width < 550)) // 0.5sec
ret_code = 1; // High
if ((pulse_width > 750) && (pulse_width < 850)) // 0.8sec
ret_code = 0; // Low
lcd.setCursor(0, 1);
lcd.print(pulse_width);
lcd.setCursor(4, 1);
lcd.print(ret_code);
if (ret_code ==3){
lcd.print(" ");
lcd.print(++err);
}
return(ret_code);
}

void setup() {
//仮設定
year = 0;
month = 0;
week_no = 0;
day = 0;
hour = 0;
minutes = 0;

// 行と列を設定
lcd.begin(16, 2);
// 信号入力ピン設定
pinMode(Input_Pin, INPUT);
// 信号が LOW になるまで待つ
while (digitalRead(Input_Pin) == 1){
}
marker = 0;
// marker が2回続いたらスタート
do{
code = get_code();
if (code == 2){
marker++;
}else{
marker = 0;
}
} while (marker < 2);
}

void loop() {
sec = 0;
// 分の算出
c_minutes = 0;
for (i = 0; i < 8; i++){
c_minutes = c_minutes << 1;
c_minutes += get_code();
}
minutes = (c_minutes >> 5) * 10 + (c_minutes & B1111) ;
code = get_code(); // ポジションマーカー

// 時の算出
c_hour = 0;
for (i = 0; i < 9; i++){
c_hour = c_hour << 1;
c_hour += get_code();
}
hour = (c_hour >> 5) * 10 + (c_hour & B1111) ;
code = get_code(); // ポジションマーカー

// 日の算出
c_day = 0;
for (i = 0; i < 9; i++){
c_day = c_day << 1;
c_day += get_code();
}
day = (c_day >> 5) * 100 + (c_day & B1111) * 10 ;
code = get_code(); // ポジションマーカー

c_day = 0;
for (i = 0; i < 4; i++){
c_day = c_day << 1;
c_day += get_code();
}
day += c_day;

month = 0;
do{
day -= month_day[month];
month ++;
} while( day > month_day[month]);

do{
code = get_code();
} while (code != 2); // ポジションマーカー待ち

// 15 分 45 分は呼び出し符号(モールス信号)
if (minutes != 15 && minutes != 45){
// 年の算出
c_year = 0;
for (i = 0; i < 9; i++){
c_year = c_year << 1;
c_year += get_code();
}
year = (c_year >> 4) * 10 + (c_year & B1111) + 2000;
code = get_code(); // ポジションマーカー

// 曜日の算出
week_no = 0;
for (i = 0; i < 3; i++){
week_no = week_no << 1;
week_no += get_code();
}
}else{ // ポジションマーカー待ち
// while (get_code() != 2);
delay(10500);
}

// marker がきたら 59秒
while (get_code() != 2);
// sec = 59;

// 表示させる位置の指定 一行目頭
lcd.setCursor(0, 0);
lcd.print(year);
lcd.print("/");
if (month < 10)
lcd.print(" ");
lcd.print(month);
lcd.print("/");
if (day < 10)
lcd.print(" ");
lcd.print(day);
lcd.print("/");
lcd.print("(");
lcd.print(week_no);
lcd.print(") ");

// 表示させる位置の指定 二行目頭
lcd.setCursor(8, 1);
if (hour < 10)
lcd.print(" ");
lcd.print(hour);
lcd.print(":");
if (minutes < 10)
lcd.print("0");
lcd.print(minutes);

// marker がきたら 再スタート
code = get_code();

}