今から始める電子工作 Step3 ⑤ Wi-Fi、SPI グラフィック・ディスプレイ、RTC

 NTPによる時刻は正確ですが、いつも問い合わせをしておくと、インターネットへの通信量が膨大な回数になります。Arduino UNO R4 WiFiには、RTCが含まれています。これは時計の機能で、定期的に時刻合わせをNTPで行い、その後RTCの時刻の読みだしを行うという使い方ができます。

環境

  • Arduino IDE;2.3.5
  • Windows11;24H2
  • Arduino UNO R4 WiFi 

RTCの機能

 ドキュメントは、下記のWebページにあります。

  Arduino UNO R4 WiFi Real-Time Clock

 RTCの特徴は、この機能のために電源(VRTCピンに1.6~3.6Vの電圧)をつないでおけば、Arduino UNO R4 WiFiの電源を切っていても、時計が動き続けることです。RTCの電源は消費電流が極めて少ないので、ボタン電池などが利用できます。

接続ピンの位置

 VRTCとGNDがDCジャックの近くに用意されています。一般的なピンヘッダです。

 隣にOFFと書かれたピンがあります。下記のドキュメントに説明されています。

  Arduino UNO R4 WiFi VRTC & OFF Pins

 「OFFピンは電源を遮断してボードの電源をOFFにするために使用します」と書かれています。DCジャックもしくはUSB経由でボードに電源が供給されている状態で、その主電源をOFFにして「VRTCとGND」につながれた電池だけでRTCを動作させるという使い方に思えます。OFFピンとGNDを短絡させると、ボードの電源はOFFになります。

 このドキュメントには、「VRTC バッテリを追加してから初めてボードを起動する場合にのみ時間を設定する」スケッチは掲載されていますが、OFFした後、RTCだけが動いていて、ボードの電源をONするタイミングやスケッチはありません。

 OFFピンとGNDを短絡を解除すればいいように読み取れます。

RTCを使ってみる

 RTCのライブラリはすでに入っています。

  スケッチ例 -> RTC -> test_RTC

を読み込んでコンパイル、実行します。

 実行している様子です。

 初期値に、

  // Set a specific initial time (August 25, 2022, 14:37:00 Thursday)

   RTCTime mytime(25, Month::AUGUST, 2022, 14, 37, 00, DayOfWeek::THURSDAY, SaveLight::SAVING_TIME_ACTIVE);

を設定しているので、そこから時刻がスタートしています。と思ったのですが、違いますね。

  ドキュメントArduino UNO R4 WiFi Real-Time Clockにあるサンプルを動かします。日時は現在の値に変更しました。

#include "RTC.h"

void setup() {
  Serial.begin(9600);

  RTC.begin();
  
  RTCTime startTime(27, Month::APRIL, 2025, 20, 54, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);

  RTC.setTime(startTime);
}

void loop() {
  RTCTime currentTime;

  // Get current time from RTC
  RTC.getTime(currentTime);

  // Print out date (DD/MM//YYYY)
  Serial.print(currentTime.getDayOfMonth());
  Serial.print("/");
  Serial.print(Month2int(currentTime.getMonth()));
  Serial.print("/");
  Serial.print(currentTime.getYear());
  Serial.print(" - ");

  // Print time (HH/MM/SS)
  Serial.print(currentTime.getHour());
  Serial.print(":");
  Serial.print(currentTime.getMinutes());
  Serial.print(":");
  Serial.println(currentTime.getSeconds());

  delay(1000);
}

 実行している様子です。

 正常に設定し、読み出せているようですね。

NTPで時刻を取得してRTCに設定

 NTPの時刻に同期させて動くサンプルを動かします。

  RTC -> RTC_NTPSync.inoです。

 コンパイルして動かします。起動したのちresetボタンを押します。

