ESP32 の GPIO から 疑似的に 日本標準電波 JJY を出してみる実験、第1弾

ESP32 ( ESP-WROOM-32 )

こんばんは。

私は3つくらい電波時計を持っていますが、部屋の中まで電波が届きません。
また、仕事上で正確な時刻を刻む腕時計が必要なこともあって、腕時計も電波時計にしたのですが、仕事場の中まで電波が届きません。

仕事場と家の中も壁掛け時計がありますが、電波が届かず結局のところ手動で時刻を合わせている次第です。

そこで、数年前 Arduino で電子工作を始めた頃、以下の記事を見つけました。

Arduino で電波時計を合わせよう

この記事には衝撃を受けて、夢中でプログラムを入力した覚えがあります。
そして、Ethernet Shield をつけて実際に動かして、電波時計が合わせられた時は感動しましたね。

でも、当時はそのプログラムの意味や、福島もおおたかどや山から送信されてくる 日本標準電波 ( JJY )の仕組みはサッパリわかりませんでした。

最近になって、趣味で電子工作や数々のプログラミングをこなしているうちに、マイコンの知識や電子部品の知識が少しずつでも身についてきて、やっとこのプログラムや 標準電波の意味を理解できるようになってきました。

私は工事担任者や陸上特殊無線の免許は持っていて、ある程度の電波の知識はあるつもりですが、随分昔に取ったので殆ど忘れていました。
アマチュア無線の免許は持っていません。
ですから、基本的に無線工学は素人です。

でも、実際に電子工作で電波を出すことにトライしてみると、最近はとてもよく理解できるようになってきました。

そこで、今回は ESP-WROOM-32 ( ESP32 )を使って、GPIO から 標準電波 JJY を出してみることに挑戦してみようと思います。
ただし、電波は30㎝ も飛びませんし、ノイズの多いパルス波(方形波)なので、あくまで疑似的な標準電波モドキです。

まずは、実験の第1弾として、ESP-WROOM-32 開発ボードを使って、GPIO から 40kHz のパルスを出すことをやってみます。

※ここで紹介する方法はあくまで個人的趣味の実験です。
基本的には無線工学についてはド素人ですので、あくまで自己満足報告です。
この実験で起きるいかなるトラブルも当方では責任を負いません。

また、この記事を書いた当初は LC共振回路というものを良く知りませんでした。
最新記事では、LC共振回路の理解を深めて、LC共振回路を使って、ガッツリ電波時計を合わせる実験もして見ましたので、合わせて以下の記事もご覧ください。
(2019/01/17)
https://www.mgo-tec.com/blog-entry-esp32-lc-resonance-radio-watch.html

更にその後、以下の時事で PWM ( LEDC )ライブラリを使うことによって、確実に安定した 40kHz クロックを出力する方法が解りました。
LEDC を使えば、電波時計が合わせやすくなると思いますので、参照してみて下さい。
(2019/04/04)
https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html

スポンサーリンク

日本標準電波 JJY の出し方について

※以下は個人的見解です。誤っていたらコメント等でご連絡ください。
まずは、標準電波の出し方ですが、以下のサイトで公開されています。

標準電波の出し方について

そして、その電波のタイムコード例は以下のページにあります。

タイムコード(例)

これを見ても昔はサッパリ分かりませんでしたが、今は私なりにかなり解明できました。
もし、間違っていたらコメント欄等でお知らせください。

要するに、簡単に言うとこんな感じです。

福島のおおたかどや山標準電波送信所の場合、搬送波が 40kHz で、デジタル伝送のビットを送信する場合、振幅を 10:1 の比率にします
そして、3種類のビットデータを1秒毎に送ってトータル1分のデータを送ればよい

3種類のビットデータはこんな感じです。

●マーカー —- 200ms(High) 800ms(Low)
●2進数のゼロ  —-  800ms(High)  200ms(Low)
●2進数の1  —-  500ms(High)  500ms(Low)

よくよく読み込んでみると、ESP32 で意外とできそうな気がしてきました。

では、それぞれについて私なりに考えてみます。

搬送波について

搬送波というのは、基準となる周波数の電波ですね。
その電波の周波数を変えずに、その電波を大きくしたり小さくしたりして、データを送ることができます。
(AM変調)

