Arduino UNO R4 WiFiを自動時刻合わせ機能つきの時計にする

Arduino UNO R4

ちょっと応用

ここまでのおさらいとして、Arduino UNO R4 WiFiで搭載されたWiFi機能、RTC機能、LEDマトリクスをすべて使って、デジタル時計を作ってみました。

プログラム

#include "Arduino_LED_Matrix.h"
#include "WiFiS3.h"
#include "RTC.h"

#include "arduino_secrets.h"
char ssid[] = _SSID;
char pass[] = _PASS;

// NTPアクセス関係
//char ntpServer[] = "pool.ntp.org";
IPAddress ntpServer(192,168,0,22);
int ntpPort = 123;

// 送受信用バッファの用意
const int NTP_PACKET_SIZE = 48;
byte buffer[NTP_PACKET_SIZE];

// クライアントのインスタンスの生成
WiFiUDP client;

// LEDマトリクスのインスタンスの生成
ArduinoLEDMatrix matrix;

// 表示のためのパターン
byte frame[8][12];
byte charPattern[10][5][3]={
  { {0,1,0}, {1,0,1}, {1,0,1}, {1,0,1}, {0,1,0} },  //0
  { {0,1,0}, {0,1,0}, {0,1,0}, {0,1,0}, {0,1,0} },  //1
  { {1,1,1}, {0,0,1}, {1,1,1}, {1,0,0}, {1,1,1} },  //2
  { {1,1,1}, {0,0,1}, {1,1,1}, {0,0,1}, {1,1,1} },  //3
  { {1,0,1}, {1,0,1}, {1,1,1}, {0,0,1}, {0,0,1} },  //4
  { {1,1,1}, {1,0,0}, {1,1,1}, {0,0,1}, {1,1,1} },  //5
  { {1,0,0}, {1,0,0}, {1,1,1}, {1,0,1}, {1,1,1} },  //6
  { {1,1,1}, {0,0,1}, {0,1,0}, {0,1,0}, {0,1,0} },  //7
  { {1,1,1}, {1,0,1}, {1,1,1}, {1,0,1}, {1,1,1} },  //8
  { {1,1,1}, {1,0,1}, {1,1,1}, {0,0,1}, {0,0,1} }   //9
};

void setup() {
  Serial.begin(9600);
  delay(1000);
  
  // WiFiモジュールの存在確認
  if(WiFi.status() == WL_NO_MODULE){
    Serial.println("WiFiモジュールがありません");
    while(true);
  }

  // WiFiファームウェアバージョンの確認
  String fv = WiFi.firmwareVersion();
  if(fv < WIFI_FIRMWARE_LATEST_VERSION){
    Serial.println("ファームウェアが最新のものではありません");
  }

  // 無線LANへの接続
  Serial.print("接続中...");
  while(true){
    if(WiFi.begin(ssid, pass) == WL_CONNECTED)break;
    Serial.print(".");
    delay(1000);
  }
  Serial.println("完了");

  // Arduinoに割り当てられたIPアドレスの確認
  Serial.print("IPアドレス:");
  Serial.println(WiFi.localIP().toString());

  // UDPポートを開く
  client.begin(ntpPort);

    // NTPサーバに対してリクエストを送信
  memset(buffer, 0, NTP_PACKET_SIZE);
  buffer[0] = 0b00001011;
  client.beginPacket(ntpServer, ntpPort);
  client.write(buffer, NTP_PACKET_SIZE);
  client.endPacket();
  Serial.println("リクエストを送信");
  // NTPサーバからのレスポンスを受信
  unsigned long transTime;
  while(true){
    if(client.parsePacket()){
      client.read(buffer, NTP_PACKET_SIZE);
      // 時刻(Transmit Timestamp)の取得
      transTime = buffer[40]<<24 | buffer[41]<<16 | buffer[42]<<8 | buffer[43];
      break;
    }
  }
  Serial.println("時刻を受信");
  // RTC開始と時刻の設定
  RTC.begin();
  unsigned long unixTime = transTime - 2208988800L + 32400L;
  RTCTime time(unixTime);
  RTC.setTime(time);

  // 表示マトリクスの初期化
  matrix.begin();
}

void putCharacter(int x0, int y0, int ch){
  for(int y=0;y<5;y++){
    for(int x=0;x<3;x++){
      frame[y0+y][x0+x]=charPattern[ch][y][x];
    }
  }
}

void loop() {
  RTCTime currentTime;
  RTC.getTime(currentTime);

  int hh = currentTime.getHour();
  int mm = currentTime.getMinutes();
  int ss = currentTime.getSeconds();

  memset(frame,0,96);
  putCharacter(0,1,hh/10);
  putCharacter(3,1,hh%10);
  putCharacter(6,1,mm/10);
  putCharacter(9,1,mm%10);
  for(int i=0;i<ss/5;i++){
    frame[7][i]=1;
  }
  if(ss%2==0){
    frame[7][ss/5]=1;
  }
  matrix.renderBitmap(frame,8,12);
  char tmp[10];
  sprintf(tmp,"%02d:%02d:%02d",hh,mm,ss);
  Serial.println(tmp);
  delay(1000);
}

プログラムの解説

このプログラムでは、開始時にネットワークに接続して自動的にNTPサーバから現在時刻を取得します。それ以降の時刻のカウントは内蔵のRTC機能を使用しています。

