shigobuのアイコン画像
shigobu 2021年01月31日作成 (2021年12月07日更新) © CC BY 4+
製作品 製作品 閲覧数 4479
shigobu 2021年01月31日作成 (2021年12月07日更新) © CC BY 4+ 製作品 製作品 閲覧数 4479

タイムゾーンを自動設定するGPS時計を作る

タイムゾーンを自動設定するGPS時計を作る

きっかけ

きっかけは、この記事です。
アナログメータ時計を作る|なんでも独り言
これはかっこいい!!
と思い、アナログメータ時計を製作することに。
この記事にもあるように、時刻はGPSモジュールから取得するようにします。
ここで、ふと思います。
「GPS搭載していて現在地の座標が取得できるのだから、タイムゾーンを自動で設定してくれたら便利だろうな」
と・・・

理想の完成形

ほしい機能は以下の通り。

  1. GPSを使用した時刻自動設定
  2. GPSを使用したタイムゾーンの自動設定
  3. 上記機能がオフライン(ネットに接続しない)で使用できる
  4. 電池駆動
  5. 持ち歩きができる

タイムゾーンの判定

タイムゾーン判定方法の調査

緯度経度からタイムゾーンを判定する方法を調べると、各種Webサービスがヒットします。
代表的なもので、

  • Google Maps Time Zone API
  • Bing Maps Time Zone API
  • Azure Maps Time Zone API

などがあります。しかし、これらはすべて使用できません。
なぜなら、インターネットに接続しないと使えないからです。インターネットに接続すると、前項3番が達成できません。

オフライン実装の方法を調べると、いくつか出てきますが、どれもPC用の仕組みです。
今回は、CPUにArduinoUNO(互換機)を使用するので使えません。
自分で実装する必要がありそうです。

タイムゾーン境界データの作成

タイムゾーンは全部で40種類ほどあるらしいですが、国や地域ごとに、どのタイムゾーンを採用しているか異なります。
タイムゾーンの境界データは、「Timezone Boundary Builder(GitHub)」というプロジェクトからjsonファイルでダウンロードできます。ファイルサイズは約130MBです。SDカードに保存するにしても、jsonを解析するためにメモリに読み込む必要があります。しかし、ArduinoUNO(に使われているATmega328P)のメモリは2KBしかないので、jsonの解析はできません。

そこで、独自の境界データを作成することにしました。
Windows用のアプリをC#で作成し、元のjsonファイルを解析します。そして、Arduinoで解析しやすいようにバイナリファイルにします。このバイナリファイルをSDカードに保存し使用します。バイナリファイルにする際に、境界データの間引きも行いました。そのおかげか、ファイルサイズを23MBまで小さくすることができました。
「Timezone Boundary Builder」には、時差情報は含まれていないので、Windowsから時差情報を検索しバイナリファイルに含めました。

独自の境界データ作成アプリのコードはGithubにあります。
TimezonesGeojsonThinOut

タイムゾーン内外判定

タイムゾーンの内外判定アルゴリズムは、こちらのページを参考にしました。

【第2回】点の多角形に対する内外判定|【技業LOG】技術者が紹介するNTTPCのテクノロジー|【公式】NTTPC

SDカード内のバイナリファイルを開き、タイムゾーン境界を表す多角形の頂点を順番に読み込み、GPSから取得した座標と比較していきます。タイムゾーンの範囲内であることがわかった段階で処理を中断し時差を設定します。
時差の設定は、標準ライブラリの「time.h」に含まれているset_zone()関数を使用します。
全体のソースコードは、GitHubにあります。
AnalogMeterClockArduino

ちなみに、タイムゾーンの判定には最大で15分かかりました。

時計機能の制作

時計部分は標準ライブラリの「time.h」を使用しました。
タイマー割り込みを使用し、一秒ごとにsystem_tick()関数を呼び出し、時間を進めます。
一定時間毎にGPSから時刻を取得し、set_system_time()関数で時刻を設定します。