電波は、電線の電圧を高速でHigh-Low切り替えるだけで飛び出してきます。
切り替える周波数が高いほど飛びやすくなります。
そして、電圧や電流が大きいほど電波が強くなります。
この電波の強さは電波法で厳しく規定されているので、むやみに強くすることができません。

電波時計の標準電波 JJY の搬送波は40kHz (福島おおたかどや山標準電波送信所の場合)です。
40kHz ということは、サイン波形の1波長が 25μs (マイクロセコンド)です。

基本的に、マイコンの GPIO ピンを HIGH – LOW を高速で繰り返して、電線を引き回せば電波が出てきます。
しかし、それは以下のような方形波(矩形波、パルス波)となり、これを電波に出してしまうと様々な障害が出てきます。
方形波とは、簡単に言うと基準サイン波の奇数倍周波数のサイン波を合成したものと同じなのです。

例えば、40kHz パルス波(方形波)の場合、40kHz, 120kHz, 200kHz, 280kHz,・・・という奇数倍のサイン波(高調波)の合成波です。
ですから、それぞれの周波数を受信する機器にとっては混信やノイズにさらされていることになります。
つまり、搬送波は純粋なサイン波でなければならないということです。

ただ、いくら純粋なサイン波でも、電波を強く出してしまうと電波法違反となって捕まってしまいます。
使える強い電波は既に免許を持っている無線局に割り当てられていて、その免許の無い者はその電波を出すことができません。
ただし、電波法によれば、微弱電波ならば周波数を自由に設定して使用することができます。
その件については、総務省の以下のページを熟読してください。

微弱無線局の規定

このページに書いてあるように、距離が3m のところで電界強度が 500μV/m 以下であれば自由に使うことができます。

電子工作で、3.3V~5V で銅線を引き回して、マイコンの GPIO を 40kHzで High-Low 切り替える程度ならば、電波は50㎝ も飛びませんので、電波法違反にはならないと言えます。
実際のところ、ブレッドボードのジャンパワイヤーなどは、それ以上の周波数のパルス波(方形波)を I2C や SPI 通信で使っているので、問題ないと言えるでしょう。

ただし、電圧や電流を増幅したり、高効率のアンテナを作って微弱無線局の規定を超えたら電波法違反となって捕まってしまいます。
その点は十分ご注意ください。

ESP-WROOM-32 ( ESP32 )のプログラムを組んで、GPIO からDAC ( DAコンバーター)ピンを使って純粋なサイン波を出そうと試みましたが、40kHz という高周波は無理のようです。
ですから今回は高調波ノイズを含みますが、GPIO の High-Low切り替えのみの方形波(パルス波)を搬送波として作ってみることにしました。

マーカー(M)及びポジションマーカー(P0~P5)

時刻をタイムコードに変換したデータを実際の電波で送信するには、搬送波の振幅の大きさを変えてデータを送ります。
これは要するにデジタルの AM 変調です。

マーカーおよびポジションマーカーとは、データの開始点や終了点を示したり、データを区切ったりする役割をするものです。
その電波の波形はこうなります。

搬送波の40kHz を保ったまま、Highレベルを 200ms、Low レベルを 800ms にして、振幅の大きさを 10:1 の比率にすると規定されています。
これを ESP32 ( ESP-WROOM-32 )だけで実現しようとすると結構難しいです。

なぜかというと、GPIO を連続して長時間パルスを出し続けるプログラムを組むと、マイコンを監視する機能のウォッチドッグタイマの動作する余地が無いからです。
ですから、Lowレベルは delay関数を使って休止させることにしました。
delay関数を使っている間はウォッチドッグタイマが動作することができます。
振幅は 10:1 の比率ではなくなってしまいますが、それでも電波時計は受信できたので今回はヨシとします。

2進数のゼロについて

2進数のゼロというデータの電波はこんな感じです。

ESP32 でやる場合は、Lowレベル をdelay関数で休止します。

2進数の1について

2進数の1という電波はこんな感じです。

ESP32 でやる場合は、Lowレベル をdelay関数で休止します。

