マイコンをはじめよう~水槽管理システム STEP2時刻取得編~

IoT

お次のテーマ

ここで一度LEDからは離れて時刻取得とOTAを入れてみたいと思います。

LEDとCo2管理は回路周りがめんどいんです。。。

思ったより長くなってしまったのでまずは時刻取得だけやってみます。

時刻取得

早速ですがサンプルコードです。

#include <WiFi.h>       // Wifiを使うライブラリを持ってきます
#include "time.h"       // 時刻を扱うためのライブラリをもってきます

// Wi-FiのSSIDとパスワードを定義
#define WIFI_SSID     "YOUR_SSID"           // 皆さんのおうちのWifiの名前をいれましょう
#define WIFI_PASSWORD "YOUR_PASSWORD"       // wifiに入るためのパスワードを書きましょう

// NTPサーバー設定
#define NTP_SERVER "pool.ntp.org"           // このサーバーから時刻を取得します
#define GMT_OFFSET_SEC (9 * 3600)           // 日本標準時(JST: UTC+9)…こうすれば日本の時刻にできます
#define DAYLIGHT_OFFSET_SEC 0               // サマータイム用なので日本では不要。0にする。

void setup() {
    Serial.begin(115200);
    
    // Wi-Fi接続
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);   // wifiに接続します
    Serial.print("Wi-Fiに接続中");
    while (WiFi.status() != WL_CONNECTED) { // Wifiが接続されるまで待ちます (接続するとWiFi.status()がWL_CONNECTEDになります)
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWi-Fi 接続完了!");

    // NTPの時刻を取得
    configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);    // defineで設定したパラメータで、こういう時刻が欲しい!と設定します。
    Serial.println("時刻同期中...");

    struct tm timeinfo;         // time.hで定義されたtmというデータ構造があった、その形でtimeinfoというものを定義します。
    if (!getLocalTime(&timeinfo)) {     // getLocalTimerという関数に、「時刻を取得してここにtimeinfoがあるから入れておいてください」といいます。成功したらTrueが戻ります。
        Serial.println("時刻の取得に失敗しました");
        return;                 // 失敗したらあきらめてloop()に移ります
    }

    // 現在時刻を表示
    Serial.println("現在時刻:");
    Serial.printf("%04d-%02d-%02d %02d:%02d:%02d\n", 
                  timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                  timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}

void loop() {
    // ここから下は10秒ごとにsetup()でやっていた時刻取得を繰り返すだけ
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.printf("現在時刻: %04d-%02d-%02d %02d:%02d:%02d\n", 
                      timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                      timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
    }
    delay(10000);  // 10秒ごとに更新
}

なんかいっぱい書かれていますが、だいたい状態表示のためのシリアル通信です。

そこを削ってシンプルにしてみましょう

#include <WiFi.h>       // Wifiを使うライブラリを持ってきます
#include "time.h"       // 時刻を扱うためのライブラリをもってきます

// Wi-FiのSSIDとパスワードを定義
#define WIFI_SSID     "YOUR_SSID"           // 皆さんのおうちのWifiの名前をいれましょう
#define WIFI_PASSWORD "YOUR_PASSWORD"       // wifiに入るためのパスワードを書きましょう

// NTPサーバー設定
#define NTP_SERVER "pool.ntp.org"           // このサーバーから時刻を取得します
#define GMT_OFFSET_SEC (9 * 3600)           // 日本標準時(JST: UTC+9)…こうすれば日本の時刻にできます
#define DAYLIGHT_OFFSET_SEC 0               // サマータイム用なので日本では不要。0にする。

void setup() {
    
    // Wi-Fi接続
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);   // wifiに接続します
    while (WiFi.status() != WL_CONNECTED) { // Wifiが接続されるまで待ちます (接続するとWiFi.status()がWL_CONNECTEDになります)
        delay(500);
    }

    // NTPの時刻を取得
    configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);    // defineで設定したパラメータで、こういう時刻が欲しい!と設定します。

    struct tm timeinfo;         // time.hで定義されたtmというデータ構造があった、その形でtimeinfoというものを定義します。
    if (!getLocalTime(&timeinfo)) {     // getLocalTimerという関数に、「時刻を取得してここにtimeinfoがあるから入れておいてください」といいます。成功したらTrueが戻ります。ちなみにタイムアウト時間がデフォルトで5秒に設定されています。引数で変えられます。
        return;                 // 失敗したらあきらめてloop()に移ります
    }
}