ネットワークに接続してNTPサーバから時刻を取得する部分は以前のプログラムと同じなので、新しい部分のみ解説します。

NTPサーバの指定
// NTPアクセス関係
//char ntpServer[] = "pool.ntp.org";
IPAddress ntpServer(192,168,0,22);
int ntpPort = 123;

以前からある部分ですが補足です。接続先のNTPサーバを指定するグローバル変数 ntpServerchar[]型の文字列でサーバ名(FQDN)を指定するか、または IPAddress型でIPv4アドレスを指定します。実際にデータの送信先を指定する beginPacket()関数が引数がchar[]型でもIPAddress型でも動作する(オーバーロード定義されている)ので、NTPサーバの変更はこの行の変更だけですみます。

ちなみに 192.168.0.22 というのはブログ主の実験環境(LAN)内のWindows PCです。以前の記事『Windows PCをNTPサーバにする』のやり方で実験用サーバにしています。

文字パターンの定義
// 表示のためのパターン
byte frame[8][12];
byte charPattern[10][5][3]={
  { {0,1,0}, {1,0,1}, {1,0,1}, {1,0,1}, {0,1,0} },  //0
  { {0,1,0}, {0,1,0}, {0,1,0}, {0,1,0}, {0,1,0} },  //1
  { {1,1,1}, {0,0,1}, {1,1,1}, {1,0,0}, {1,1,1} },  //2
  { {1,1,1}, {0,0,1}, {1,1,1}, {0,0,1}, {1,1,1} },  //3
  { {1,0,1}, {1,0,1}, {1,1,1}, {0,0,1}, {0,0,1} },  //4
  { {1,1,1}, {1,0,0}, {1,1,1}, {0,0,1}, {1,1,1} },  //5
  { {1,0,0}, {1,0,0}, {1,1,1}, {1,0,1}, {1,1,1} },  //6
  { {1,1,1}, {0,0,1}, {0,1,0}, {0,1,0}, {0,1,0} },  //7
  { {1,1,1}, {1,0,1}, {1,1,1}, {1,0,1}, {1,1,1} },  //8
  { {1,1,1}, {1,0,1}, {1,1,1}, {0,0,1}, {0,0,1} }   //9
}

変数 frame[][] はLEDマトリクス全体の点滅情報を格納する配列です。今回は一度に1画面分のデータしか使わないので、メモリ効率は悪いですが直感的に判りやすいように12×8の配列を用意しました。

変数 charPettern[][][] は数字の点滅パターン(フォントデータ)です。1文字3×5のデータです。

文字を描画する
void putCharacter(int x0, int y0, int ch){
  for(int y=0;y<5;y++){
    for(int x=0;x<3;x++){
      frame[y0+y][x0+x]=charPattern[ch][y][x];
    }
  }
}

数字を1つ描画します。引数x0、y0が表示位置、chが表示する数字を表します。
動作は単純で、LEDの点滅情報を charPattern[][][] から frame[][] の該当位置にコピーしているだけです。

loop()関数
void loop() {
  RTCTime currentTime;
  RTC.getTime(currentTime);

まず、RTC.getTime(currentTime) で変数currentTimeに現在時刻を取得します。

  int hh = currentTime.getHour();
  int mm = currentTime.getMinutes();
  int ss = currentTime.getSeconds();

変数currentTimeから、getHour()関数getMinutes()関数getSeconds()関数でそれぞれ時・分・秒を取得します。

  memset(frame,0,96);
  putCharacter(0,1,hh/10);
  putCharacter(3,1,hh%10);
  putCharacter(6,1,mm/10);
  putCharacter(9,1,mm%10);

memset()関数は変数frame[][]の要素の値をすべて0にしています。画面クリアに相当します。

先に定義したputCharacter()関数で数字を1文字ずつ描画します。
hh/10は『時』の十位
hh%10は『時』の一の位
mm/10は『分』の十位
mm%10は『分』の一の位
を計算しています。

  for(int i=0;i<ss/5;i++){
    frame[7][i]=1;
  }
  if(ss%2==0){
    frame[7][ss/5]=1;
  }

この部分では、画面下のバーを描画しています。1つめのfor文がバー本体、2つめのfor文が点滅しているバーの先端部分を描画しています。

  matrix.renderBitmap(frame,8,12);

renderBitmap()関数で変数frame内の点滅情報を実際のLEDマトリクスに表示します。

  char tmp[10];
  sprintf(tmp,"%02d:%02d:%02d",hh,mm,ss);
  Serial.println(tmp);
  delay(1000);
}

この部分では、動作確認用に時刻を『hh:mm:ss』の形式でシリアル送信しています。

実行結果

LEDマトリクスは横に12ピクセル、数字を表示するにはどうしても幅3ピクセルは必要なので、文字同士がくっついてしまって読みにくいですが、それでも時刻がちゃんと表示されています。

一番下のバーは秒針代わりです。12ピクセルなので5秒に1ピクセルずつ伸びていきます。

まとめ

今回はArduino UNO R4 WiFi の独自機能を一通り使用する簡単なサンプルとして、『NTPから自動的に時刻を取得する時計』を作ってみました。さらに定期的にNTPサーバから時刻を取得して正確さを保つ、『Arduinoで音を出す』を参考にしてアラーム機能を付けるなどしても面白いかもしれません。

コメント

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