以上の3つの種類のパルスを ESP-WROOM-32 ( ESP32 )の GPIO で作ることができれば、標準電波 JJY の疑似電波が出力できるということになります。
よくよく解明してみると、意外と簡単そうに見えますね。

時刻のBCD 値とは

2進の0とか、2進の1とかいうデータは、時刻を BCD値に変換した2進数の値のことです。

例えば37分という10進数の値を BCD値に変換するとこんな感じになります。
時計の場合は各桁を分けた方が、プログラミングを操作しやすいですね。

パリティビットとは

パリティビットとは、電波でデータを送受信した際の誤りチェックに使うものです。
例えば、電波でビットを送信していて、途中でノイズが入って、ゼロであるはずの値が1となってしまった場合、パリティビットで整合性をチェックして、誤っていたらそのビットは受信しないという判断ができます。

標準電波の出し方のページにあるように、パリティビットの計算は

PA1 = (20h+10h+8h+4h+2h+1h) mod 2
PA2 = (40m+20m+10m+8m+4m+2m+1m) mod 2

となっています。
要するに、PA1 は「時」を BCD値に変換した2進数ビットを全て足して、2で割った余り。
PA2 は「分」を BCD値に変換した2進数ビットを全て足して、2で割った余りです。

Arduino IDE でプログラミングする場合、

PA1 = (20h+10h+8h+4h+2h+1h) % 2;
PA2 = (40m+20m+10m+8m+4m+2m+1m) % 2;

となります。
時刻や分を取得した後、しばらく経ってパリティを送信して、整合性が取れていれば、前に送信した時刻データは正しいということになります。

では、実際にプログラムを組んでみましょう。

使用するもの

ESPr Developer 32 または ESP32-DevKitC

ESP-WROOM-32開発ボードは、USB機器に優しいソフトスタート機能が付いていて、Windows機器との相性が良いFTDI製 USBシリアル変換を搭載した、スイッチサイエンス製の ESPr Developer 32 がお勧めです。

ESPr Developer 32
スイッチサイエンス(Switch Science)

スイッチサイエンスさんのページ→ ESPr® Developer 32

私のレビューの記事は以下を参照してください。

ESPr Developer 32 ( スイッチサイエンス製 ) を使ってみました

本家 Espressif社製の開発ボードは、秋月電子通商さんや、Amazonさんで購入できます。

秋月電子通商さんのページ→ ESP32-DevKitC ESP-WROOM-32開発ボード

砲弾型LED

一般的な砲弾型LED です。
10mA の電流が流れても壊れないものにしてください。
GPIO に 10mA 以上流れないように、以下の電流制限抵抗をつけます。

固定抵抗

GPIO に流れる電流を 10mA に抑えるための抵抗です。
今回の私の環境では、70Ω程度で電流が10mA でした。
後で紹介する方法で抵抗値を決めます。

ブレッドボードに挿せる φ0.65mm 単線電線

ブレッドボードに挿せる、単線(φ0.65mm)電線をアンテナに使います。
要するに硬いジャンパーワイヤー線です。
長めの物が必要です。

協和ハーモネット 耐熱通信機器用ビニル電線 H-PVC 0.65mm 2mX10色 茶赤橙黄緑青紫灰白黒
協和ハーモネット(Kyowa Harmonet)
¥1,448(2024/03/29 00:55時点)

自己融着テープ

アンテナ線を固めるためのものです。
マスキングテープとかでもよいのですが、年月が経つとベタ付くので、自己融着テープが良いかと思います。

日東 自己融着粘着テープ セパなし NO.15 19mmX10m 1519
ニトムズ(Nitoms)
¥462(2024/03/28 12:03時点)

サンハヤト SAD-101 ニューブレッドボード

ブレッドボードはサンハヤト製の SAD-101 は列が多くて、抜き差しし易くお勧めです。

サンハヤト SAD-101 ニューブレッドボード
サンハヤト
¥529(2024/03/28 14:27時点)

Wi-Fi環境

インターネットに接続できる Wi-Fi環境が必要です。

1kΩ程度の可変抵抗器(ボリューム)

LED に10mA の電流を流すための抵抗値を決めるための試験用です。

電流計

LED に 10mA の電流が流れているかどうかのチェック用です。
私は以下のアナログテスターを使っています。