グラフィック・ディスプレイに表示

 前回作成した時計のスケッチと、上記のスケッチとを合体させます。

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include <Fonts/FreeSerif9pt7b.h> #define TFT_CS 10 #define TFT_RST 9 // Or set to -1 and connect to Arduino RESET pin #define TFT_DC 8 #include <NTPClient.h>
#include <WiFiS3.h> // for WiFi shield
#include <WiFiUdp.h> #include "arduino_secrets.h" char ssid[] = SECRET_SSID; // your network SSID (name) char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP) Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP); #include "RTC.h" int x10,y10,x10s,y10s,x10ss,y10ss,x10sss,y10sss,x10ssss,y10ssss; int x20,y20,x20s,y20s,x20ss,y20ss,x30,y30,x30s,y30s; int x11,y11,x11s,y11s,x11ss,y11ss,x11sss,y11sss,x11ssss,y11ssss; int x21,y21,x21s,y21s,x21ss,y21ss,x31,y31,x31s,y31s; void setup(){ Serial.begin(9600); delay(2000); Serial.println("\nstart Wi-Fi connect"); tft.init(240, 320); // Init ST7789 320x240 Serial.println(F("Initialized TFT")); tft.fillScreen(ST77XX_BLACK); tft.setRotation(3); tft.setFont(&FreeSerif9pt7b); tft.setCursor(0, 0); tft.fillRect(0,0,3,3,ST77XX_WHITE); Serial.println(); WiFi.begin(ssid, pass); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); Serial.print ( "." ); } timeClient.begin(); timeClient.update(); delay(100); timeClient.update(); Serial.print(" NTP time: "); Serial.println(timeClient.getFormattedTime()); // connectToWiFi(); RTC.begin(); auto timeZoneOffsetHours = 9; auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600); Serial.print("Unix time = "); Serial.println(unixTime); RTCTime timeToSet = RTCTime(unixTime); RTC.setTime(timeToSet); // Retrieve the date and time from the RTC and print them RTCTime currentTime; RTC.getTime(currentTime); Serial.println("The RTC was just set to: " + String(currentTime)); Serial.println(""); } void loop() { unsigned long time0, time; time0 = millis(); RTCTime currentTime; RTC.getTime(currentTime); // Print out date (DD/MM//YYYY) Serial.print(currentTime.getYear()); Serial.print("/"); Serial.print(Month2int(currentTime.getMonth())); Serial.print("/"); Serial.print(currentTime.getDayOfMonth()); Serial.print(" - "); int H = currentTime.getHour() ; Serial.print(H); int M = currentTime.getMinutes(); Serial.print(":"); Serial.print(M); int S = currentTime.getSeconds(); Serial.print(":"); Serial.println(S); tft.fillRect(0,20,70,20,ST77XX_BLACK); tft.setCursor(3, 35); tft.setTextColor(ST77XX_YELLOW); tft.setTextSize(1); tft.print(H); tft.print(":"); tft.print(M); tft.print(":"); tft.print(S); tft.drawCircle(tft.width()/2, tft.height()/2, 118, ST77XX_WHITE); tft.drawCircle(tft.width()/2, tft.height()/2, 118-1, ST77XX_YELLOW); tft.drawCircle(tft.width()/2, tft.height()/2, 118-2, ST77XX_WHITE); tft.drawCircle(tft.width()/2, tft.height()/2, 2, ST77XX_WHITE); float angle; int X =tft.width()/2; int Y =tft.height()/2; //hour ticks for( int z= 0; z < 360; z = z + 30 ){ //Begin at 0° and stop at 360° angle = z ; angle=(angle/57.29577951) ; //Convert degrees to radians int x2=(X+(sin(angle)*116)); int y2=(Y-(cos(angle)*116)); int x3=(X+(sin(angle)*(116-10))); int y3=(Y-(cos(angle)*(116-10))); int x4=(X+(sin(angle+0.5)*116)); int y4=(Y-(cos(angle+0.5)*116)); int x5=(X+(sin(angle+0.5)*(116-10))); int y5=(Y-(cos(angle+0.5)*(116-10))); tft.drawLine(x2,y2,x3,y3,ST77XX_RED); tft.drawLine(x4,y4,x5,y5,ST77XX_RED); } // display second hand angle = S * 6.0 ; angle=(angle/57.29577951) ; //Convert degrees to radians x30=(X+(sin(angle)*(116))); y30=(Y-(cos(angle)*(116))); x30s=(X+(sin(angle)*(116-20))); y30s=(Y-(cos(angle)*(116-20))); if (x30 != x31) { tft.drawLine(X,Y,x31,y31,ST77XX_BLACK); tft.drawCircle(x31s, y31s, 7, ST77XX_BLACK); } if (abs(cos(angle)*(116)) < 24.2) { tft.drawLine(X,Y,x31,y31,ST77XX_BLACK); tft.drawCircle(x31s, y31s, 7, ST77XX_BLACK); } tft.drawLine(X,Y,x30,y30,ST77XX_GREEN); tft.drawCircle(x30s, y30s, 7, ST77XX_GREEN); x31=x30;x31s=x30s; y31=y30;y31s=y30s; // display minute hand angle = M * 6 ; angle=(angle/57.29577951) ; //Convert degrees to radians x20=(X+(sin(angle)*(116-15))); y20=(Y-(cos(angle)*(116-15))); x20s=(X+(sin(angle+0.02)*(116-26))); y20s=(Y-(cos(angle+0.02)*(116-26))); x20ss=(X+(sin(angle-0.02)*(116-26))); y20ss=(Y-(cos(angle-0.02)*(116-26))); if (x20 != x21) { tft.drawLine(X,Y,x21,y21,ST77XX_BLACK); tft.drawLine(X+1,Y+1,x21s,y21s,ST77XX_BLACK); tft.drawLine(X-1,Y-1,x21ss,y21ss,ST77XX_BLACK); tft.drawLine(x21,y21,x21s,y21s,ST77XX_BLACK); tft.drawLine(x21,y21,x21ss,y21ss,ST77XX_BLACK); } if (abs(cos(angle)*(116-15)) < 24.2) { tft.drawLine(X,Y,x21,y21,ST77XX_BLACK); tft.drawLine(X+1,Y+1,x21s,y21s,ST77XX_BLACK); tft.drawLine(X-1,Y-1,x21ss,y21ss,ST77XX_BLACK); tft.drawLine(x21,y21,x21s,y21s,ST77XX_BLACK); tft.drawLine(x21,y21,x21ss,y21ss,ST77XX_BLACK); } tft.drawLine(X,Y,x20,y20,ST77XX_WHITE); tft.drawLine(X+1,Y+1,x20s,y20s,ST77XX_WHITE); tft.drawLine(X-1,Y-1,x20ss,y20ss,ST77XX_WHITE); tft.drawLine(x20,y20,x20s,y20s,ST77XX_WHITE); tft.drawLine(x20,y20,x20ss,y20ss,ST77XX_WHITE); x21=x20;x21s=x20s;x21ss=x20ss; y21=y20;y21s=y20s;y21ss=y20ss; // display hour hand angle = H * 30 + int((M / 12) * 6 ) ; angle=(angle/57.29577951) ; //Convert degrees to radians x10=(X+(sin(angle)*(116-40))); y10=(Y-(cos(angle)*(116-40))); x10s=(X+(sin(angle+0)*(116-42))); y10s=(Y-(cos(angle+0)*(116-42))); x10ss=(X+(sin(angle-0.2)*(116-70))); y10ss=(Y-(cos(angle-0.2)*(116-70))); x10sss=(X+(sin(angle+0.2)*(116-70))); y10sss=(Y-(cos(angle+0.2)*(116-70))); x10ssss=(X+(sin(angle)*(116-105))); y10ssss=(Y-(cos(angle)*(116-105))); if (x10 != x11) { tft.drawLine(X,Y,x11,y11,ST77XX_BLACK); tft.fillTriangle(x11s, y11s, x11ss, y11ss, x11sss, y11sss, ST77XX_BLACK); tft.fillTriangle(x11ssss, y11ssss, x11ss, y11ss, x11sss, y11sss, ST77XX_BLACK); } if (abs(cos(angle)*(116-42)) < 24.2) { tft.drawLine(X,Y,x11,y11,ST77XX_BLACK); tft.fillTriangle(x11s, y11s, x11ss, y11ss, x11sss, y11sss, ST77XX_BLACK); tft.fillTriangle(x11ssss, y11ssss, x11ss, y11ss, x11sss, y11sss, ST77XX_BLACK); } tft.drawLine(X,Y,x10,y10,ST77XX_WHITE); tft.fillTriangle(x10s, y10s, x10ss, y10ss, x10sss, y10sss, 0x7777); tft.fillTriangle(x10ssss, y10ssss, x10ss, y10ss, x10sss, y10sss, 0x5555); x11=x10;x11s=x10s;x11ss=x10ss;x11sss=x10sss;x11ssss=x10ssss; y11=y10;y11s=y10s;y11ss=y10ss;y11sss=y10sss;y11ssss=y10ssss; time = millis(); while ((time - time0)<1000){ // 1000ms delay(1); time = millis(); } }

 実行中の様子です。