GPSから時刻を取得できるので、RTCは使用しませんでした。

表示部分は、電圧計です。
ArduinoのanalogWrite()関数を使用します。
この部分は、きっかけになった記事と同じです。
表示の更新もsystem_tick()関数を呼ぶタイミングと同じタイミングにしました。

タイマー割り込み関数(わかりやすさ重視のため、実際に使用しているものと異なります)

void timerFire() { //システム時間を一秒すすめる。 system_tick(); //アナログメーターへ出力 time_t nowTime = time(NULL); tm* pTimeStruct; pTimeStruct = localtime(&nowTime); analogWrite(SEC_PIN, map(ptimeStruct->tm_sec, 0, MAX_SECOND, 0, MAX_ANALOG_WRITE_VALUE)); analogWrite(MIN_PIN, map(ptimeStruct->tm_min, 0, MAX_MINIUTE, 0, MAX_ANALOG_WRITE_VALUE)); analogWrite(HOUR_PIN, map(ptimeStruct->tm_hour, 0, MAX_HOUR, 0, MAX_ANALOG_WRITE_VALUE)); }

GPSから時刻を取得しシステム時間を更新する処理(テスト段階のコード)

//1時間おきにシステム時間を更新します。 void setSystemTimeFromGPS() { while (Serial.available() > 0){ char c = Serial.read(); gps.encode(c); if(gps.time.isUpdated()){ //現在時刻の取得 time_t timenow = time(NULL); tm timeStruct; localtime_r(&timenow, &timeStruct); //毎時、0分0秒のときに更新する if(timeStruct.tm_min == 0 && timeStruct.tm_sec == 0){ struct tm rtc_time; rtc_time.tm_sec = gps.time.second(); rtc_time.tm_min = gps.time.minute(); rtc_time.tm_hour = gps.time.hour(); rtc_time.tm_mday = gps.date.day(); rtc_time.tm_wday = 0; rtc_time.tm_mon = gps.date.month() - 1; //tm構造体は0-11の範囲なので1引く rtc_time.tm_year = gps.date.year() - 1900; //tm構造体は1900年起点なので1900を引く rtc_time.tm_yday = 0; rtc_time.tm_isdst = 0; set_system_time( mk_gmtime(&rtc_time) ); } } } }

回路図

全体

全体回路図

電源

電源は、単3乾電池二本を直列につないで3Vにしました。
ATmega328P駆動用に5Vと、SDカード・GPS通信用に3.3vが必要なので、昇圧型DCDCコンバータで昇圧しています。
図中のPWR_FLAGと記載のある記号は、KiCadで回路チェックを行うために必要なものです。無視して構いません。
昇圧コンバータは秋月電子で購入しました。

5V出力昇圧DCDCコンバーター(秋月電子通商)
3.3V出力昇圧DCDCコンバーター(秋月電子通商)

電源回路

SDカード・GPS付近

SDカードやGPSとの通信は3.3Vなので、レベル変換が必要です。
今回、レベル変換ICにFXMA108を使用しました。秋月電子で専用のピッチ変換基盤に実装済のものが購入できます。

8ビット双方向ロジックレベル変換モジュール(秋月電子通商)

SDカードリーダーは、秋月電子のマイクロSDカードスロットDIP化キットを使用しました。
GPSモジュールは、秋月電子の「GNSS(GPS・GLONASS・QZSS)受信機キット 1PPS出力 みちびき3機対応 アンテナセット付キット」を使用しました。

マイクロSDカードスロットDIP化キット(秋月電子通商)
GNSS(GPS・GLONASS・QZSS)受信機キット 1PPS出力 みちびき3機対応 アンテナセット付キット(秋月電子通商)

SDカード・GPS付近

PC接続端子(UART)

今回、基盤に部品を実装後もスケッチの書き換えを行いたかったので、PC接続端子(UART)を用意しました。
シナプスさんの以下のページを参考に設計しました。

Arduinoで作った回路の小型化(Arduino互換機の製作)(9) - しなぷすのハード製作記

UARTは、GPSとの通信にも使用しているので、スイッチで接続先を変更できるようにしました。
電源が電池だとうまく通信できなかったので、電源も一緒に切り替えるようにしました。

ちなみに、この回路図は間違っています。RXDとTXDの接続が逆です。すべて実装してPCに接続して初めて気づきました。
当初、スイッチサイエンスで購入できる「FTDI USBシリアル変換アダプター Rev.2」がそのまま刺さるように設計したつもりでした。仕方ないので、RXDとTXDの接続が逆のケーブルを自作し、つなげることにします。

PC接続用端子付近

スイッチ

スイッチは、4つ用意しました。
時刻同期、タイムゾーン検索、時差設定上、時差設定下、の4つです。
ATmega328Pの内部プルアップ機能を使用するので、スイッチは直接グランドとの間に接続します。
スイッチ

基盤作成

プリント基板を作ってみたかったので、中国の格安プリント基板製造会社に注文しました。
詳細は以下の記事に書きました。

PCBgogoへプリント基板製造を頼んでみた

部品実装と組み立て

基盤に部品実装

今回は、プリント基板を作ったので、部品をつけてはんだ付けするだけです。
すべてスルーホールの部品です。表面実装部品のはんだ付けに自身が無かったからなのですが、SDカードスロットがDIP化基盤をそのまま載せているのが不格好に見えます。せっかくプリント基板を作ったのだから、SDカードスロットを表面実装できると格好良かったのですが。
スイッチとアナログメーターとGPSを接続する部分は、ピンヘッダをつけ、ケーブル経由で接続します。
基盤に部品を実装した写真
改めて見ると、隙間が目立つような気がします。もっと、小さくできたかもしれません。

ケース加工と部品取り付け

ケースは、百均のタッパーです。加工しやすくて最高です。
フタ部分にアナログメーターを取り付けました。
フタはカッターで切れるので、大きな穴を開けるのが簡単です。
スイッチは側面につけました。ドリルで穴を開け、リーマーで穴を広げました。
基盤はボタンの反対側の側面につけました。
電池は底面につけました。基盤をつけた面を下にして、立てて使います。
前面
上面
中身
スイッチは、NKKスイッチズ(日本開閉器工業)の「LP01」シリーズを使用しました。押し心地最高です。千石電商で購入しました。

日本開閉器工業 LP01-15CCKNS1K 押ボタンSW(黒)(千石電商)

動作

動画を撮影したので載せます。

ここに動画が表示されます

連続稼働時間

さて、重要な電池の持ちですが。
正直、使い物にならないくらい短いです。
GPSを繋げたままにすると一晩、時差と時間を設定した後にGPSとの接続を外すとちょうど一日。
消費電力を考えないで、適当に設計した結果がこれです。
電池駆動は諦めたほうが良さそうです。

まとめ

当初、搭載予定だった機能を振り返って見ます。

  1. GPSを使用した時刻自動設定
  2. GPSを使用したタイムゾーンの自動設定
  3. 上記機能がオフライン(ネットに接続しない)で使用できる
  4. 電池駆動
  5. 持ち歩きができる

この内実現できたものにチェックをつけます。

  1. ☑︎GPSを使用した時刻自動設定
  2. ☑︎GPSを使用したタイムゾーンの自動設定
  3. ☑︎上記機能がオフライン(ネットに接続しない)で使用できる
  4. ☐電池駆動
  5. ☑︎持ち歩きができる

実現できなかったのは、4の電池駆動のみです。

今回の目玉機能「タイムゾーン自動設定」が実現できたので、満足です。
海外に行く予定が無いので、海外での動作テストができないのですが。

shigobuのアイコン画像
arduinoを触ってから、マイコンを使った電子工作に目覚めました。
ログインしてコメントを投稿する