Easy JJY Station の製作

はじめに

標準電波(JJY※)は、NICTが決定した標準周波数と日本標準時を日本全国に供給するための電波です。
福島長波局(おおたかどや山:40 kHz)
九州長波局(はがね山:60 kHz)
ただ、近畿地方はどちらの基地局の狭間にあり、両方の電波を使える一方、どちらからも遠いということになります。

標準電波の到達範囲

自宅に電波時計を設置しても、設置場所によっては基準電波を受信することができず、長時間の間で時計の時間がずれてしまうこともあります。せっかくの電波時計の仕組みが有効に機能しないこともあります。せっかく電波時計のがあるり悔しいです。そこで、インターネット上のNTPサーバーから正確な時間を取得して電波時計の形式で送信する装置。いわばローカルJJY基地局みたいなものを作ってみたいと思います。
世の中には共立電子などから「ケイシーズ Wi-Fi式電波時計用リピータKEISEEDS P18-NTPWR」というものが売られているのですが、ちょっと高いかもです。
このWEBページにの写真も掲載されていたけど、マイコンはたぶんESP8266のような感じ。
ちょうど昔買ったESP8266があったので、在庫処分的な意味合いも兼ねて、このマイコンを使います。

“Easy JJY Station”の主な仕様

項目内容備考
キャリア周波数60kHz40kHzは現在未サポート
電源USB供給方式マイクロUSBタイプ
使用プロセッサESP8266・コア:Xtensa_LX6(32bit) ・クロック:160MHz ・ROM:2MB ・プログラムメモリー[B]:2MB ・GPIO:11 ・UART/USART:1 ・I2C:1 ・SPI:1 ・オシレーター:内蔵 ・無線機能:Wi-Fi ・無線機能詳細:Wi-Fi(802.11b/g/n)2.4GHz
送信電力??実測、バーアンテナ端で20vPPの搬送波。調整はオプション
搬送波発振方式タイマーIC555による方形波発振定数を変更することで60kHzにも対応可能
操作方法(UI)UARTにより簡易的なシェルコマンドTeraterm等UART:115200bps
オフセット時間設定-11h~11h(オプション)
NTPサーバーへの接続接続先ホスト(AP)のSsidとpasswordを設定するリトライ、接続断発見機能あり
パラメータ保存あり不揮発メモリに保存

電波時計のデータ構造

電波時計のデータフォーマットは「標準電波のタイムコード」に詳細が記載されているので、これを参考にします。
秒の信号は、パルス信号の立ち上がりとし、立ち上がり時の55%値(10%値と100%値の中央)が日本標準時の1秒信号に同期します。立ち上がり後、100%値を継続するパルス幅の長さによって、その秒での情報を符号化しています。
パルス幅の定義は次の通りです。
パルス幅 0.8s ±5ms 2進の0
パルス幅 0.5s ±5ms 2進の1
パルス幅 0.2s ±5ms ポジションマーカー (M, P0~P5)

構成ブロック図

機能ブロック図

装置全体の回路図

過去に作った「ESP8266の評価ボード」+「ベースボード」という感じにします。

ベースボードと60kHz搬送波発振および変調(ON/OFF)、アンテナ回路部

H/W製作

かなり前に作ってあったESP8266の評価ボード
ベースボードのパターン設計をサクッと行い、切削で基板で完成!
3Dプリンタでケースを作成。
ケースの蓋は片方は爪でひっかけてネジ一本で固定するようにした。
OLEDに日付、時間を表示したが小さすぎて実用性はないかもしれない・・・

バーアンテナ部(共振アンテナ部)

今回の無線キャリアは40kHz,60kHzの一般的にLF(長波帯)に属し、波長が5kmととても長い。
なので、一般的には短縮コイル形状のループアンテナあるいはバーアンテナが一般的に用いられる。バーアンテナはAMラヂヲのアンテナでよく使用されるが、周波数が中波帯(530kHz~)であり、約10倍違う。したがって効率よくアンテナにするためにバーアンテナコアを容量的にも10倍程度になるように巻きなおす。

ベースにしたバーアンテナは中波用長さ14cmのAR-140というもの。なかなかこのサイズは入手困難なので、ヤフオクで入手。デフォルトのインダクタンスは640uHくらい。
ベースにしたバーアンテナは中波用長さ14cmのAR-140というもの。なかなかこのサイズはこの巻線を外してΦ0.2mmのポリウレタン線を巻きなおす。この巻線材は太いほうが銅損が減るのでQが高くなるが、大きく重くなり、手元に偶然Φ0.2mmのポリウレタン線があったのでそれを活用した。巻きやすくするため、ボビンを3Dプリンタで作成した。

DE-5000のLCRメータで測定して100kHzのリファレンス周波数で約5mH Q=120くらいものができた!コイルはこんなもんでいいかな。