Sanwa アナログマルチテスター CP-7D
三和電気計器(Sanwa Electric Instrument)
¥7,800(2024/03/28 07:37時点)

ジャンパーワイヤー、パソコン、USBケーブル等

Arduino core for ESP32 の設定を事前に済ませておく

Arduino IDE は最新版の 1.8.3 以降を使用してください。

Arduino core for ESP32 のインストール方法は以下のページを参照してください。

Arduino core for the ESP32 のインストール方法

簡易アンテナを作る

※あくまで無線工学素人工作で、自己満足工作です。
本来のアンテナ作成方法ではありません。
くれぐれもアンテナを増強したり、高性能アンテナに変えないでください。
電波法違反に抵触する恐れがあります。

硬いジャンパーワイヤー、つまり、φ0.65mm 単線電線を使って、簡易アンテナを作ります。

40kHz 電波の波長の整数倍に出来るだけ近づけようとしたのですが、波長は約 7.5km という膨大な長さですので、あまり長さは関係ないのかもしれません。
私は電波に関しては素人ですので、とりあえずそれに近い1辺が7.5㎝にしてみるという安易な考えで作ってみました。

こんな感じで、電線を3回くらい巻いて、1辺を7.5㎝として四角く形を整えて、マスキングテープで仮止めします。

次に、自己融着テープを切って、下図の様に引っ張りながら巻き付けます。
自己融着テープはマスキングテープと違って、日数が経過してもベタ付きません。

こんな感じで引き伸ばしながら巻き付けます。

そうするとこんな感じないなります。

後は、先の被覆を剥いて終わりです。

アンテナに接続する抵抗値を決める。

ESP-WROOM-32 の GPIO に流せる電流値は最大 12mA までです。
12mA ギリギリでは怖いので、10mA の電流を流すことに決めたいと思います。

GPIO の電圧は 3.3V と低いので、電波として飛ばすためには 10mA は欲しいところです。
実験したところ、5mA よりは 10mA 流した方が電波時計にしっかり受信できていました。
本当は 5V 欲しいところですが、今回は 3.3V で我慢します。

ということで、下図の様に可変抵抗器(ボリューム)と電流計を接続してみます。
ボリュームは最大抵抗値にしておくことを忘れないでください。

そこで、下図の様な単純なスケッチを Arduino IDE に入力してコンパイル実行します。

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

void setup() {
  pinMode(17, OUTPUT);
  digitalWrite(17, HIGH);
}

void loop() {

}

可変抵抗器を慎重に調整しながら、電流計が 10mA になったところで止めます。
間違えても 12mA 以上流さないでください
マイコンボードが故障する可能性がありますので十分注意してください。

私の場合の抵抗値は約 70Ωでした。

接続する

では、抵抗値が決定したところで、下図を参考に接続してみてください。

写真ではこんな感じになります。
70Ωの抵抗は無かったので、51Ωと20Ωの抵抗を直列で接続しています。

スケッチの入力

スケッチを入力する前に考慮しなければいけないことは、例えば標準電波 ( JJY )出力機能がついた自作時計を作る場合、時計表示プログラムで電波発信のパルス出力に支障が無いようにしなければいけません。
そうすると、ESP-WROOM-32 ( ESP32 )は CPU がデュアルコアで、マルチタスクプログラムを組むことが出来るので、それを使います。
マルチタスク(デュアルコア)のプログラミングについては以下の記事を参照してください。

Arduino – ESP32 のマルチタスク ( Dual Core ) を試す

では、40kHz の搬送波で「2進の0」というパルスをマルチタスクで出してみたいと思います。

GPIO は 17番を使用します。

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

const uint8_t Denpa_pin = 17;