大きな問題が

 精度が悪すぎて、時計の意味を成しません。

 確認用のスケッチです。

#include <NTPClient.h>
#include <WiFiS3.h> // for WiFi shield
#include <WiFiUdp.h> #include "arduino_secrets.h" char ssid[] = SECRET_SSID; // your network SSID (name) char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP) WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP); #include "RTC.h" void setup(){ Serial.begin(9600); delay(2000); Serial.println("\nstart Wi-Fi connect"); WiFi.begin(ssid, pass); while ( WiFi.status() != WL_CONNECTED ) { delay ( 500 ); Serial.print ( "." ); } timeClient.begin(); timeClient.update(); delay(100); timeClient.update(); Serial.print(" NTP time: "); Serial.println(timeClient.getFormattedTime()); // connectToWiFi(); RTC.begin(); auto timeZoneOffsetHours = 9; auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600); Serial.print("Unix time = "); Serial.println(unixTime); RTCTime timeToSet = RTCTime(unixTime); RTC.setTime(timeToSet); // Retrieve the date and time from the RTC and print them RTCTime currentTime; RTC.getTime(currentTime); Serial.println("The RTC was just set to: " + String(currentTime)); Serial.println(""); } void loop() { RTCTime currentTime; RTC.getTime(currentTime); Serial.print("\n NTP time: "); Serial.println(timeClient.getFormattedTime()); Serial.println("RTC " + String(currentTime)); // Print out date (DD/MM//YYYY) Serial.print(currentTime.getYear()); Serial.print("/"); Serial.print(Month2int(currentTime.getMonth())); Serial.print("/"); Serial.print(currentTime.getDayOfMonth()); Serial.print(" - "); int H = currentTime.getHour() ; Serial.print(H); int M = currentTime.getMinutes(); Serial.print(":"); Serial.print(M); int S = currentTime.getSeconds(); Serial.print(":"); Serial.println(S); delay(1000); }

 実行して約2分後の様子です。

 左側はPCの時刻です。右の一番上はNTPから取得した時刻です。同じです

 右の下二つはRTCの時刻です。2秒ずれています。このずれはどんどん拡大していきます。

 残念ながらRTCを使った時計は実用にはなりませんでした。

前へ

今から始める電子工作 Step3 ④ Wi-Fi、SPI グラフィック・ディスプレイ -円