搬送波発信部

数十kHzの周波数はクリスタルでは低すぎるので、NE555を用いることにする。ここで使用するコンデンサは本来温度特性と漏れ電流が小さいフィルムコンデンサを用いたほうがいいのだが、適当な容量が在庫になかったのでセラミックコンデンサ(B特)を用いることにする。

今回は決め打ちで60kHzのキャリア周波数にする。

共振アンテナ

40kHzの場合 1/(5mH(2*3.14*40k)^2)=3169pF 60kHzの場合 1/(5mH(2*3.14*60k)^2)=1407pF

まずはコンデンサの容量として一般的な1500pFをつけてみる。
AnalogDiscovery2のネットワーク測定を用いて共振特性を測定する。

トリマコンデンサで調整しなくてもいい感じになった

発振回路制御および出力波形

キャリア発振とON/OFF制御
時間拡大

S/W編 JJY 時間データ生成とキャリア変調

セットアップルーチン

// --- Setup ---
void setup() {
  Serial.begin(SERIAL_BAUDRATE);            // シリアルポートの初期化
  pinMode(jjyOutPin, OUTPUT);               // GPIOピンの初期化
  digitalWrite(jjyOutPin, LOW);
  EEPROM.begin(256);
  EEPROM.get(0, Ntcwave);

  u8g2.begin();                             // OLED表示器用の文字フォント初期化
  u8g2.setFont(u8g2_font_ncenB14_tr);
 
  //WiFi.begin(ssid, password);               // Wifiネットワークの接続
  //WiFi.begin(Ntcwave.ssid, Ntcwave.pass);   // Wifiネットワークの接続

  // 500msごとにステートマシンを進める
  wifiTicker.attach_ms(500, checkWiFiState);
  Serial.println("Starting WiFi state machine...");
 
  timeClient.begin();                       // NTPサーバーから時刻情報取得
  timeClient.update();

  time_t now = timeClient.getEpochTime();
  struct tm* t = localtime(&now);
  encodeJJY(t, jjyBits);                    // JJYフォーマットデータ生成
  printJJYBits(t,jjyBits);                  // 送信ビット列表示

  usbif_init();
  Serial.println("ESP8266 Serial Console Ready");
}

JJY データエンコーダー

現在時刻から電波時計用JJY送信フォーマットデータを作成する。

jjy encode関数

// ------------ JJY Bit Encoder ----------------------
void encodeJJY(struct tm* t, int* bits) {
  memset(bits, 0, sizeof(int) * 60);
  const int markers[] = {0, 9, 19, 29, 39, 49, 59};
  for (int i = 0; i < sizeof(markers)/sizeof(int); i++) {
    bits[markers[i]] = JJY_MARKER;
  }

  int minbcd = bin2bcd(t->tm_min);
  bits[8] = minbcd & 1;
  bits[7] = (minbcd >> 1) & 1;
  bits[6] = (minbcd >> 2) & 1;
  bits[5] = (minbcd >> 3) & 1;
  bits[3] = (minbcd >> 4) & 1;
  bits[2] = (minbcd >> 5) & 1;
  bits[1] = (minbcd >> 6) & 1;

  int hourbcd = bin2bcd(t->tm_hour);
  bits[18] = hourbcd & 1;
  bits[17] = (hourbcd >> 1) & 1;
  bits[16] = (hourbcd >> 2) & 1;
  bits[15] = (hourbcd >> 3) & 1;
  bits[13] = (hourbcd >> 4) & 1;
  bits[12] = (hourbcd >> 5) & 1;

  int daysOfYearbcd = bin2bcd(dayOfYear(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday));

  bits[38] = 0;
  bits[37] = bits[1] ^ bits[2] ^ bits[3] ^  bits[5] ^ bits[6] ^ bits[7] ^ bits[8]; //分のParity
  bits[36] = bits[12] ^ bits[13] ^ bits[15] ^ bits[16] ^ bits[17] ^ bits[18];      //時間のParity
  bits[35] = 0;
  bits[34] = 0;
  bits[33] = (daysOfYearbcd ) & 1;
  bits[32] = (daysOfYearbcd >> 1) & 1;
  bits[31] = (daysOfYearbcd >> 2) & 1;
  bits[30] = (daysOfYearbcd >> 3) & 1;
  bits[28] = (daysOfYearbcd >> 4) & 1;
  bits[27] = (daysOfYearbcd >> 5) & 1;
  bits[26] = (daysOfYearbcd >> 6) & 1;
  bits[25] = (daysOfYearbcd >> 7) & 1;
  bits[23] = (daysOfYearbcd >> 8) & 1;
  bits[22] = (daysOfYearbcd >> 9) & 1;
  bits[21] = 0;
  bits[20] = 0;

  int yearbsc = bin2bcd(t->tm_year + 1900);

  bits[48] = (yearbsc ) & 1;;
  bits[47] = (yearbsc >> 1) & 1;
  bits[46] = (yearbsc >> 2) & 1;
  bits[45] = (yearbsc >> 3) & 1;
  bits[44] = (yearbsc >> 4) & 1;
  bits[43] = (yearbsc >> 5) & 1;
  bits[42] = (yearbsc >> 6) & 1;
  bits[41] = (yearbsc >> 7) & 1;

  int wday = t->tm_wday;
  bits[52] = wday & 1;
  bits[51] = (wday >> 1) & 1;
  bits[50] = (wday >> 2) & 1;
}

