ESP32 で 市販の 電波時計 を合わせてみた
NeoCat さんのこちらの記事では、Arduino と Ethernet Shield を使って、有線LAN でNTPサーバーから時刻を補正して標準電波 JJY を出していましたが、ESP-WROOM-32 ( ESP32 )はそれ自体が 2.4GHz 帯の Wi-Fi 電波を出しています。 それって、Wi-Fi電波に影響が出たり、標準電波 JJY のノイズになって、電波時計が合わせられないのではないかと素人的に考えてしまいます。
でも、実際にやってみたら全く問題無く合わせることができました。 ただ、電波は30㎝も飛ばないので、アンテナ線に殆ど接触させる感じになってしまいます。 3.3V のパルス波(方形波)なので仕方ありません。
いかがでしょうか。 最初と最後の方で3つの異なる電波時計が同期していますね。 一番同期が遅かった電波時計は13分ほどかかりました。 これは実際の時間とは異なって、私が適当な時間を設定しました。 市販の電波時計を自動で自由に合わせることができて、イイ感じです。
電波時計というものは正確にキッチリ時刻を合わせてくれますが、仕事をしている人にとっては5分早めたかったりしたい場合ありますよね。 そういう場合にはこの自作標準電波 JJY を使うと自由な時刻を正確に合わせることができて便利です。
それに、常にESP-WROOM-32 ( ESP32 )から Wi-Fi電波が出ているはずですが、これだけ近い位置に電波時計を置いてもしっかり同期してくれました。 これはなかなかスバラシイ!!!
ただ、市販の電波時計の仕様によって、取扱説明書に書いてある時刻誤差の範囲を超えてしまうと、いくら正確な標準電波 JJY が出ていても合わせてくれない事がありますので気を付けてください。 時計の取扱説明書を予めよく読んでおいてくださいね。
では、ESP-WROOM-32 ( ESP32 )で市販の電波時計を合わせる方法を紹介したいと思います。
※ここで紹介した標準電波発信は、とても微弱で1mも飛ばないので、電波法違反ではないと思います。 また、以下に紹介する方法は個人の趣味の自己満足で実験した方法により、動作保証はしません。 電波時計によっては受信できない場合もあります。
※ここで紹介する方法はあくまで個人的趣味の実験です。 基本的には無線工学についてはド素人ですので、あくまで自己満足報告です。 大げさな測定器も使っていますが、自分が使ってみたかっただけです。また、この記事を書いた当初は LC共振回路というものを良く知りませんでした。 最新記事では、LC共振回路の理解を深めて、LC共振回路を使ってガッツリ電波時計を合わせる実験もして見ましたので、合わせて以下の記事もご覧ください。 (2019/01/17) https://www.mgo-tec.com/blog-entry-esp32-lc-resonance-radio-watch.html
前回の記事を参照して事前設定を済ませておく
前回の記事を参照して、ESP-WROOM-32 ( ESP32 )開発ボードの接続や設定、アンテナ作り等を事前に済ませておいてください。
Time ライブラリのインストール
前回の記事に付け加えて、Arduino 標準の Time ライブラリをインストールしておきます。 これのインストール方法は以下のページを参照してください。
事前にWi-Fi環境を整えておく
今回はインターネットに接続できる Wi-Fi 環境が必要です。 NTPサーバーにアクセスして、ESP-WROOM-32 ( ESP32 )の内蔵時計を補正します。
事前にESP-WROOM-32 ( ESP32 )がご自分の Wi-Fi ルーターに接続できるようにしておいてください。 ファイアウォールやMACアドレスフィルタリングなどは設定し直すか、解除しておいてください。
スケッチの入力
では、いよいよ Arduino IDE に標準電波 JJY タイムコードを出すプログラムを組んでみます。 殆どが独自に組んだものですが、通算日を計算するTimeライブラリ計算では、NeoCatさんの記事を拝借させていただきました。
また、標準電波 JJY タイムコードについては、前回の記事を参照しながらスケッチを見てください。
こちらの記事のコメント欄で、Kat-Kai さんからとても有効な情報をいただきました。 ESP32 のクロックカウントから正確な時間経過をμセコンドオーダーで微調整ができるとのことです。 パルス長が格段に安定しますので、是非試してみて下さい。 また、コンパイルする時に、Arduino IDE の Core Debug Level を "なし" に選択してください。 Debugモードにしてしまうと、100ms毎にウォッチドッグタイマが作動して、パルス周波数が不安定になりますのでご注意ください。 スケッチ10行目の T_day が uint8_t型になっていたため、1月1日からの起算日が256以上になると0日になってしまいました。 よって、11行目に新たに uint16_t型に変更しました。 失礼しました。m(_ _)m 2017/9/13【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include #include #include "TimeLib.h" //Arduino time library ver1.5 const char* ssid = "xxxx"; //ご自分のルーターのSSIDに書き換えてください const char* password = "xxxx"; //ご自分のルーターのパスワードに書き換えてください const uint8_t Denpa_pin = 17; uint8_t Min, Hour, Year, Weekday; uint16_t T_day; uint8_t PA1, PA2; //パリティ定義 //-----------NTP server Get 関連初期化 -------------------------- unsigned int localPort = 2390; IPAddress timeServer; const char* ntpServerName = "time.windows.com"; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets byte timeZone = 9; //Tokyo WiFiUDP Udp; uint32_t Ntp_Get_LastTime = 0; //*****************セットアップ****************************** void setup() < Serial.begin(115200); delay(10); Serial.println(); Serial.print(F("Connecting to ")); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) < delay(500); Serial.print("."); >Serial.println(); Serial.println(F("WiFi connected")); delay(1000); Serial.println(WiFi.localIP()); delay(10); pinMode(Denpa_pin, OUTPUT); WiFi.hostByName(ntpServerName, timeServer); //サーバーネームをIPアドレスに変換 Udp.begin(localPort); setTime( Get_Ntp_Time() ); //NTPサーバーから時刻を取得し、ESP32内蔵時計にセットする delay(2000); //安定するまで待つ setTime( now() + 1 ); //標準時とキッチリ合わせるため、1秒早める。 Ntp_Get_LastTime = millis(); TaskHandle_t th; //マルチタスクハンドル定義 //マルチタスク実行 xTaskCreatePinnedToCore(Task1, "Task1", 4096, NULL, 5, &th, 0); > //************* メインループ **************************************** void loop() < if((millis() - Ntp_Get_LastTime) >1800000) < //30分毎にNTPサーバーから時刻取得 setTime( Get_Ntp_Time() ); Now_Time(); //現在時刻表示 Ntp_Get_LastTime = millis(); >> //************* マルチタスク **************************************** void Task1(void *pvParameters) < uint32_t LastTime = 0; if(second() == 0)< delay(1500); //ゼロ秒の場合は次のゼロ秒までやり過ごすために、1.5秒待つ。 >while(second() != 0) < //ゼロ秒になるまで待つ delay(1); //ウォッチドッグタイマを動作させるために必要 >Serial.println(); while(1) < LastTime = millis(); Now_Time(); //現在時刻初期化 Marker(); //M マーカー送信 Tcode_Min_Send(); //タイムコード(分)送信 Marker(); //P1 ポジションマーカー送信 Tcode_Hour_Send(); //タイムコード(時)送信 Marker(); //P2 ポジションマーカー送信 Tcode_TotalDay_Parity_Send(); //タイムコード(通算日)送信 Marker(); //P4 ポジションマーカー送信 Tcode_Year_Send(); //タイムコード(年)送信 Marker(); //P5 ポジションマーカー送信 Tcode_Weekday_Uruu_Send(); //タイムコード(曜日、うるう秒)送信 Marker(); //P0 ポジションマーカー送信 Serial.printf("\r\n[Total = %d ms]\r\n", (int)(millis() - LastTime)); while(second() != 0)< //もし、時間がズレた場合、ゼロ秒になるまで待つ Serial.print('-'); delay(1); //ウォッチドッグタイマを動作させるために必要 >> > //******** マーカーおよびポジションマーカー送信 ************************ void Marker() < Serial.print(".M."); Generator_High(200); Generator_Low(800); >//******** タイムコード(分)送信 ************** void Tcode_Min_Send()< uint8_t Min10 = floor(Min / 10); uint8_t Min01 = Min % 10; uint8_t m[7] = ; //分のパリティ PA2 計算用配列初期化 //---------10の位------------- m[6] = bitRead( Min10, 2 ); Serial.print( m[6] ); Time_Code_Bit_Generate( m[6] ); m[5] = bitRead( Min10, 1 ); Serial.print( m[5] ); Time_Code_Bit_Generate( m[5] ); m[4] = bitRead( Min10, 0 ); Serial.print( m[4] ); Time_Code_Bit_Generate( m[4] ); Serial.print('0'); Time_Code_Bit_Generate(0); //---------1の位-------------- m[3] = bitRead( Min01, 3 ); Serial.print( m[3] ); Time_Code_Bit_Generate( m[3] ); m[2] = bitRead( Min01, 2 ); Serial.print( m[2] ); Time_Code_Bit_Generate( m[2] ); m[1] = bitRead( Min01, 1 ); Serial.print( m[1] ); Time_Code_Bit_Generate( m[1] ); m[0] = bitRead( Min01, 0 ); Serial.print( m[0] ); Time_Code_Bit_Generate( m[0] ); //-------分パリティ計算------- PA2 = (m[6] + m[5] + m[4] + m[3] + m[2] + m[1] + m[0]) % 2; > //******** タイムコード(時)送信 ************* void Tcode_Hour_Send()< uint8_t Hour10 = floor(Hour / 10); uint8_t Hour01 = Hour % 10; uint8_t h[6] = ; //時のパリティ PA1 計算用配列初期化 Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); //---------10の位------------- h[5] = bitRead( Hour10, 1 ); Serial.print( h[5] ); Time_Code_Bit_Generate( h[5] ); h[4] = bitRead( Hour10, 0 ); Serial.print( h[4] ); Time_Code_Bit_Generate( h[4] ); Serial.print('0'); Time_Code_Bit_Generate(0); //---------1の位------------- h[3] = bitRead( Hour01, 3 ); Serial.print( h[3] ); Time_Code_Bit_Generate( h[3] ); h[2] = bitRead( Hour01, 2 ); Serial.print( h[2] ); Time_Code_Bit_Generate( h[2] ); h[1] = bitRead( Hour01, 1 ); Serial.print( h[1] ); Time_Code_Bit_Generate( h[1] ); h[0] = bitRead( Hour01, 0 ); Serial.print( h[0] ); Time_Code_Bit_Generate( h[0] ); //-------分パリティ計算------- PA1 = (h[5] + h[4] + h[3] + h[2] + h[1] + h[0]) % 2; > //******** タイムコード(その年の1月1日からの通算日)送信 *********** void Tcode_TotalDay_Parity_Send() < uint8_t T_day100 = floor(T_day / 100); uint8_t T_day10 = ((uint8_t)(floor(T_day / 10))) % 10; uint8_t T_day01 = T_day % 10; uint8_t b; Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); //---------100の位------------- b = bitRead( T_day100, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day100, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); Serial.print('0'); Time_Code_Bit_Generate(0); //---------10の位------------- b = bitRead( T_day10, 3 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day10, 2 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day10, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day10, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); Marker(); //P3 ポジションマーカー //---------1の位------------- b = bitRead( T_day01, 3 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day01, 2 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day01, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( T_day01, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); //---------パリティビット--------- Serial.print(PA1); Time_Code_Bit_Generate(PA1); Serial.print(PA2); Time_Code_Bit_Generate(PA2); //---------予備ビット------------- Serial.print('0'); Time_Code_Bit_Generate(0); //SU1 予備ビット >//**********タイムコード(年)下2桁送信********************** void Tcode_Year_Send() < uint8_t Y10 = (uint8_t)(floor( Year / 10 )); uint8_t Y01 = Year % 10; uint8_t b; //---------予備ビット--------- Serial.print('0'); Time_Code_Bit_Generate(0); //SU2 予備ビット //---------10の位------------- b = bitRead( Y10, 3 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y10, 2 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y10, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y10, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); //---------1の位------------- b = bitRead( Y01, 3 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y01, 2 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y01, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Y01, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); >//********** タイムコード(曜日、うるう秒)送信 ************ void Tcode_Weekday_Uruu_Send() < uint8_t b; //---------曜日------------- b = bitRead( Weekday, 2 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Weekday, 1 ); Serial.print( b ); Time_Code_Bit_Generate( b ); b = bitRead( Weekday, 0 ); Serial.print( b ); Time_Code_Bit_Generate( b ); //---------うるう秒--------- Serial.print('0'); Time_Code_Bit_Generate(0); //LS1:うるう秒 Serial.print('0'); Time_Code_Bit_Generate(0); //LS2:うるう秒 //-------------------------- Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); Serial.print('0'); Time_Code_Bit_Generate(0); >//********** タイムコードビット送信 ***************** void Time_Code_Bit_Generate(uint8_t b)< if( b )< //2進数の1 Generator_High(500); Generator_Low(500); >else < //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; idigitalWrite(Denpa_pin, LOW); for(i=0; i if(millis() - LastTime1 == H_time) break; > > //*********** 40kHz LOW レベル ****************** void Generator_Low(uint32_t H_time) < digitalWrite(Denpa_pin, LOW); delay(H_time); >//*********** その年の1月1日からの通算日計算 ****** uint16_t TotalDay()< //Arduino Timeライブラリ関数を使う tmElements_t tm = ; time_t t = makeTime(tm); return (now() - t) / SECS_PER_DAY + 1; > //*********** 現在時刻代入 ************************ void Now_Time() < Min = minute(); Hour = hour(); Year = year() - 2000; if(Year >100) Year = 0; //有り得ない数値は0にする Weekday = weekday() - 1; T_day = TotalDay(); Serial.printf("Year=%d / TotalDay=%d / Weekday=%d / %02d:%02d\r\n", Year, T_day, Weekday, Hour, Min); > //********* NTP time GET *************************** time_t Get_Ntp_Time() < while (Udp.parsePacket() >0) ; Serial.println("Transmit NTP Request"); Send_NTP_Packet(timeServer); uint32_t beginWait = millis(); while (millis() - beginWait < 1500) < int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) < Serial.println("Receive NTP Response"); Udp.read(packetBuffer, NTP_PACKET_SIZE); unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] > Serial.println("No NTP Response :-("); return 0; // return 0 if unable to get the time > //******** Send NTP Packet ************************* void Send_NTP_Packet(IPAddress &address) < memset(packetBuffer, 0, NTP_PACKET_SIZE); packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); >●1-2行: Arduino core for ESP32 標準のライブラリインクルードです。 WiFi を使ってインターネットに接続するために使います。
●3行目: Arduino 標準の Time ライブラリのインクルードです。
●14-20行: NTPサーバーClient のサンプルスケッチの初期化をそのまま流用しています。 16行目ではNTPタイムサーバーのサーバーネームを定義します。 私が一番取得し易いサーバーが “time.windows.com” でした。 ご自分の都合の良いサーバーに指定してください。
●46行: digitalWrite 関数でGPIO の High-Low を出力する場合、忘れずに pinMode 関数を使って出力設定にしておきます。
●52-55行: Get_Ntp_Time関数は 366行で定義しています。 ここで、NTPサーバーから時刻を取得して、ESP-WROOM-32 の内蔵時計をセットしています。 55行目ではその時刻をさらに1秒進めています。 なぜかというと、このまま電波時計を合わせても、電話の時報よりどうしても1秒遅くなってしまうので、ここで1秒早めておきます。
●59-61行: 電波時計用電波発信のパルスを遅らせることが出来ないので、メインループとは別タスクで実行します。 60行で ESP32 のもう一つのCPU(コア番号0)で電波を発信させています。 それが、72-105行になります。
ESP32 ( ESP-WROOM-32 )のマルチタスク(デュアルコア)の使い方については以下の記事を参照してください。
●64-70行: メインループの CPUコア番号は 1 です。 ここで30分毎に NTPサーバーから時刻を取得して、内蔵時計を補正します。
●72-105行: マルチタスク、CPUコア番号0 のループ動作です。 74-79行では、時刻がゼロ秒の途中からこのタスクに入ってしまったら、秒を標準時とピッタリ合わせることが出来ないので、次のゼロ秒まで待ちます。 79行目のwhileループ内では最低delay(1)などの休止期間がないと、監視用のウォッチドッグタイマが動作できないので、必ず delay(1) を置きます。
83-104行で、規定に乗っ取ってタイムコードを送信します。 標準電波 JJY のタイムコード構成については、前回の記事を参照してください。
ここで重要なのが、85行目で現在時刻を各変数に代入しているということです。 時刻ゼロ秒のマーカーを発信する時点の時刻を変数に代入する必要があります。 タイムコードはトータル1分かけて出力するので、一番最初のマーカーを送信した時点の時刻でなければいけません。 1分以内だから途中で時刻が変化することはあり得ませんが、NTPサーバーから時刻取得して補正することが途中で入るとややこしくなるからです。
98行目でトータルタイムをミリセコンド単位でシリアルモニターに表示させています。 Totalがピッタリ 60000ms になっていることが重要です。 これがズレてしまうと、タイムコード発信もだんだんとズレていってしましますので要注意です。
●113-146行: マーカーの後に分のタイムコードを送信します。 114-115行で、2桁の分表示を十の位と一の位に分割する計算です。 116行でパリティ―ビット計算の為のビットを格納しておく配列定義です。
119-142行で2桁の数値をそれぞれ BCD値に変換してタイムコード送信しています。 145行で予めパリティビット計算しておきます。
●185-248行: その年の1月1日からの通算日を出力します。 185-187行では3桁の10進数を各桁に分ける方法がちょっと難しくなりますね。 積算日は、349-354行にあるように、Timeライブラリの関数を使って計算します。
219行にポジションマーカー P3 を送信することを忘れないでください。
●286-313行: 曜日と「うるう」秒ビットを送信しています。 「うるう」秒はNTPサーバーから時刻を取得しているので、ここは常にゼロで良いです。
●325-342行: ここが心臓部です。 whileループ内で 40kHz のパルスをGPIO に出力するわけですが、1波長が 25μsという細かい時間を設定することが難しいのです。 delayMicroseconds関数がうまく機能すれば問題無かったのですが、Arduino core for ESP32 ではパルスの長さをマイクロセコンド単位で微調整できませんでした。
ただ、オシロスコープで波形を見ると、340行の条件式があるせいか、微妙にパルス幅が変わったり、意図しない休止期間があったりして、完璧な40kHz を発信することができませんでした。 SPI通信のように、ビットを送信するところだけ安定したパルスが出ればよいという場合はこれでも良いのですが、標準電波は最大800ms もの長い間、安定した40kHz を発信し続けなければならないので、マイコンのGPIOではちょっと無理のような気がします。 搬送波の発信は別途発信機器を設けた方が良いと改めて思いました。
●344-347行: Lowレベル発信はdelay関数を使って、一切発信しないようにしました。 前回の記事でも述べたように、ESP32 のウォッチドッグタイマ(WDT)を動作させるためにもこの方が都合良いためです。
●349-354行: Timeライブラリ関数を使って、その年の1月1日からの通算日を計算しています。 これは、以下の記事を参照して、ほぼそのまま使用させていただきました。
●366-402行: ESP8266用のUdpNtpClient のサンプルスケッチをそのまま流用しています。
コンパイル書き込み、実行
では、いよいよArduino IDE でコンパイル書き込みおよび実行させてみてください。
※こちらの記事のコメント欄で、Kat-Kai さんからとても有効な情報をいただきました。 ESP32 のクロックカウントから正確な時間経過をμセコンドオーダーで微調整ができるとのことです。 パルス長が格段に安定しますので、是非試してみて下さい。 また、コンパイルする時に、Arduino IDE の Core Debug Level を "なし" に選択してください。 Debugモードにしてしまうと、100ms毎にウォッチドッグタイマが作動して、パルス周波数が不安定になりますのでご注意ください。Arduino IDE のシリアルモニターを 115200 bps で起動します。 日本標準時刻がゼロ秒になっていなければ、ゼロ秒になるまでタイムコード生成するのを待ちます。
電波時計の取扱説明書を見ると、AUTO受信設定されているものは、最初に60kHz搬送波の受信をし始めて、受信できなければ 40kHz 搬送波を受信するという設定になっていたりします。
これは、最初の5分間で60kHz の電波を受信し、失敗したら次の5分間で 40kHz を受信するとのことです。
また、40kHzの搬送波を出しているつもりでも、60kHz で受信したりします。 パルス波(方形波)なので、仕方ないところですね。
うまく受信できない場合
うまく受信できない場合があると思います。 その場合、時計の取扱い説明書をもう一度読み返してみて、40kHz受信と60kHz 受信を切り替えてみて下さい。
可能ならば 100MHz クラスのオシロスコープでGPIO の波形を計測して、1パルスが25μsになっているかどうか確認しながら、ソースコードの327-328行目の数値を変えてみて下さい。
ESP-WROOM-32 ( ESP32 )にも個体差があって、上記で紹介したプログラムでは40kHz からズレてしまっているかも知れません。
325-342行 例1【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
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; idigitalWrite(Denpa_pin, LOW); for(i=0; i if(millis() - LastTime1 == H_time) break; > > 325-342行 例2【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
void Generator_High(uint16_t H_time)< int i; uint16_t HL_cnt = 460; //1パルスが25μsになるようにこの数値を微調整 uint32_t LastTime1 = millis(); while(1)< if(millis() - LastTime1 >= H_time) break; digitalWrite(Denpa_pin, HIGH); for(i=0; i digitalWrite(Denpa_pin, LOW); for(i=0; i > > 325-342行 例3【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
void Generator_High(uint16_t H_time) < int i; uint16_t H_cnt = 481; //FFTアナライザで40kHzになるようにこの値を微調整 uint16_t L_cnt = 411; //FFTアナライザで40kHzになるようにこの値を微調整 uint32_t LastTime1 = millis(); while(1)< digitalWrite(Denpa_pin, HIGH); for(i=0; idigitalWrite(Denpa_pin, LOW); for(i=0; i if(millis() - LastTime1 == H_time) break; > >例2のように if文の条件式も、 == とするのと、>=とするのでは計算速度が違っているように思います。
ということで、安定した搬送波を得るためには、やはりマイコンのGPIO ではかなり難しいものがありますね。 良いライブラリがあればそれを使うか、別途デバイスで搬送波を発信させる方が良いですね。
まとめ
結局のところ、この簡易プログラムでは安定した搬送波とは程遠いので、うまく受信できない場合があると思います。 その場合は40kHz または60kHzのサイン波を発信できるデバイスを別途構成した方が現実的ですね。 それはいつか試してみたいと思います。
また、普段は取扱説明書など読まなかった電波時計ですが、こうやって実験してみると電波時計のしくみが何となく分かってきてオモシロイですよね。 電波の送信ができれば、受信もできそうな気がします。 これを突き詰めると、無線装置開発なんていう夢が膨らんでしまいますね・・・。
この記事以降、以下の記事でさらに電波時計を合わせやすくするものを作りました。 波形発生器で純粋なサイン波を発生させて搬送波を作っています。 ただし、自己満足工作で、大げさです。 合わせてご覧ください。 https://www.mgo-tec.com/blog-entry-ad9833-sign-radio-clock-jjy-esp32.html ESP32 の GPIO から 疑似的に 日本標準電波 JJY を出してみる実験、第1弾 ESP32 で波形信号発生モジュール AD9833 を制御して サイン波 を発生させてみる mgo-tecをフォローするコメント
コメントをどうぞ コメントをキャンセル お知らせ- しばらく停滞中で~す。再開未定 (-_-)
- Google ColabのAPI変更により、こちらの記事の最後のサンプルコードを修正しました。(2022/09/18)
- こちらの記事もYahoo天気から気象庁天気予報に変更したものを追記しました。 (2022/09/02)
- こちらの記事をYahoo天気から気象庁天気予報に変更したものを追記しました。 (2022/08/27)
- 諸事情で記事更新停滞しています。1年以上経過している記事が殆どなので、うまく動作しない場合があります。ご了承ください。
- Blynk2.0で双方向通信できない場合はこちらの記事のコメント欄をご覧ください(2022/06/24)
- 諸事情でしばらく記事更新できません。ほとんどの記事が1年以上経過している為、動作しないものもあることをご了承ください。
- Yahoo RSS天気予報が配信終了したことに伴い、気象庁から天気予報を取得する方法にライブラリを更新しました。こちらの記事を参照してください(2022/04/15)
- Yahoo! RSS天気予報の配信が2022/03/31で終了してしまいました。よって、過去のプログラムは動きません。(2022/04/06)
- 工学社さん技術情報誌I/O(アイオー)2018/04号にもこちらの記事を転載して頂きました。有難うございます!
- こちらの記事を工学社さんの技術情報誌I/O(アイオー)に転載していただけました。ありがとうございます!
- Make:Japanさんにこちらの記事が紹介されました。ありがとうございます。
- Arduino core for the ESP32 のインストール方法
- ESP-IDF の使い方( ESP32 )
- ESP32 トラブルシューティング
- ESP32 チップ情報確認方法
- Arduino-ESP32 SPIFFS アップローダープラグインのインストール方法
- ESP32データシート独自和訳
- ESP32 Technical Reference Manual和訳
- ESP8266 チップ情報確認方法
- ESP8266, ESP-WROOM-02 トラブルシューティング
- ESPr Developer ESP-WROOM-02開発ボードの使い方
- Arduino – ESP8266 インストール方法
- ZIP形式ライブラリのインストール方法
- Androidスマホ画像リサイズImageShrink使い方
- iPhone 写真リサイズ バッチリサイズの使い方
- micro SD カードのフォーマット方法
- LC 共振回路計算ツール
- RC フィルタ回路計算ツール
当ブログでは広告表示しております。 このブログの維持運営のためには必要不可欠です。どうかご容赦下さい。 m(_ _)m 掲載中の広告サービスは、Google Adsenseという広告配信サービスとAmazon.co.jpアソシエイトというアフィリエイト宣伝プログラムの参加者です。