編集履歴一覧に戻る
TakSan0のアイコン画像

TakSan0 が 2021年02月23日23時57分49秒 に編集

初版

タイトルの変更

+

ドットマトリクス・ネットワーク時計

タグの変更

+

ESP32

+

WiFi

+

NTP

+

秋葉原2021

メイン画像の変更

メイン画像が設定されました

本文の変更

+

# はじめに 自分の部屋の時計は自作の時計を使うという事にこだわっています。 3.5インチのLCDを使ったアナログ時計と、7セグLEDの物を使っていましたが、最近視力低下が進んだのか小さすぎて見づらくなってきました… ディスプレイを大型化するために新調することにしました。 時計合わせの為に面倒な操作が必要なのも改善するため出来るだけ単純に… というわけで、サーバーから取得した時刻で自動同期するネットワーク時計を作りました。 まずは、完成形紹介動画をご覧ください。 @[youtube](https://youtu.be/VLTfb2B0Fjc) # 使用サービス NTP (Netowork Time Protocol) 時刻同期サーバとNTPというプロトコルを使用して時刻を取得します。 身近な所では、正確性が低い PCの時間を定期的に同期する用途によく利用されています。 # 構成部品 自宅に転がっているジャンクを使いどこまで安く作れるかを試してみました。 |品名|型番 or SPEC|個数|参考価格|備考| |:---|---|---|---:|---| |マイコン基板|KP-ESP32C|1|1,430|| |ドットマトリックスLED|AD-501-C (32x16dots)|1|600|| |バッファ|TC74LCX245F|1|140|| |レギュレータ|Rohm BA033CC0FP|1|50|| |SOP/SSOP 20Pin変換基板|DGCB-20SD|1|80|| |SOT89/SOT223変換基板|DGCB-89-223|1|40|| |マイクロUSB変換基板|不明|1| 250|(3個セット)| |チップ抵抗|10KΩ|1|0.1|(1000個入り特価品)| |チップコンデンサ|0.1uF50V|2|2|(10個セット)| |チップ電解コンデンサ|22uF16V|2|10|(10個セット)| |ポリカねじ|M3x10|12|120|| |ナット|M3|12|60|| |コネクタセット|XH2pin コネクタ+ポスト|1|30|| |コネクタセット|PH8pin コネクタ+ポスト|1|40|| |コネクタセット|EH3pin ポスト|1|15|| |基板固定具|不明|6|60|| |ネームスタンド|不明|1|110|| |下敷き|PET製※|1|10|(A4サイズをカット) 基板固定用| 参考価格で複数個入りの物は個数で割って必要数を乗算した価格です。 ※はんだごてが当たった時のことを考えると、塩化ビニルは避けたほうが無難かもです。(塩素発生の可能性が無いとは言い切れませんので) 3,000円程となりました。結構安くできました。 # ハードウェア ### 構成基板 以下の5枚の基板で構成されています。 #### 電源コネクタ マイクロSD typeB のコネクタが余っていたので使用しましたが、5Vなら給電できるものなら何でもいいかと思います。 #### 電源部 ディスプレイモジュールは5V、マイコン(ESP32)は 3.3V と2種類の電源が必要ですので、入力 5V をレギュレータで 3.3V に変換して2種類用意しました。 #### マイコン部 ESP32 の変換基板ですが、書き込みモードと切り分けるためのスイッチやプルアップ抵抗がほとんど実装されているので、そのまま使っています。 プログラム書き込みには、USBシリアル変換ケーブルが別途必要です。 #### バッファ部 ESP32側が3.3V でディスプレイモジュール側が 5Vなので、通常は信号線の電圧レベル変換が必要です。 実はディスプレイ側が入力だけ(双方向ならマイコンが壊れる可能性もあります)なので、スレッショルド電圧 ("H" を認識する電圧) が 3.3V以下なので、H /L を十分識別できるはずで直結できるとは思うのですが、やってみるとうまくいきませんでした。 起動時にいくつか端子を見るらしく、直接つなぐと影響を受けて起動不可になってしまいましたので、急遽でバッファを追加し対策しました。 このバッファは OE(OutputEnable) をプルアップしておくと、ハイインピーダンス状態にできるトライステート機能が入っているため、起動時には完全に切断状態にできます。その後プルアップしている端子をプログラムで "L" にすることで、端子が接続されます。 #### ディスプレイモジュール PH10pin のケーブルが付いてきましたが、バッファー側は8本の物に変更し、電源を分岐する等の改造をしました。 ### 接続 |名|電源レギュレータ|ESP32|バッファ(A側)|バッファ(B側)|ディスプレイモジュール|備考| |---|---|---|---|---|---|---| |5V|In|-|-|-|1(Vcc),8(VLED)|| |3.3V|OUt|2|1(DIR)|20(Vcc)|-|| |Ground|Gnd|1,15,38|10(Gnd),8(A7),9(A8)|-|9,10|| |SIN1|-|8(IO32)|2(A1)|18(B1)|2(SIN_1)|| |SIN2|-|9(IO33)|3(A2)|17(B2)|3(SIN_2)|| |SIN3|-|12(IO27)|4(A3)|16(B3)|4(SIN_3)|| |CLK|-|13(IO14)|5(A4)|15(B4)|5(CLOCK)|| |LAT|-|14(IO12)|6(A5)|14(B5)|6(LATCH)|| |ENA|-|16(IO13)|7(A6)|13(B6)|7(ENABLE)|| |OE|-|23(IO15)|19(OE)|-|-|3.3Vにプルアップ| ### 回路図 準備中(現在 使い始めたばかりのEagle と格闘中です) # ソフトウェア ### 開発環境 Arduino IDE ボードマネージャーで "ESP32 Dev Module" を選択 ### 表示部 フォントは 7x14 ドット、数字だけなので数もないし自作しました。センスが問われますが…いまいち!? 桁数的に秒表示が難しかったので、最上部、最下部の2行をそれに使用しました、ドットが1ドットずつ動くようにしました。(動画参照) ### 各部制御方法 ざっくりと解説しますが、詳しくはソースを参照してください。 #### WiFi 接続 大したことはしていませんが、複数のアクセスポイントを選択できるように、 WiFiMulti というものを使用しています。 家のWiFi とスマホのテザリングといったように、複数の SSID / パスワード 組み込んでおけば、自動的に見つけたものを使用してくれるので家でも外出先で使用できます。 #### 時刻同期 NTP での時刻同期は ESP32で使用するのは、実際には複雑ではありません。サーバーアドレスを引数に指定して configTime() という関数を呼び出すだけで、サーバーから取得した日時をESP32 内部の時計に設定します。 但し、内部の時計だけではあまり正確ではありませんので、1日1回 (AM4:00 に) 同期をやり直すようにしています。 #### MatrixLED 制御 安価なDisplay によくあるパターンなのですが、コントローラーを内蔵していないため、自分ですべて制御する必要があります。 一般的なダイナミック点灯という仕組みを使用します。 今回使用した液晶は 16Bit のシリパラ IC の3個構成です。 シリパラIC とは、クロック同期シリアルで1ビットずつ連続で流し込んだデータを全ビット分送り終わった後、出力してやると16ビット分のパラレル出力が出来るものです。(シリアルパラレル変換IC の略) 横16ドット分×2 縦16ビット分が必要なので、3個必要となります。 1行単位で、横側の点灯したいビットの H を出力し、縦側を L を出力することで、電流が H->L に流れLED が点灯する仕組みです。 これを、縦幅 16ドット分目に見えないほどの高速で繰り返すことにより、ディスプレイする仕組みです。(縦方向は内部で反転している様で、マイコンからは"H" を出力した行が選択されます。) ちらつきが目立たないようにするために、1msec ごとに1行出力する形にしました。本当は、ESP8266 ボードでやりたかったのですが、こちらは能力不足で(おそらく digitalWrite() の速度が遅い) ちらつきがひどすぎた為、使用を断念しました。 ### ソース ```C:NTPClockWithMatrixLed.ino // [[[ インクルード ]]] #include <Arduino.h> #include <time.h> #include <WiFi.h> #include <WiFiMulti.h> // [[[ マクロ定義 ]]] // 使用端子設定 #define PIN_SIN1 32 #define PIN_SIN2 33 #define PIN_SIN3 27 #define PIN_CLK 14 #define PIN_LAT 12 #define PIN_ENA 13 #define PIN_OE 15 // 時差設定 #define TIMER_INTERVAL_US 1000 #define NTP_PRIMARY "ntp.nict.jp" #define NTP_SECONDARY "ntp.jst.mfeed.ad.jp" #define JST (3600L*9L) // 世界標準からの日本の時差 // 時刻再取得時刻 #define ADJUST_HOUR 4 #define ADJUST_MIN 0 #define ADJUST_SEC 0 // [[[ 型定義 ]]] typedef struct { const char* ssid; // アクセスポイント(ルーター)のSSID const char* pass; // アクセスポイント(ルーター)のパスワード } AP_INFO; // 個人設定ファイル (このファイルを自分の WiFiアクセスポイント設定の物に置き換える) #include "AccessPoints.h" // [[[ 変数 ]]] WiFiMulti wifiMulti; hw_timer_t* timer = NULL; static uint32_t system_count = 0; struct tm time_struct; uint32_t disp_buf[16]; char print_buf[120]; static const char week_day_str[7][4] = {"SUN","MON","TUE","WED","THR","FRI","SAT"}; /* <<<FontData>>> -----------------------------------------------------------*/ #define BIN (uint8_t)((((((((0 #define O )*2+1 #define _ )*2 const struct { uint8_t lines[16];} num_font[]={ /* ' [[[[[ Font Data ]]]]] */ /* 0 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 1 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ O O O _ _ _ _ , BIN O O O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN O O O O O O _ _ , BIN O O O O O O _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 2 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ O O O _ _ _ , BIN _ O O O _ _ _ _ , BIN O O O _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O O O O O _ _ , BIN O O O O O O _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 3 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ O O O O _ _ _ , BIN _ O O O O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 4 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ O O O _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O _ _ _ , BIN O O _ O O _ _ _ , BIN O O _ O O _ _ _ , BIN O O _ O O _ _ _ , BIN O O O O O O _ _ , BIN O O O O O O _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 5 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN O O O O O O _ _ , BIN O O O O O O _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O O O O _ _ _ , BIN O O O O O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 6 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 7 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN O O O O O O _ _ , BIN O O O O O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ _ O O _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 8 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* 9 */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O O _ _ , BIN _ _ _ _ O O _ _ , BIN _ _ _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN O O O O O O _ _ , BIN _ O O O O _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* _ */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* ? */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ O O O O _ _ _ , BIN O O O O O O _ _ , BIN O O _ _ O O _ _ , BIN O O _ _ O O _ _ , BIN _ _ _ O O O _ _ , BIN _ _ O O O _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ O O _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* : */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN O O _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }}, /* _ */ {{ BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ , BIN _ _ _ _ _ _ _ _ }} }; #undef BIN #undef O #undef _ /* <<<FontData>>> -----------------------------------------------------------*/ // [[[ プロトタイプ ]]] void draw_font(int x_pos, int num); void matrix_led_send_data(uint16_t com_data, uint16_t a_data, uint16_t b_data); void matrix_led_control(void); void update_display(void); void IRAM_ATTR timer_callback(); void get_date_time_from_server(void); void setup(); void loop(); // フォントの描画 void draw_font(int x_pos, int num) { int x, y; uint32_t draw_bit_data = 1 << (32 - 1 - x_pos); uint8_t bit_data; int width = (num >= 12)? 3: 7; // 12 番以上のフォントは横幅3ドット、それ以外は7ドットとする if ( num < 14 ) { bit_data = 0x80; for (x = 0; x < width; x++) { for (y = 0; y< 16; y++) { // ONのビットの場合 if ( ( num_font[num].lines[y] & bit_data ) != 0) { disp_buf[y] |= draw_bit_data; } // OFFのビットの場合 else { disp_buf[y] &= ~draw_bit_data; } } bit_data >>= 1; draw_bit_data >>= 1; } } } // Matrix LED のシリパラ IC に COMMON COLUMN A COLUMN B のデータを1ビットずつ16ビット分送信する void matrix_led_send_data(uint16_t com_data, uint16_t a_data, uint16_t b_data) { int i; uint16_t bit_data = 0x0001; // 16ビット分のループ for (i = 0; i<16; i++) { // クロックを "L" にしてから更新 digitalWrite(PIN_CLK,0); // データを出力 digitalWrite(PIN_SIN1, ( ( ( com_data & bit_data ) != 0 )? 1 : 0 )); digitalWrite(PIN_SIN2, ( ( ( a_data & bit_data ) != 0 )? 1 : 0 )); digitalWrite(PIN_SIN3, ( ( ( b_data & bit_data ) != 0 )? 1 : 0 )); // 立ち上がりでデータ反映 digitalWrite(PIN_CLK,1); // Bit Shift bit_data<<=1; } } // Matrix LED のダイナミック点灯 (バッファの 1 になっているビットを点灯させる) void matrix_led_control(void) { static int line = 0; // 制御信号を書き込み準備状態にする digitalWrite(PIN_ENA,1); digitalWrite(PIN_LAT,0); // データの送信 matrix_led_send_data( (1<<line), (disp_buf[line]>>16), (disp_buf[line]&0xFFFF)); // 制御信号を書き込み準備解除にする digitalWrite(PIN_LAT,1); digitalWrite(PIN_ENA,0); line++; if (line>= 16) line = 0; } // ディスプレイの更新 void update_display(void) { // 時 をフォントで描画 draw_font( 1, ((time_struct.tm_hour<10)? 10: (time_struct.tm_hour / 10))); draw_font( 8, time_struct.tm_hour % 10); // ":" をフォントで描画 draw_font( 15, (((time_struct.tm_sec&1)== 0)? 12: 13)); // 分 をフォントで描画 draw_font( 18, time_struct.tm_min / 10); draw_font( 25, time_struct.tm_min % 10); // 30秒未満では最上段にドットを描画 if (time_struct.tm_sec < 30) { disp_buf[0] = (uint32_t)(1ul << (uint32_t)(32 - 2 - time_struct.tm_sec)); disp_buf[15] = 0; } // 30秒以上では最下段にドットを描画 else { disp_buf[0] = 0; disp_buf[15] = (uint32_t)(1ul << (uint32_t)( time_struct.tm_sec - 30 + 1)); } } // タイマー満了時のコールバック関数(1msecごとに呼ばれる関数) void IRAM_ATTR timer_callback() { system_count++; matrix_led_control(); } // 時刻取得関数 (WiFi を使って、タイムサーバーに時刻を取得する) void get_date_time_from_server(void) { struct tm localTime; // <WiFi ONする> WiFi.mode(WIFI_MODE_APSTA); // <AP接続> Serial.println("Connecting to AP"); // <AP接続待ち> while ( wifiMulti.run() != WL_CONNECTED) { delay(100); Serial.print("."); } Serial.println(); // <AP接続完了> Serial.print("Connected, SSID: "); Serial.println(WiFi.SSID()); Serial.print("Connected, IP address: "); Serial.println(WiFi.localIP()); // <時刻が取得できるまで繰り返す> do { // <NTP サーバーから時間取得> Serial.println("Get time From NTP"); configTime( JST, 0, NTP_PRIMARY, NTP_SECONDARY); // 取得できたらループから抜ける if (getLocalTime(&localTime) != 0) break; // 取得できなかったら、500msec待って繰り返す delay(500); Serial.println("Failed:Get time From NTP RETRY"); } while (1); // <AccessPoint からの切断> Serial.print("Disconnecting AP\r\n"); if (WiFi.softAPdisconnect(true)) Serial.print("Leave AP success\r\n"); // <WiFi を休止する> Serial.print("WiFi OFF\r\n"); WiFi.mode(WIFI_OFF); } void setup() { // デバッグ用シリアルのボーレート設定 Serial.begin(115200); // 端子設定 pinMode(PIN_ENA, OUTPUT); pinMode(PIN_LAT, OUTPUT); pinMode(PIN_CLK, OUTPUT); pinMode(PIN_SIN1, OUTPUT); pinMode(PIN_SIN2, OUTPUT); pinMode(PIN_SIN3, OUTPUT); pinMode(PIN_OE, OUTPUT); digitalWrite(PIN_ENA, HIGH); digitalWrite(PIN_LAT, HIGH); digitalWrite(PIN_CLK, HIGH); digitalWrite(PIN_SIN1, HIGH); digitalWrite(PIN_SIN2, HIGH); digitalWrite(PIN_SIN3, HIGH); digitalWrite(PIN_OE, LOW); // <AP登録> for (int i = 0; i<(sizeof(ap_info) / sizeof(ap_info[0])); i++) { sprintf(print_buf,"%d/%d) SSID:%s, Password:%s\n", i, (sizeof(ap_info) / sizeof(ap_info[0])), ap_info[i].ssid, /* ap_info[i].pass */"********" ); Serial.println(print_buf); wifiMulti.addAP(ap_info[i].ssid, ap_info[i].pass); } // <定期タイマー登録> timer = timerBegin(0, 80,true); timerAttachInterrupt(timer, timer_callback, true); timerAlarmWrite(timer, TIMER_INTERVAL_US, true); // <時刻取得> get_date_time_from_server(); delay(500); // <定期タイマー開始 > timerAlarmEnable(timer); } void loop() { static int sec_before = 0; // <現在時刻の取得> getLocalTime(&time_struct); // <前回と秒の桁が違うときに時刻の更新> if (time_struct.tm_sec != sec_before) { // デバッグ用にシリアルに出力(デバッグしないなら不要) sprintf(print_buf," %04d/%02d/%02d(%s) %02d:%02d:%02d\n", time_struct.tm_year+1900, time_struct.tm_mon+1, time_struct.tm_mday, week_day_str[time_struct.tm_wday], time_struct.tm_hour, time_struct.tm_min, time_struct.tm_sec); Serial.print(print_buf); // <DISPLAY更新> update_display(); // 時刻再取得時刻なら if (( ADJUST_HOUR == time_struct.tm_hour) && ( ADJUST_MIN == time_struct.tm_min) && ( ADJUST_SEC == time_struct.tm_sec) ) { // 一旦画面を消す(真っ暗になる) memset(disp_buf, 0x00, sizeof(disp_buf)); delay(100); // 一旦DISPLAY用タイマーはストップ timerAlarmDisable(timer); // <時刻取得> get_date_time_from_server(); delay(500); // タイマー再開(再度LEDが光り出す) timerAlarmEnable(timer); } } // 前回の秒を保存する sec_before = time_struct.tm_sec; delay(100); } ``` ArduinoIDE の右上にある▼を押し新規タブを作成し、下記のファイル名で保存してください。 ```C:AccessPoints.h #ifndef ACCESSPOINTS_H #define ACCESSPOINTS_H const AP_INFO ap_info[] = { {"SSID 1", "Password for SSID 1"}, // スマホテザリング {"SSID 2", "Password for SSID 2"}, // 自宅 LAN {"SSID 3", "Password for SSID 3"}, // 勤務先 LAN }; #endif // ACCESSPOINTS_H ``` 上記の "" で囲まれた部分を自分の環境に合わせて登録してください、必要に応じて行を増やしたり、減らしたりしてください。 # 制作写真 ### 電源レギュレータ部 ユニバーサル基板に展開するのも面倒なので、レギュレータ基板に直接表演実装部品を実装しました。 固定が困難なので、太めの熱収縮チューブでガードするようにしました。 ![レギュレータ拡大図](https://camo.elchika.com/3fdcc0dfeddccf053f7cf6ca0c091d51a77ff821/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f37363031666134662d626366362d343439662d383831342d353234353061666665663536/) ### バッファ #### パスコン パスコンはなるべく最短距離でつけるために真っ先に実装します。 ![パスコン実装](https://camo.elchika.com/3fb5e74d3162ceccc0f0fb16a40d6dd808fa9e40/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f37663236323433652d373763612d346163612d383235382d353932643539313130386335/) 20 pin 19pin の間にはプルアップ抵抗を、未使用ピンはLに、DIR は H にして A側入力B側出力にします。 ![バッファ基板完成](https://camo.elchika.com/1ac56aed37f085f79112045726cb8a1571442bab/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f36336137323733382d353539372d343032392d623064322d366433623230626131366435/) ### 基板固定 切断や穴あけが比較的簡単にできるPET製下敷きを、切って穴開けて固定しました。ショート防止の為ポリカねじも使用しています。 ![基板固定方法](https://camo.elchika.com/0692afd010c1f7b62e8adf726c23a241a03a49c2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f66323637326663362d613436642d346637622d383965642d656335383134646536643338/) ### 完成写真全体 #### 表側 100円ショップのネームプレートに固定して出来上がりです。 完成写真表側です。 ネームプレートのサイズに対して表示エリアの閉める面積が小さいのがデザイン的には難点。まぁ、ジャンクディスプレイなのでしょうがないですね。 ![完成写真表側](https://camo.elchika.com/826b46712886f485564aa7ec2da2ed9e68c9a648/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f35373162363435632d353035322d343236382d613961302d383130303733303032303630/) #### 裏側 完成写真裏側です。意外とすっきりして、表からほとんど見えません。 ![完成写真裏側](https://camo.elchika.com/4bfa2d45619b36eaab93327bcde264551f651ccc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39636634393335312d326365662d343030322d383838372d3962356664656261383735662f31353535646233372d663866622d343436652d393035642d623737303430343965313035/) # 終わりに 作品としては単純に見えて、テクニカルな部分やノウハウが必要な部分は結構あります。 1日もあれば出来ると思いきや、なんだかんだと3日程かかってしまいました。 それぞれ、細かく書きたいところではありますが、記事を見る人のレベルが広そうなので、どのレベルで書くべきか悩みました。 ある程度電子工作やArduinoIDE に経験のある人向けの記事になってしましました。 質問点や不足情報など指摘・要望があれば追記しますが、気長にお待ちいただければと思います。