Raspberry Piで電波時計の時刻合わせ
最終更新日2018-02-13 01:30:14
カテゴリ:Diary 閲覧数:14814
あじぇんだ
部屋にある電波時計が電波拾ってくれずに時間がずれる。
電子工作好きだけど,なんだかんだRaspberry PiのGPIOって使ったこと無いなぁ。
ということでRaspberry Piで電波時計用の電波(JJY信号)を出力してみる。
環境
- Raspberry Pi (1A)
このRaspberry Piは諸々の事情によりインターネットに常時接続されていない。
なので,インターネットの接続がある時間(起動直後と15:30前後)にntpdateコマンドで時刻同期をすることにする。
JJY信号は常時発信する。
fake-hwclockの削除
RaspberryPiにはRTCが無い代わりに擬似的なハードウェアクロックを実現するfake-hwclockが存在する。
しかしこいつのせいで時間が進んでいた時の修正(時間の巻き戻し)ができない。
設定ファイルいじったりしても良いのだが,NTPベースで時刻合わせをするので,こいつはまるごと削除してしまおう。
$ sudo apt purge fake-hwclock
時刻同期の設定
まずはntpdateコマンドをインストール。
$ sudo apt install ntpdate
起動時の実行はrc.local,定期実行はcronを使う
$ sudo nano /etc/rc.local /usr/sbin/ntpdate ntp.jst.mfeed.ad.jp # この一行を“exit 0”の直前に追記 $ sudo crontab -e 30 19 * * * /usr/sbin/ntpdate ntp.jst.mfeed.ad.jp # 追記
dateコマンドで時間を適当にずらし,ntpdateがちゃんと動いているかを確認する。
JJY信号を発信するアンテナの作成
40KHzはラズパイの外で作る。ちょうど赤外線の工作で38kHz作るようにNE555買ってあったから,その在庫をそのまま使う。抵抗値だけ変えて40kHzになるように調整する。
電源はラズパイの5Vを使用,GPIOからは変調波(で言葉あってるよね)を出力,トランジスタのベースに繋いでスイッチング。
あとついでに,GPIOからの信号が分かるようにLEDもつける。
こんなかんじ。ブレッドボードで動作確認ができたら回路図(右)も作成。
回路図ができたらさくっとハンダ付け。あと,アンテナの作成もする。
はんだ付け終わってから思ったけど,アンテナって抵抗より上にした方が良かったのではないだろうか。というかこれじゃアンテナずっと0Vじゃん。でもブレッドボードでは動いたし,電波は電流が生み出すから良いのか?(←電波の仕組みよくわかってない人)
JJY信号発信用プログラムの作成・サービス化
jjy.pyの作成
作成っていっても,Raspberry Pi で作る Wi-Fi 式電波時計用リピータ こちらのページに合ったプログラムをそのまま拝借しました。
テストのときとかパスが通ってたほうが良いから,/usr/local/bin/jjy.pyに直接スクリプトを書き込む。
jjy.pyのサービス化
/etc/init.d/ 以下にサービス名(jjyとする)と同じ名前のファイルを作成して,実行権限を与える。
jjyの中身は後述の通り。詳細は省くが,最初のコメントアウトの中と,daemon,appdirを編集,あとは38行目のstart-stop-daemonの引数を編集(python用に作ったので,phpとかbashとかのスクリプトの場合は要編集)。
$ sudo nano /etc/init.d/jjy # 内容は後述 $ sudo chmod 755 /etc/init.d/jjy # 実行権限を付与 $ sudo systemctl daemon-reload # daemonのリロード
jjyが出来たら,あとはsystemctlなりserviceなりで起動できる。
$ sudo service jjy start # 起動 $ sudo service jjy status # 確認 Active: active (running) # 動いていることを確認
再起動して自動起動が確認できればOK。
ラズパイにアンテナとかを接続
ラズパイに接続,光った!オシロで40kHzでてることも確認できたし,JJY信号もちゃんと出てる!
アンテナも接続して,電波時計をアンテナ近くに移動,どうだ!
完璧!
後ろに映ってるのなにかって?iMacだよ。
え?時間なら電波時計じゃなくてiMacで確認すればいいって?
ばっか,あじぇんだに書いだたろ,工作は手段じゃなくて目的なんだよ!
楽しかったから何も問題ない!
電波法についての確認
今回の工作は電波を出すものだから,電波法に抵触してないかの確認。
※電波については完全な素人なので,言い回しや内容がおかしかったりするかも。間違いがあれば指摘をお願いします。
手元にある電波時計のマニュアルを見てみると,どうやらこの時計は1200km離れていても電波を受信出来るらしい。
今回の実験に使った40kHzを出してる福島局から1200km離れたところというと,ちょうど鹿児島県あたりがそれに相当する。
NICTの長波標準電波の電界強度予測値のページの情報から,鹿児島に届く40kHzの電波の電界強度は平均0.71mV/m。
つまりこの電波時計は0.7mV/mぐらいの電界強度の電波なら受信できる。
総務省の微弱無線局の規定のページから,40kHzの電波は3m離れた場所における電界強度が0.5mV/m未満なら免許は不要。
実際に電波時計動かしながら受信できる最長距離を測ってみたら,30cmぐらいが限界で,60cmぐらい離すと完全に反応しない。
ということで,アンテナの周囲30cmぐらいは0.71mV/mの強度があるけど,それより離れるとどんどん減衰していくようだ。
なら,3mもはなれれば流石に0.5mV/mは下回ってるだろう。
参考文献
付録:スクリプトとか
/usr/local/bin/jjy.py
#!/usr/bin/python # coding: utf-8 # # JJY タイムコードを出力するスクリプト. # GPIO に H を出力する事で 40kHz パルスが停止する回路を想定 # しています. import RPi.GPIO as GPIO import datetime import time # JJY 時刻符号を出力する端子 GPIP_PORT = 4 def set_pin(mode): GPIO.output(GPIP_PORT, mode) def send_bit(bit): if bit == -1: # マーカ set_pin(1) time.sleep(0.2) set_pin(0) time.sleep(0.799) elif bit == 0: # 0 set_pin(1) time.sleep(0.799) set_pin(0) time.sleep(0.2) elif bit == 1: # 1 set_pin(1) time.sleep(0.499) set_pin(0) time.sleep(0.5) def send_bcd(num, count, parity=0): for i in range(count): bit = (num >> ((count-1) - i)) & 0x1 send_bit(bit) parity ^= bit return parity def send_datetime(now): now = datetime.datetime.now() minute = now.minute hour = now.hour day = now.toordinal() - datetime.date(now.year, 1, 1).toordinal() + 1 year = now.year % 100 wday = now.isoweekday() % 7 sec = now.second usec = now.microsecond min_parity = 0 hour_parity = 0 ############################################################ send_bit(-1) # 10分位のBCD min_parity = send_bcd(minute/10, 3, min_parity) send_bit(0) # 1分位のBCD min_parity = send_bcd(minute%10, 4, min_parity) send_bit(-1) ############################################################ send_bit(0) send_bit(0) # 10時位のBCD hour_parity = send_bcd(hour/10, 2, hour_parity) send_bit(0) # 1時位のBCD hour_parity = send_bcd(hour%10, 4, hour_parity) send_bit(-1) ############################################################ send_bit(0) send_bit(0) # 累計日数100日位のBCD send_bcd(day/100, 2) send_bit(0) # 累計日数10日位のBCD send_bcd((day%100) / 10, 4) send_bit(-1) ############################################################ # 累計日数1日位のBCD send_bcd(day%10, 4) send_bit(0) send_bit(0) # パリティ send_bit(hour_parity) send_bit(min_parity) send_bit(0) send_bit(-1) ############################################################ send_bit(0) # 西暦年10年位のBCD send_bcd((year%100)/10, 4) # 西暦年1年位のBCD send_bcd(year%10, 4) send_bit(-1) ############################################################ # 曜日のBCD send_bcd(wday, 3) send_bit(0) send_bit(0) send_bit(0) send_bit(0) send_bit(0) send_bit(0) # マーカ set_pin(1) time.sleep(0.2) set_pin(0) # 0.8 秒残しておき,次回呼び出しタイミングの調整代とする GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(GPIP_PORT, GPIO.OUT) set_pin(0) while True: now = datetime.datetime.now() minute = now.minute sec = now.second usec = now.microsecond # 0 秒になるまで待つ time.sleep(60 - (sec + usec/1000000.0)) send_datetime(now + datetime.timedelta(minutes=1))
/etc/init.d/jjy
#!/bin/sh ### BEGIN INIT INFO # Provides: jjy # Required-Start: $network $local_fs $remote_fs # Required-Stop: $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 1 # Short-Description: Start jjy daemon ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin daemon="jjy" appdir=/usr/local/bin/ pidfile=/var/run/${daemon}.pid logfile=/var/log/${daemon}.log . /lib/lsb/init-functions RETVAL=0 # System configuration unset TMPDIR start() { echo -n "Starting ${daemon} services: " echo -n "waiting " # wait for boot of mysql, only needed raspberry pi sleep 1m echo -n "restart " # cheking already run? if start-stop-daemon --stop --quiet --signal 0 --pidfile $pidfile then pid=`cat ${pidfile}` log_warning_msg "daemon is already running? (pid=${pid})" exit 1 fi start-stop-daemon --start --background --exec "/usr/bin/python" --pidfile "$pidfile" --make-pidfile -- "$appdir$daemon.py" status=$? log_end_msg $status } stop() { log_daemon_msg -n $"Stopping ${daemon} service: " # cheking already run? if start-stop-daemon --stop --signal 0 --pidfile $pidfile ; then start-stop-daemon --stop --pidfile $pidfile status=$? rm -f $pidfile log_end_msg $status else log_warning_msg "daemon not running? (check ${pidfile})." fi } case "$1" in start) start ;; stop) stop ;; restart) stop start ;; *) echo $"Usage: ${daemon} {start|stop|restart}: " RETVAL=2 esac exit $RETVAL
Comments
All Comments
Post Comment