void loop() {
    // ここから下は10秒ごとにsetup()でやっていた時刻取得を繰り返すだけ
    struct tm timeinfo;
    getLocalTime(&timeinfo)
    delay(10000);  // 10秒ごとに更新

一応、コードの雑解説

loop()にの部分はsetup()でやったことの繰り返しなので、setup()部分だけみましょう

Wifi関連

#include <WiFi.h>       // Wifiを使うライブラリを持ってきます

// Wi-FiのSSIDとパスワードを定義
#define WIFI_SSID     "YOUR_SSID"           // 皆さんのおうちのWifiの名前をいれましょう
#define WIFI_PASSWORD "YOUR_PASSWORD"       // wifiに入るためのパスワードを書きましょう

void setup() {
    // Wi-Fi接続
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);   // wifiに接続します
    while (WiFi.status() != WL_CONNECTED) { // Wifiが接続されるまで待ちます (接続するとWiFi.status()がWL_CONNECTEDになります)
        delay(500);
    }

解説しようと思いましたが、コメントで十分だったので無しとします

time関連

#include "time.h"       // 時刻を扱うためのライブラリをもってきます

// NTPサーバー設定
#define NTP_SERVER "pool.ntp.org"           // このサーバーから時刻を取得します
#define GMT_OFFSET_SEC (9 * 3600)           // 日本標準時(JST: UTC+9)…こうすれば日本の時刻にできます
#define DAYLIGHT_OFFSET_SEC 0               // サマータイム用なので日本では不要。0にする。

void setup() {
    // NTPの時刻を取得
    configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);    // defineで設定したパラメータで、こういう時刻が欲しい!と設定します。

    struct tm timeinfo;         // time.hで定義されたtmというデータ構造があった、その形でtimeinfoというものを定義します。
    if (!getLocalTime(&timeinfo)) {     // getLocalTimerという関数に、「時刻を取得してここにtimeinfoがあるから入れておいてください」といいます。成功したらTrueが戻ります。ちなみにタイムアウト時間がデフォルトで5秒に設定されています。引数で変えられます。
        return;                 // 失敗したらあきらめてloop()に移ります
    }
}

まずはdefineで定義している者たち

  • NET_SERVER :
    • pool.ntp.orgというところで時刻を配信しているのでそこを指定しています
  • GMT_OFFSET_SEC :
    • GMT(グリニッジ標準時。イギリスが基準)で日本の時差分を秒で指定します。日本はGMT+9というやつなので、9×3600秒という値をしています。
  • DAYLIGHT_OFFSET_SET
    • サマータイム分のオフセットです。日本はサマータイムがないので0にします

それをconfigTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER)として、私はこういう時間情報が欲しいんです、と設定します。

次にstruct tm timeinfo;です。

tmというのがtime.hで定義されている構造体で、次のように決まっています。

struct tm {
        int     tm_sec;         //	秒
        int     tm_min;         //	分
        int     tm_hour;        //	時
        int     tm_mday;        //	日
        int     tm_mon;         //	月 (1月が0)
        int     tm_year;        //	年 (1900年が0)
        int     tm_wday;        //	曜日 (日曜日が0)
        int     tm_yday;        //	年の日 (1月1日を0とした日付表記)
        int     tm_isdst;       //	サマータイム
        long    tm_gmtoff;      //	GMTからのオフセット
};

なので、tm型のtimeinfoという変数を用意する、という意味になります。

次にこの部分です。

    if (!getLocalTime(&timeinfo)) {     // getLocalTimerという関数に、「時刻を取得してここにtimeinfoがあるから入れておいてください」といいます。成功したらTrueが戻ります。ちなみにタイムアウト時間がデフォルトで5秒に設定されています。引数で変えられます。
        return;                 // 失敗したらあきらめてloop()に移ります
    }

ここで大事なのは、getLocalTime()が実際に時刻を取得しろ!と指示するコマンドです。

そこに、結果を入れるための入れ物であるtimeinfoさんを指定しています。

ただ、timeinfoさんの前に&がついています。これはなんでしょう?

これは「timeinfoさんはここにいるよ」という、timeinfoのメモリ上のアドレスを指しています。

もう何がなんだかわからないですよね?そういう場合は、いったん「そういうもんか。。。」で済ませましょう。

コメントでも書いてありますが、ここは引数が省略されていて第2引数にミリ秒単位でタイムアウト時間を指定できます。何も書かなければ5sです。

ちなみに、getLocalTimeは下記のようになっており、戻り値にはブーリアン(TRUE/FALSE)が返ります。取得成功でTRUEです。

bool getLocalTime(struct tm * info, uint32_t ms);

そのため、以下はgetLocalTime()を!で反転させたものがTRUEなら、つまり時間取得が失敗していたらあきらめてreturnする、という意味です。

    if (!getLocalTime(&timeinfo)) {     // getLocalTimerという関数に、「時刻を取得してここにtimeinfoがあるから入れておいてください」といいます。成功したらTrueが戻ります。ちなみにタイムアウト時間がデフォルトで5秒に設定されています。引数で変えられます。
        return;                 // 失敗したらあきらめてloop()に移ります
    }

関数(setup())内でreturnが発動するとその関数を終わらせるので、すぐにloop()に移ります。

実行してみる

起動したらシリアル通信の中身を見る必要があるので、ArduinoIDEのシリアルモニターを起動します。

そうすると下の方にシリアルモニタが開きます。

こんな感じで現在時刻取得に成功しました!

次回

次回はOTA(Over the Air)です。

コメント

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