搬送波60kHzをjjy encodeされたビット列(60bit)で変調生成する部分

// --- JJY Signal Output State Machine ---
void updateJJY() {
  unsigned long now = millis();
  static int bit = 0; // スコープの外で宣言
  time_t nowT;
  struct tm* t;

  switch (state) {
    case SETUP:
        nowT = timeClient.getEpochTime();
        t = localtime(&nowT);
        if(t->tm_sec == 0){
          encodeJJY(t, jjyBits);
          printJJYBits(t,jjyBits);
          currentBit = 0;
          state = IDLE;
        }
        break;
    case IDLE:
      if (currentBit >= 60) {
        timeClient.update();
        nowT = timeClient.getEpochTime();
        t = localtime(&nowT);
        if(t->tm_sec != 0) {
          state = SETUP;
          break;
        }
        encodeJJY(t, jjyBits);
        printJJYBits(t,jjyBits);
        currentBit = 0;
      }
      bit = jjyBits[currentBit];
      switch (bit) {
        case JJY_0: bitOnDuration = 800; break;
        case JJY_1: bitOnDuration = 500; break;
        case JJY_MARKER: bitOnDuration = 200; break;
      }
      digitalWrite(jjyOutPin, HIGH);
      bitStartTime = now;
      state = ON;
      break;

    case ON:
      if (now - bitStartTime >= bitOnDuration) {
        digitalWrite(jjyOutPin, LOW);
        state = OFF;
      }
      break;

    case OFF:
      if (now - bitStartTime >= 1000) {
        currentBit++;
        state = IDLE;
      }
      break;
  }
}

Wifi接続管理

接続先アクセスポイントの設定されたSSID、パスワードをもとにWifi接続を行う。
接続中何らかのトラブルで接続断したとしても、再度APが接続可能となれば自動復帰する。ただし、一度切断すると、NTPサーバーから正しい時間を取得し、JJYのデータ更新境界(0秒)で同期するまで2分程度時間を要する。

装置設定、ユーザーインターフェースと動作パラメータ管理

シェルコマンドラインのように設定できるような感じにした。 実はこういった地味な作りこみの方が手間がかかっている。
以下のように個別にWifiアクセスポイントへの接続設定を行い、その設定内容を装置に記憶する。
> set
<<Set Parameter List>>
ssid:Pixel_5704
pass:******
freq(0:40kHz/1:60kHz)(NA):1
power(NA):4
toffset(h)(NA):0
>

実証動作確認

①電波時計の電池抜いて処理状態に戻す。
②電波時計の電池を入れると、長針、短針、秒針がすべて「00時00分」に揃えられる。
③「Easy JJY Station」の電源をONにする。
④しばらくするとOLDLED表示器に日付、時刻とJJYカウント(0~60)表示される。
⑤電波時計は5分くらいしたら電波時計は「Easy JJY Station」がOLE表示器に示す時刻になってる。動作良好!

電波時計を合わせているところ 電波時計をリセットして5分くらい放置していると自動的に時刻を合わせてくれた。 一連の動作には特に支障はなかった。

消費電力

グラフより 電流MAX:約300mA 、平均:約129mA 電波を出している期間、電流も平均20mA程度の差がある程度。
時々320usec程度300mAの電流が流れる。 おそらくこれはESP8266内部のWifiの動作(電波を出している)ものではないかと思われる。

まとめ

今回原理試作という位置づけで製作おこなったため、機能としては最低必要なものを実装するにとどまった。オプション機能として

考えられる追加機能変更手法
①キャリア周波数の選択(40kHz/60kHz)搬送波を作っているMC555のコンデンサの切替機能追加 バーアンテナ共振定数コンデンサの切替機能追加
②送信電力の変更供給電圧の可変機能/切替機能追加
③時間オフセット設定ソフトフェアによる変数パラメータの追加

①②はH/Wの変更を伴う内容であり、今後は本件の反響具合により進めたいと考えています。
その際は専用基板を起こすかも・・ではまたその機会があったらお会いしましょう!さよオナラ・・・・👋

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です