//*********** セットアップ ******************
void setup() {
  pinMode(17, OUTPUT);
  TaskHandle_t th; //マルチタスクハンドル定義
  //マルチタスク実行
  xTaskCreatePinnedToCore(Task1, "Task1", 4096, NULL, 5, &th, 0);
}
//*********** メインループ ******************
void loop() {

}
//*********** マルチタスク ******************
void Task1(void *pvParameters){
  while(1){
    //2進の0パルス
    Generator_High(800);
    Generator_Low(200);
  }
}
//*********** 40kHz High レベル発信**********
void Generator_High(uint16_t H_time){
  int i;
  uint16_t H_cnt = 493; //Highレベルが12.5μs になるようにこの値を微調整
  uint16_t L_cnt = 422; //LOWレベルが12.5μs になるようにこの値を微調整
  
  uint32_t LastTime1 = millis();
  while(1){
    digitalWrite(Denpa_pin, HIGH);
    for(i=0; i<H_cnt; i++){
      NOP();  //No Operation(無演算命令)関数
    }
    digitalWrite(Denpa_pin, LOW);
    for(i=0; i<L_cnt; i++){
      NOP();  //No Operation(無演算命令)関数
    }
    if(millis() - LastTime1 == H_time) break;
  }
}
//*********** 40kHz LOW レベル **************
void Generator_Low(uint32_t H_time){
  digitalWrite(Denpa_pin, LOW);
  delay(H_time);
}

【解説】

●8行目:
ここで、メインループのCPUコアとは別のコアでパルス波を出すタスクを実行開始します。
メインループのコア番号は1で、Task1 のコア番号は0 です。

●11-13行:
今回の実験ではメインループには一切プログラムを置きません。

●15-21行:
ここで、Core 0 のTask1 が無限ループで実行されます。
while 文などで無限ループを組む場合、最低 delay(1) が無いとウォッチドッグタイマが動作できないのでご注意ください。
この場合、19行目の関数で delay(200) が実行されているので、問題ありません。

「2進の0」というデータは上記で説明したように、40kHz のパルスを800ms出せば良いです。

●23-40行:
ここの部分が重要です。
40kHz という高周波パルスを出す場合、適当なライブラリ関数が見つからなかったので、無謀にも digitalWrite で HIGH – LOW を切り替えてみました。

問題は、delayMicroseconds 関数がうまく機能しないので、アセンブラ?用関数の NOP() を使いました。
これは無演算命令らしく、これを繰り返し実行して、HIGHレベルをキープしたりして、細かい時間の調整ができました。
これについては Twitter で複数の方々から教えていただきました。

100MHz 程度のオシロスコープでモニタリングしながら、25-26行の数値を微調整して、High-Low レベルそれぞれ 12.5μs に調整します。
気を付けなければならないのは、38行目の if文の計算時間が結構かかりますので、それを差し引いています。
この調整がしっかりできないと、ピッタリ 40kHz の搬送波を作ることが出来なく、電波時計も反応しませんので慎重に調整します。
でも、この方法は私的にはあまり良くありません。
もっと良いレジスタやライブラリ関数を使って安定したパルスを出したいと思っていますが、今回はその方法が見つかりませんでした。

●42-45行:
標準電波 ( JJY )の規定では、Lowレベルは High の10分の1レベルですが、マイコンのウォッチドッグタイマを動作させるために delay 関数を使って停止させています。
これでも電波時計は反応してくれるので問題ないと思います。

※その後、PWM ( LEDC )ライブラリ関数を使えば、このソースコードよりも格段に安定した 40kHz パルスを出力できることが解りました。
以下の記事も合わせて参照してください。
(2019/04/04)

https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html

 

コンパイル書き込み、実行

では、スケッチをコンパイル書き込みし、実行させてみてください。
LED が 800ms 点灯し、200ms 消灯していると思います。

ここで、100MHz クラスのオシロスコープでGPIO #17 の電圧値を測定してみます。
すると、こんな感じになりました。

しっかり 800ms のパルス波と、200ms の休止が出力されています。

これをもっと拡大してみるとこうなります。

イイ感じで 12.5 + 12.5 = 25μs のパルス長になりました。

これを 高速フーリエ変換( FFT解析 )して周波数を見てみます。
私の使用しているこの USB オシロスコープは FFT解析機能があって便利です。

USB接続デジタルストレージオシロスコープ メモリ1M搭載モデル PA-S2000/E
ピーアンドエーテクノロジーズ
¥107,700(2024/03/28 13:41時点)

こんな感じになりました。

先ほど説明した通り、パルス波(方形波)なので、40kHz の奇数倍周波数もしっかり出ていますね。
要するにこれはノイズです。
けっこう高周波まで高いレベルで出ています。
こんな電波を強いレベルで世間に放出したら、とんでもない迷惑ですね。
3m以上は飛ばさないように気を付けましょう!

