M5StackでDNS設定を行う

こんにちは。ビーグルソフトの真鍋です。

このところ、コンサル案件で現場を可視化するため、M5Goにセンサーを取り付けてセンサーデータを集めるべくプログラムをゴリゴリ書いています。

M5Goを活用した仕組み(GenbaScope)の紹介

センサーとしては、M5Goに最初から付いているENV.Ⅲ、TOC/eCO2センサーを取り付けています。写真のとおりENV.Ⅲで収集した温度と湿度はM5Goでグラフに出したりしています。

一方で、M5Goはあくまでセンサー端末なので派手さはありつつも本来の目的はセンサーデータを収集してサーバーへ送信することです。M5GoはさくらのVPSで操作するFlaskへデータを送信しPostgreSQLに保存しています。

この通り、M5Goからさくらのサーバーへデータを送信しているのですが、DNSサーバーが見つからないというエラーが定期的に発生しデータのロストが課題となっていました。

DNSを設定する

M5Goは、Wi-Fiに接続してデータをHTTPSで送信しています。まだ実験中ということもありMQTTなどの軽量な仕組みを利用していません。HTTPSで送信するかにかかわらず、ドメインを指定しています。もちろん、IPアドレスを固定すればいいでしょうが、サーバーは増えることもあるためドメイン指定としています。そうすると、IPを引くためにDNSの設定が必要となります。

では、どのようにDNSサーバーを設定しているかです。これは以下の通り、WiFi.beginメソッドで接続をしていることから、DHCPサーバーから割り振られたDNSサーバが設定されます。

void connectWiFi(DeviceConfig& config)
{
    WiFi.begin(config.ssid.c_str(), config.password.c_str());
    unsigned long start = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - start < 100000)
    {
        delay(500);
        Serial.print(".");
    }

    if (WiFi.status() == WL_CONNECTED)
    {
        Serial.println("\nWiFi connected");
        Serial.println(WiFi.localIP());
    }
    else
    {
        Serial.println("\nWiFi failed to connect");
    }

    printDnsSettings();
}
Code language: C++ (cpp)

自宅で利用している場合には、DNSサーバーはゲートウェイを指定しています。しかし、不安定なので、Googleの"8.8.8.8"と"8.8.4.4"を指定して様子を見たいと考えています。

DNSだけを変更できない

このWiFiですが、以下の通りDNSだけを変更する事ができません。変更するにはIPアドレスも含めて変更する必要があります。

WiFi.config(ip);
WiFi.config(ip, dns);
WiFi.config(ip, dns, gateway);
WiFi.config(ip, dns, gateway, subnet);
Code language: C++ (cpp)

docs.arduino.cc/libraries/wifi/#WiFi.config()

これだと、適当なIPアドレスを指定するわけにはもちろんいきません。取得したIPアドレスを再設定するのも、なんだか違和感があります。いちばんスッキリする方法はDNSサーバーだけ指定することですよね。

さてどうしたものかと調べたところWiFi.onEventでIP取得時にイベントを発火できることを知りました。

このタイミングであればDNSを設定することもできるので、こちらを利用して設定するようにしました。

ESP-NETIF - ESP32 - — ESP-IDF Programming Guide v4.4 documentation

こうすることで、DNSサーバーの指定だけを行うことができました。

void onGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
{
    esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
    esp_netif_dns_info_t dnsInfo = {};

    // プライマリDNS
    dnsInfo.ip.u_addr.ip4.addr = inet_addr(PRIMARY_DNS);
    esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dnsInfo);
    // セカンダリDNS
    dnsInfo.ip.u_addr.ip4.addr = inet_addr(SECONDARY_DNS);
    esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dnsInfo);
}

void printDnsSettings()
{
    // WiFi 接続済みであることを前提とします
    IPAddress dns1 = WiFi.dnsIP(0); // プライマリ DNS
    IPAddress dns2 = WiFi.dnsIP(1); // セカンダリ DNS

    Serial.print("Primary DNS: ");
    Serial.println(dns1);
    Serial.print("Secondary DNS: ");
    Serial.println(dns2);
}

void connectWiFi(DeviceConfig& config)
{
    WiFi.onEvent(onGotIP, ARDUINO_EVENT_WIFI_STA_GOT_IP);
    WiFi.begin(config.ssid.c_str(), config.password.c_str());
    unsigned long start = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - start < 100000)
    {
        delay(500);
        Serial.print(".");
    }

    if (WiFi.status() == WL_CONNECTED)
    {
        Serial.println("\nWiFi connected");
        Serial.println(WiFi.localIP());
    }
    else
    {
        Serial.println("\nWiFi failed to connect");
    }

    printDnsSettings();
}
Code language: C++ (cpp)

これに併せて、マルチタスク化によるSDカードへのログ記録も行うようにしたので、HTTPリクエストが失敗したときのエラーログも残るようになりました。

さいごに

今回ここまで自力で調べたわけではなく、ChatGPTに相談しつつ進めました。以前であればStackOverflowで関係あるかわからない情報をひたすら探して断片を組み合わせることが多かったのですが、生成AIのおかげで実現したいことまでの距離が短くなりました。

一方で、情報処理技術者試験や放送大学の授業で学んだ基礎的な知識があるおかげで実現することの概念が理解できるため、実装を直感的に理解できるなと思います。

コンサルティング先で活用できそうなデータを集める手段を準備することで、とりあえず試すことができるようになる日も近そうです👍