はじめに
あの Arduino UNO がパワーアップして、しかも WiFi が搭載されたボードもあるみたいと、勢いで Arduino UNO R4 wifi を購入した方も多い方思いますが、積んでいませんか?
時間が出来たら触ろうと思っていると、そうなりますよね。
私もそうでした…
という訳で、とりあえず、積んでおくくらいなら、時計にしてしまいませんか?
ということで時計を作ってみました。
※記事中の写真にマスキングテープを張っているのは、写真写りの都合です。実際は外して使います。
準備
-
PC
Arduino IDE が動くPC (Windows / MAC / Linux) ならなんでもOK。 -
Arduino UNO R4 wifi
そもそも積んでいるはずなので探せばあると思いますが、見つからなかった場合とか失くしてしまったのなら仕方がない。
近くの電子部品屋や電子部品の通販で買えますので、 "Arduino WiFi 通販" とでも検索掛けて購入してください。 -
USB Type C コネクタ データ用 (充電用不可)
Arduino UNO R4 wifi には RTC も WiFi もディスプレイもついていますので、他に追加のハードは要りません。 楽勝ですよね。
インストール
Arduino IDE の導入します。
Arduino IDE 2. を使用する前提の手順になっていますので、まだ 1.8 シリーズを使い続けている方はこの際導入してみてもいいかもしれません。
既にインストール済みの場合は、スケッチ作成の所まで飛んでください。
ダウンロード
公式サイト URL に飛びます。
https://www.arduino.cc/en/software
赤枠の所で、自分の環境に合わせてダウンロードします。
寄付を求められますが、[JUST DOWNLOAD] を選ぶとダウンロードできます。
email アドレスを入れ、上の同意をするの方にはチェックを入れて [JUST DOWNLOAD] をクリックします。
ダウンロードが始まります。ダウンロードしたものを実行するとインストールが始まると思いますが、そのインストール方法は環境によって違うと思うので割愛します。ネットで調べましょう。
インストールが終わったら [File] - [Preferences...] で設定画面を開き、下の赤枠の箇所で言語を日本語にしておくといいと思います。
ソフト書き込み
接続
先ずはボードを差してしまいましょう。
USB Type C のケーブルで PC と Arduino UNO R4 wifi を接続します。初回はドライバーのインストールが走ると思いますので、もし、「ドライバーをインストールします」といった内容の表示が出たら、インストールが終わり「準備が出来ました」とポップが上がるまで少し待ちます。
ArduinoIDE の起動
Arduino - IDE を立ち上げます。
既に使ったことがある人は、最後に使ったスケッチが開くかもしれません。
その場合は [ファイル] - [新規スケッチ作成] を選択します。
その後、下の画面の赤枠部分を押し、[他のボードとポートを選択] を押します。
ボードの所に "UNO r4" と入れて検索し、 Arduino UNO R4 WiFi を選択します。
さらに右側のポートの所のシリアルポートを選択します。ポートは PCに Arduino しか刺さっていなければ ひとつしか選択できないはずです。
プログラムソースを貼り付け
以下のコードブロックを全て選択してから 右クリック [コピー] でコピーします。
R4-WiFi_NtpClock.ino
// -----=====<<<<<[[[[[ Comments ]]]]]>>>>>=====-----
/*
This program is based on the sample program in the Arduino Official Tutorial linked to the URL below.
[Arduino UNO R4 WiFi Real-Time Clock]
https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc/
[Using the Arduino UNO R4 WiFi LED Matrix]
https://docs.arduino.cc/tutorials/uno-r4-wifi/led-matrix/
*/
// -----=====<<<<<[[[[[ Include ]]]]]>>>>>=====-----
#include "Arduino_LED_Matrix.h"
#include "RTC.h"
#include <NTPClient.h>
#include <WiFiS3.h>
#include <WiFiUdp.h>
#include "arduino_secrets.h"
// -----=====<<<<<[[[[[ Define ]]]]]>>>>>=====-----
#define LED_PIN 13
#define JST_OFFSET 9
#define TIMEZONE_OFFSET JST_OFFSET
#define DISP_POS_HOUR_X 0
#define DISP_POS_HOUR_Y 0
#define DISP_POS_MIN_H_X 5
#define DISP_POS_MIN_L_X 9
#define DISP_POS_MIN_Y 0
#define DISP_POS_SEC_X 0
#define DISP_POS_SEC_Y 7
#define DISP_POS_A_X 0
#define DISP_POS_D_X 4
#define DISP_POS_J_X 8
#define DISP_POS_ADJ_Y 0
#define DISP_WIDTH 12
#define DISP_HEIGHT 8
#define HOUR_FONT_WIDTH 4
#define HOUR_FONT_HEIGHT 5
#define HOUR_FONT_BITS (HOUR_FONT_WIDTH * HOUR_FONT_HEIGHT)
#define MIN_FONT_WIDTH 3
#define MIN_FONT_HEIGHT 6
#define MIN_FONT_BITS (MIN_FONT_WIDTH * MIN_FONT_HEIGHT)
#define ADJ_FONT_WIDTH 4
#define ADJ_FONT_HEIGHT 7
#define ADJ_FONT_BITS (ADJ_FONT_WIDTH * ADJ_FONT_HEIGHT)
/*
#define ADJUSTING_HOUR 4
#define ADJUSTING_MIN 0
#define ADJUSTING_SECOND 0
*/
#define ADJUSTING_IN_10_MIN
#define ADJUSTING_CYCLE (10*60) // 10minites
// -----=====<<<<<[[[[[ Types ]]]]]>>>>>=====-----
// -----=====<<<<<[[[[[ Valiable ]]]]]>>>>>=====-----
ArduinoLEDMatrix matrix;
const char ssid[] = SECRET_SSID; // your network SSID (name)
const char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int wifiStatus = WL_IDLE_STATUS;
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
NTPClient timeClient(Udp);
uint32_t matrix_data[3];
static bool need_to_adjust = true;
// -----=====<<<<<[[[[[ Prototype ]]]]]>>>>>=====-----
void printWifiStatus();
void connectToWiFi();
void drawTime(RTCTime* time);
void drawAdjusting();
void setup();
void loop();
// -----=====<<<<<[[[[[ Tables ]]]]]>>>>>=====-----
const uint32_t hour_font_data[] = {
0x075557, // 0
0x011111, // 1
0x071747, // 2
0x071717, // 3
0x055711, // 4
0x074717, // 5
0x074757, // 6
0x071122, // 7
0x075757, // 8
0x075717, // 9
0x0bbbbb, // 10
0x099999, // 11
0x0b9bab, // 12
};
const uint32_t min_font_data[] = {
0x03db6f, // 0
0x009249, // 1
0x039f27, // 2
0x039e4f, // 3
0x02de49, // 4
0x03ce4f, // 5
0x03cf6f, // 6
0x039292, // 7
0x03df6f, // 8
0x03de4f, // 9
};
const uint32_t adj_font_data[] = {
0x044aaeaa, // A
0x0caaaaac, // D
0x0E444449, // J
};
void printWifiStatus(){
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
void connectToWiFi(){
Serial.println("\nStarting connection to server...");
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// Check the WiFi status
wifiStatus = WiFi.status();
// attempt to connect to WiFi network:
while (wifiStatus != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
wifiStatus = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to WiFi");
printWifiStatus();
}
void drawTime(RTCTime* time){
uint32_t font;
uint32_t bit_mask;
int8_t hour = time->getHour();
int8_t min = time->getMinutes();
int8_t sec = time->getSeconds();
int8_t x, y, hour_offset_y = (hour < 12)? 0: 1;
int8_t disp_bit_pos;
// Clear
memset(matrix_data, 0x00u, sizeof(matrix_data));
// Disp the Hour part
font = hour_font_data[((hour == 12)? 12: (hour%12))];
bit_mask = (uint32_t)(1<<(HOUR_FONT_BITS-1));
for (y = (DISP_POS_HOUR_Y + hour_offset_y); y < ( HOUR_FONT_HEIGHT + DISP_POS_HOUR_Y + hour_offset_y) ; y++) {
for (x = DISP_POS_HOUR_X; x < (DISP_POS_HOUR_X + HOUR_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Disp the Minutes higher part
font = min_font_data[min / 10];
bit_mask = (uint32_t)(1<<(MIN_FONT_BITS-1));
for (y = DISP_POS_MIN_Y; y < ( MIN_FONT_HEIGHT + DISP_POS_MIN_Y) ; y++) {
for (x = DISP_POS_MIN_H_X; x < (DISP_POS_MIN_H_X + MIN_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Disp the Minutes lower part
font = min_font_data[min % 10];
bit_mask = (uint32_t)(1<<(MIN_FONT_BITS-1));
for (y = DISP_POS_MIN_Y; y < ( MIN_FONT_HEIGHT + DISP_POS_MIN_Y) ; y++) {
for (x = DISP_POS_MIN_L_X; x < (DISP_POS_MIN_L_X + MIN_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Disp the Seconds parts
x = DISP_POS_SEC_X + ( sec * DISP_WIDTH / 60 );
y = DISP_POS_SEC_Y;
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 == ((sec % 5) & 1) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
// Send data to matrix
matrix.loadFrame(matrix_data);
}
void drawAdjusting(){
uint32_t font;
uint32_t bit_mask;
int8_t x, y;
int8_t disp_bit_pos;
// Clear
memset(matrix_data, 0x00u, sizeof(matrix_data));
// Disp the 'A' part
font = adj_font_data[0];
bit_mask = (uint32_t)(1<<(ADJ_FONT_BITS-1));
for (y = DISP_POS_ADJ_Y; y < ( ADJ_FONT_HEIGHT + DISP_POS_ADJ_Y) ; y++) {
for (x = DISP_POS_A_X; x < (DISP_POS_A_X + ADJ_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Disp the 'D' part
font = adj_font_data[1];
bit_mask = (uint32_t)(1<<(ADJ_FONT_BITS-1));
for (y = DISP_POS_ADJ_Y; y < ( ADJ_FONT_HEIGHT + DISP_POS_ADJ_Y) ; y++) {
for (x = DISP_POS_D_X; x < (DISP_POS_D_X + ADJ_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Disp the 'J' part
font = adj_font_data[2];
bit_mask = (uint32_t)(1<<(ADJ_FONT_BITS-1));
for (y = DISP_POS_ADJ_Y; y < ( ADJ_FONT_HEIGHT + DISP_POS_ADJ_Y) ; y++) {
for (x = DISP_POS_J_X; x < (DISP_POS_J_X + ADJ_FONT_WIDTH); x++ ) {
disp_bit_pos = (y * DISP_WIDTH) + x;
if ( 0 != ( font & bit_mask ) ) {
matrix_data[disp_bit_pos / ( sizeof(matrix_data[0]) * 8 )] |= (uint32_t)(0x80000000 >> (disp_bit_pos % (sizeof(matrix_data[0]) * 8 )));
}
bit_mask >>= 1;
}
}
// Send data to matrix
matrix.loadFrame(matrix_data);
}
void setup(){
// Setup serial
Serial.begin(115200);
while (!Serial);
// Setup pins
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Startup libraries
matrix.begin();
RTC.begin();
}
void loop(){
static RTCTime lastTime = {0};
static int led_on = LOW;
static int last_led_on = LOW;
static int led_on_cntdown = 5;
static int adjust_cntdown = ADJUSTING_CYCLE;
RTCTime currentTime;
// Adjusting time if needed
if ( true == need_to_adjust) {
Serial.println("\nAdjusting time now!");
drawAdjusting();
connectToWiFi();
timeClient.begin();
timeClient.update();
auto timeZoneOffsetHours = TIMEZONE_OFFSET;
auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600);
RTCTime timeToSet = RTCTime(unixTime);
RTC.setTime(timeToSet);
need_to_adjust = false;
adjust_cntdown = ADJUSTING_CYCLE;
}
// Get the current time
RTC.getTime(currentTime);
// Update display
if (lastTime.getSeconds() != currentTime.getSeconds() ) {
drawTime(¤tTime);
// Set the blink LED on
led_on = HIGH;
led_on_cntdown = 5;
lastTime = currentTime;
// Checking if it's time for adjusting
#if defined (ADJUSTING_IN_10_MIN)
if ( 0 == adjust_cntdown ) {
need_to_adjust = true;
}
else adjust_cntdown--;
#elif defined(ADJUSTING_HOUR) || defined(ADJUSTING_MIN) || defined(ADJUSTING_SECOND)
if ( true
#if defined(ADJUSTING_SECOND)
&& ( ADJUSTING_SECOND == currentTime.getSeconds() )
#endif // defined(ADJUSTING_SECOND)
#if defined(ADJUSTING_MIN)
&& ( ADJUSTING_MIN == currentTime.getMinutes() )
#endif // defined(ADJUSTING_MIN)
#if defined(ADJUSTING_HOUR)
&& ( ADJUSTING_HOUR == currentTime.getHour() )
#endif // defined(ADJUSTING_HOUR)
) {
need_to_adjust = true;
}
#endif // defined(ADJUSTING_HOUR) || defined(ADJUSTING_MIN) || defined(ADJUSTING_SECOND)
}
else {
// Set the blink LED off
if (0 == led_on_cntdown) led_on = LOW;
else led_on_cntdown --;
}
// Update the blink LED
if (last_led_on != led_on) {
digitalWrite(LED_PIN, led_on);
last_led_on = led_on;
}
delay(100);
}
[ファイル] - [名前を付けて保存] を選び、 "R4-WiFi_NtpClock" とでも付けて保存します。
秘密の情報設定
WiFi の SSID や パスワードは、うっかり流出しない様に、別ヘッダーファイルで書く様になっているようです。
先ずはそのヘッダを用意します。右上にある下記赤枠の箇所にある […] を押して
ファイル名を求められるので、 "arduino_secrets.h" と入れて [OK] を押します。
先ずは以下のプログラムを同じ様にコードエリアに貼り付けます。
arduino_secrets.h
#ifndef arduino_secrets_h
#define arduino_secrets_h
#define SECRET_SSID "" //network name
#define SECRET_PASS "" //network password
#endif // arduino_secrets_h
図の赤枠部分に、ご自身の家のWiFiルーターに合わせて、 SSID と パスワード に変更して下さい。
ライブラリの確認・導入
下の左下赤枠部分でライブラリアイコンをクリックし、そのすぐ左上のライブラリマネージャー のところに "NTPClient" と入れて検索します。
上の赤枠部分に "バージョン名 Installed" が出ていれば、インストールされていますが、下画面の様に何もなければインストールされていません。
(インストール) をクリックしてインストールします。
プログラムの書き込み
画面左上の方、下の図の赤枠部分に 右向き [→] のアイコンがあるので、押します。
エラーが無ければ、そのまま起動し、時計が表示されるはずです。
エラーが内容にプログラムを書いているつもりですが、
それでもエラーが出たら、その内容によって対処は異なるのですが、ネットで検索してみて下さい。
#使いかた
使いかたというより見方でしょうか。
12x8 という極小マトリックスに時計を表示する為、最大限の努力で表現しています。
時分表示
縦方向に1ドット短いのが時です、長い方が分です。
午前・午後の判別方法
24 時間は厳しかったので 12時間表示のみです。そして、午前、午後の見分け方ですが、時の表示は 分より縦方向に1ドット分少ないと上に書きましたが、上寄りになっているのが午前(AM) で 下寄りになっているのが 午後(PM) です。
秒表示
秒は5秒おきに位置が移動するドット表示です。横12ドットなので一番右迄行った後分が繰り上がります。
1秒ごとに点灯・消灯を繰り返します。また移動した直後は点灯になります。
秒の情報は余り重要でないので止まってない事だけが見えればいいとの考えで、小さい所に収める最大の努力をした結果でこうなってます。
時刻取得
時刻取得はネット経由で NTPサーバーから時刻を取得して同期を行うようになっています。PCの時計の同期と同じ仕組みです。
そして、取得タイミングは起動時と午前 4:00 になっています。ですので午前 4:00 前辺りが誤差が最大になるかと思います。
起動時とそれ以降10分毎としています。というのも数分で数秒ずれるほど、RTCの精度が悪いという事が判明したためです。10分でも結構ずれます...
時刻取得中には画面に "ADJ." と表示されます。
参考ソースについて
本プログラムは、下記URL で示す Arduino公式のチュートリアルの Matrix LED の記事と RTC の記事に含まれているサンプルプログラムを参考にさせていただきました。
[Arduino UNO R4 WiFi Real-Time Clock]
https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc/
[Using the Arduino UNO R4 WiFi LED Matrix]
https://docs.arduino.cc/tutorials/uno-r4-wifi/led-matrix/
おそらく Renesas 様が用意してくれたものなのかな?
この場を借りてお礼申し上げます。
最後に
半日程で作ったので、細部でまだ問題があるかもしれませんが、問題あれば修正します。
その場合コメントで報告いただければありがたいです。また要望等もあれば同じくコメントでお願いします。
使ってみたよという方は "いいねなどのリアクション" いただけれると、とてもうれしいです。
RTC の正確さが判らないので、とりあえず1日1回の時刻同期にしていますが、これから少し長時間動かしてみて足りなければ少し増やそうと思っています。
UNO R4 MINIMA も手軽に出来る何か作りたいなぁ・・・
投稿者の人気記事
-
TakSan0
さんが
2024/02/03
に
編集
をしました。
(メッセージ: 初版)
-
TakSan0
さんが
2024/02/03
に
編集
をしました。
(メッセージ: RTC精度が悪すぎるので調整周期調整)
-
TakSan0
さんが
2024/02/03
に
編集
をしました。
(メッセージ: 時刻同期の方法を少し変更)
ログインしてコメントを投稿する