実は、オシロのレンジを細かくしてみると、この40kHz はピッタリではなくて、39.90 Hz でした。
パルス長も微妙に不安定だったりします。
こんな感じで、波形も乱れたりします。

これは for文が原因なのか、ESP32 のそういう仕様なのかはよく分かりません。
これの原因が解る方がいらっしゃったら教えてください。

いずれにせよ、マイコンのGPIO で電波を出力しようとすることが間違えているかも知れません。
本来ならば、安定した搬送波はそれ専用のICチップやデバイスを別途用意するべきだと思います。
近いうちにそれは試そうと思っています。

その後、PWM ( LEDC )ライブラリ関数を使えば、このソースコードよりも格段に安定した 40kHz パルスを出力できることが解りました。
以下の記事も合わせて参照してください。
(2019/04/04)

https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html

 

簡易電磁波テスターで計測

※以下はあくまで自己満足測定です。大げさかもしれませんが、少なくとも自分自身が知りたかったのです。

では、ESP-WROOM-32 の GPIO で生成した電波がどれくらい飛んでいるか、調べてみたいと思います。

随分昔に格安の電磁波テスターを購入していて、しばらく使ってなかったのですが、今回は良い機会だったので使って見ました。
こんな製品です。

●電磁波測定器 GM3120

(※当時購入したAmazonの販売店は無くなってしまいました。
今は例えば以下の販売店がありますが、自分はそこでは買ったことはありません
BENETECH デジタル 電磁波測定器 磁界と電界両方測れます GM3120
ベネテック(Benetech)
¥2,860(2024/03/28 17:05時点)

これ、安すぎるので、正確な測定かどうかは疑問ですが、ザッとした目安には良いかもしれません。

ただ、使い方に注意があります。
下図の様に手から離して測定しても電界値は全く反応しません。

磁界のμT のところは反応しています。
電界というのは、人間の体に吸収されるとのことです。
おそらく、中の電界センサー板に入る電波が相殺されてゼロになってしまうようで、手に持てば反対側から来る電界を手が吸収してくれているのだと思われます。
(あくまで個人的な想像です)

ということで、下図の様に手でしっかり持って測定します。
すると、垂直に立ち上がった電線の中央にピッタリ付けて測定した値はこうなります。

155 V/m というかなり高い値です。
これをちょっと離してみます。

すると、たちまち値が低くなって 5 V/m まで下がりました。
もう少し離してみると

殆ど検出しなくなりました。

ということで、電波は殆ど飛ばないようです。

次にアンテナループの中心で測ってみます。

さっきの電線付近よりも少ないですね。

これをちょっと離してみます。

さっきの電線付近でこれくらい離したら検知しませんでしたが、こちらの方が飛んでいるっぽいです。

次に、上部水平の電線に平行に測ってみます。

かなり高い値が出ました。
垂直電線の倍くらいありますね。
これは、電波の偏波面の影響でしょうか。

では、この位置でちょっと離してみます。

すると、この距離で検知しなくなりました。

以上をまとめると、

●電線に接触させた方が電波が強い。
●ループアンテナの中央はそれほど電波が強くないが、ちょっと離しても電波が飛んでいる。
●電線と平行に受信アンテナを立てると感度が良い。
●この程度の信号とアンテナでは、10㎝以上離すと電波は受信できない。

ということが言えそうです。
安物テスターなので信頼度はありませんが、だいたいこんな感じです。
ということは、ブレッドボード上のジャンパーワイヤーなどはこれくらいの電波が常に放出されていると言えますね。
いずれにせよ、これくらいの微弱電波ならば電波法に抵触することは無いと思います。

ただ、電圧や電流を増幅させたり、アンテナをもっと高効率のものに変えてしまうと電波法違反になってしまうので要注意です。

まとめ

以上、ESP-WROOM-32 ( ESP32 )の GPIO から 標準電波 ( JJY )を出す方法の第1弾でした。
あくまで疑似的で、高調波ノイズの多い方法です。

次回はこれからさらに進めて、実際に電波時計を合わせる 標準電波 JJY を出力させてみようと思います。

今回はここまでです。

ではまた・・・。

この後の以下の記事では、サイン波発生器を使って、高調波ノイズを極力抑えて電波時計を合わせた実験をしました。
合わせてご覧ください。

https://www.mgo-tec.com/blog-entry-ad9833-sign-radio-clock-jjy-esp32.html

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/03/28 13:45時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/03/28 20:55時点)
Excelでわかるディープラーニング超入門
技術評論社
¥1,700(2024/03/28 09:40時点)

コメント

  1. Kat-Kai より:

    はじめまして、Kat-Kaiと申します。
    興味深い記事の数々、いつも楽しく拝見しております。
    また時刻調整の成功、おめでとうございます。

    すでに電波時計の調整に成功されているため、蛇足かもしれませんが
    40KHzパルス生成時の時間調整に関しまして、
    CPUクロック数を用いることで、より正確に調整できるかもしれません。

    ESP32のCPUは160MHzで駆動しており、1秒間にCPUクロック数は
    160,000,000増加します。本クロック数は下記のコードの
    関数get_ccount()で取得する事ができます。

    そして時刻調整はコード後半部の

    while(get_ccount() - startCount < 2000) NOP();

    で行います。CPUクロック数は1マイクロ秒で160増加します。
    今回、12.5マイクロ秒待機のため12.5*160=2000で値を設定しております。

    static inline unsigned get_ccount(void)
    {
      unsigned r;
      asm volatile ("rsr %0, ccount" : "=r"(r));
      return r;
    }
    
    void setup() {
      Serial.begin(115200);
      pinMode(20, OUTPUT);
    }
    
    void loop() {
      long startCount, time1, time2;
      
      startCount = get_ccount();
      digitalWrite(20, HIGH);
      while(get_ccount() - startCount < 2000) NOP();
      time1 = get_ccount() - startCount;
      
      startCount = get_ccount();
      digitalWrite(20, LOW);
      while (get_ccount() - startCount < 2000) NOP();
      time2 = get_ccount() - startCount;
      
      Serial.print(time1);
      Serial.print(" ");
      Serial.println(time2);
      Serial.println();
    }
    

    本コードを実行すると、シリアルモニタに2つの数値が表示されまして
    それぞれHIGH時、LOW時のクロック数です。私の環境では
    2014 2018でして、それぞれ12.59, 12.61マイクロ秒に相当します。

    長文失礼致しました。本コードがお役に立てば幸いです。

    • mgo-tec mgo-tec より:

      Kat-Kaiさん

      当ブログをご覧いただき、ありがとうございます。

      スバラシイです!!!

      こんなコードがあったんですね。
      ずっと、CPUクロックを取得する方法をライブラリ等で探してたのですが、これはアセンブラでしょうか。
      私には全く未知な宣言や関数があります。

      static inline unsigned

      asm volatile ("rsr %0, ccount" : "=r"(r));

      などは、サッパリ分かりません。
      これからネットで調べて理解したいと思います。

      私のESP32-DevKitC の CPU は 240MHz なので、
      12.5*240=3000
      となります。
      恐らく、Arduino core for ESP32 で開発した場合は、デフォルトで 240MHz になると思います。

      今朝、ザッと実際に実験してパルスを確認してみましたが、millis()を使うより、かなりガッツリ安定したパルスが出せていますね。
      それもかなり細かくパルス長を調整できて、ホントに目から鱗です。
      素晴らしいです。
      ただ、やはりマルチタスク使用で他のSPIデバイスと同時使用の場合は、パルス長は安定していても波形が乱れますね。
      これの理由はまだ良く分かっていません。
      単独使用ならばもの凄い安定したパルスが出ました。

      今後、これでパルスを調整して実験してみて、近々ブログ記事に上げさせていただきたいと思います。

      Kat-Kaiさんのおかげで、アセンブラなどをうまく使うことの有効性を改めて感じました。
      とても勉強になりました。
      感謝、感謝、感謝です。
      ありがとうございました。
      m(_ _)m

  2. Kat-Kai より:

    mgo-tecさん

    時間調整のコードでお役に立てたようで何よりです。

    通常の関数ですと、その関数呼び出しにも時間のロスが生じるようです。
    一方でinline関数ですと、コンパイラが予め関数を展開してくれるので呼び出しのロスが無くなり、その結果、実行速度が向上するようです。

    asm volatile (~~)は、本アセンブラコードはコンパイラによる最適化を受けない、
    また”rsr %0, ccount”は、rsrは命令Read Special Registerで特別なレジスタの値を取得します。本命令で、ccountと呼ばれるクロック数が記録されているレジスタの値を%0、すなわちrに代入します。

    CPUの動作周波数に関しまして、ありがとうございます。
    メインではESP-IDFを用いておりましたので把握しておりませんでした。

    マルチタスク使用時の波形の乱れの正確な理由は分かりませんが、
    厳密に時間調整しないといけない関数の優先度を最高にすれば良いかもしれません。
    具体的にはxTaskCreatePinnedToCoreの第5引数の値を0にすれば優先度最高になると思います。
    もしくはGPIOより10mAと比較的大電流を流しており、不安定になってるのであれば、トランジスタを使ったスイッチングを行うことでESP32の消費電力を抑えて安定化できるかもしれません。
    このあたりは、あまり詳しくありませんが、トランジスタを用いてアンテナに電流を流す場合、逆流防止用のダイオードが必要かもしれません。

    私自身、あまり把握していないこともあり、分かりにくい文章となってしまっているかもしれませんが、ご容赦ください。

    • mgo-tec mgo-tec より:

      Kat-Kaiさん

      ご返信感謝いたします。

      なるほど!
      そういう意味だったんですね。
      でも、未だにアセンブラやコンパイラに関しては全く無知で、まだ分からないことがあるので、今後勉強していきたいと思います。
      ありがとうございました。

      私が調べたところで間違いなければ、Arduino core for ESP32 の場合、タスク優先順位は0~21の範囲で、数値が大きい方が優先順位が高いと思います。
      波形の乱れは、GPIO を 2.5mA にして、トランジスタで増幅してみたり、マルチタスクの優先順位を20 にしてみてもあまり変わりありませんでした。
      トランジスタでアンテナ逆流防止ダイオードの意味は私には良く分かりませんので試していません。
      でも、トランジスタを使って電流を少々増加させた方が電波時計が同期しやすかったです。

      いずれにしても、Kat-Kaiさんの教えていただいたコードを使用すると、パルス長が格段に安定するので、波形は乱れていても格段に電波時計が同期しやすくなりました。
      これで私的には十分過ぎるので満足です。
      これを応用して、いろいろ作ることが出来そうで、いろいろと創造が膨らみますね。

      ということで、いろいろとアドバイスいただき感謝いたします。
      また何か気付いたことがありましたらコメント等でご連絡いただけると幸いです。
      ありがとうございました。
      m(_ _)m

    • mgo-tec mgo-tec より:

      そういえば、アンテナの逆流防止は、送信アンテナが電波を受信してしまうと解釈すると、アンテナラインにLED を付けているのでそれで良いかと勝手に思っています。

      • Kat-Kai より:

        mgo-tecさん

        タスク優先順位に関して、ご指摘ありがとうございます!
        おっしゃる通り、数値が大きい方が優先順位が高かったです。

        以前、1Mbpsでの通信する際、xTaskCreateで時間調整の関数呼び出し時、優先度の数値を小さくすると通信に成功したので、数値が小さくなると優先度が高くなると勘違いしておりました。

        何はともあれ、パルス長が安定したようで良かったです。マイクロ秒オーダーで厳密に調整できれば、より幅広いことを実現出来るようになりそうですね。

        またアンテナの逆流防止に関しては、確かにLEDが付いているので問題無さそうですね。

        いつも有用な情報を提供して頂き、ありがとうございます。
        私も自身の作品発表や情報提供できるように頑張ります。

        今後ともよろしくお願いします。

        • mgo-tec mgo-tec より:

          いえいえ、あくまで個人的趣味で作っていて、穴だらけの情報なので、プロの方々からは度々指摘されてます。
          もし、お気づきの点がありましたら遠慮なくご指摘下さいませ。
          こちらこそ、今後ともよろしくお願いいたします。

タイトルとURLをコピーしました