4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ESP8266 から MQTT を 安全に利用する。

Last updated at Posted at 2019-03-24

初めに

ESP8266 で MQTT を利用する記事はたくさん有ったのですが、TLS を使用して安全に利用する方法はすぐに出てこなかったので、まとめます。
今回は、TLSを利用しますが、クライアント証明書は鍵の管理が面倒なので、利用しません。

環境

MQTT ブローカー

  • mosquitto 1.5.8
    • ポート 8883 で待ち受け
    • Let’s Encrypt の証明書で TLS を有効化済み
    • ユーザ/パスワード で認証

ハードウェア

  • ESP-WROOM-02
    • 共立電子で購入した 4MB 版
  • Arduino で使います

Arduino のコード解説

コード自体は最後に乗せています。

// WiFi
#define STASSID "wlanAP"
#define STAPSK "wlanPSK"

// MQTT
#define MQTT_SERVER "mqtt.hoge.fuga"
#define MQTT_PORT 8883
#define MQTT_USER "username"
#define MQTT_PASS "password"
#define MQTT_PUB_TOPIC "topicname"
#define MQTT_SUB_TOPIC "topicname"
#define MQTT_ID_PREFIX "iotdevice-"

// NTP
#define NTP_SERVER_PRIMARY "ntp.nict.jp"
#define NTP_SERVER_SECONDARY "pool.ntp.org"
#define NTP_UTC_OFFSET 60 * 60 * 9
  • WIFIの設定など
  • MQTTの認証情報など
    • MQTT_PUB_TOPIC MQTT_SUB_TOPIC は、利用するトピック名を指定してください。
    • MQTT_ID_PREFIX は、この文字列の後ろにMACアドレスを付加した物をMQTTのIDとして利用します。
  • NTPの設定など
    • 証明書の検証に必要なので、NTPで時刻を合わせます。
    • 必要に応じてUTCとの時差やNTPサーバを変更してください。
// CA
// DST_Root_CA_X3.pem
// notBefore=Sep 30 21:12:19 2000 GMT
// notAfter=Sep 30 14:01:15 2021 GMT
static const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----

-----END CERTIFICATE-----
)EOF";
  • Let’s Encrypt のルート証明書です。
    • Let’s Encrypt 以外の場合は適宜変えてください。
    • 記載のものは2021年までなので、更新が必要な場合があるかもしれません。
void loop()
{
    if (!mqtt.connected() && MQTTConnect())
    {
        // 接続成功
        mqtt.subscribe(MQTT_SUB_TOPIC);
        MQTTPubStr(MQTT_ID_PREFIX + getMacAddr() + " WakeUP!");
    }
    mqtt.loop();
}
  • 接続時と再接続時に "MQTT_ID_PREFIX MACアドレス WakeUP!" を Publish します。
  • MQTTPubStr で文字列を簡単に Publish できます。
// イベントが来た際に呼ばれる
void MQTTHandle(char *topic, byte *payload, unsigned int length)
{
}

Arduino のコード

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <time.h>
#include <PubSubClient.h>

// WiFi
#define STASSID "wlanAP"
#define STAPSK "wlanPSK"

// MQTT
#define MQTT_SERVER "mqtt.hoge.fuga"
#define MQTT_PORT 8883
#define MQTT_USER "username"
#define MQTT_PASS "password"
#define MQTT_PUB_TOPIC "topicname"
#define MQTT_SUB_TOPIC "topicname"
#define MQTT_ID_PREFIX "iotdevice-"

// NTP
#define NTP_SERVER_PRIMARY "ntp.nict.jp"
#define NTP_SERVER_SECONDARY "pool.ntp.org"
#define NTP_UTC_OFFSET 60 * 60 * 9

// CA
// DST_Root_CA_X3.pem
// notBefore=Sep 30 21:12:19 2000 GMT
// notAfter=Sep 30 14:01:15 2021 GMT
static const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
)EOF";

BearSSL::WiFiClientSecure wifiClient;
BearSSL::X509List cert(ca_cert);
PubSubClient mqtt(wifiClient);

void setup()
{
    Serial.begin(115200);
    Serial.println("Booting");

    // Wi-Fi接続
    WiFi.mode(WIFI_STA);
    WiFi.begin(STASSID, STAPSK);
    while (WiFi.waitForConnectResult() != WL_CONNECTED)
    {
        Serial.println("Connection Failed! Rebooting...");
        delay(5000);
        ESP.restart();
    }
    Serial.println("Ready");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    // NTPで時刻合わせ
    setTimeNTP();

    // LetsEncryptの中間証明を読み込む
    wifiClient.setTrustAnchors(&cert);

    // MQTTの設定
    mqtt.setServer(MQTT_SERVER, MQTT_PORT);
    mqtt.setCallback(MQTTHandle);
}

void loop()
{
    if (!mqtt.connected() && MQTTConnect())
    {
        // 接続成功
        mqtt.subscribe(MQTT_SUB_TOPIC);
        MQTTPubStr(MQTT_ID_PREFIX + getMacAddr() + " WakeUP!");
    }
    mqtt.loop();
}

// MQTTブローカに接続を行う
bool MQTTConnect()
{
    Serial.print("Waiting MQTT connection...");
    char buf[50];
    (MQTT_ID_PREFIX + getMacAddr()).toCharArray(buf, 50);
    if (mqtt.connect(buf, MQTT_USER, MQTT_PASS))
    {
        Serial.println("connected");
        return true;
    }
    else
    {
        Serial.print("failed, rc=");
        Serial.println(mqtt.state());
        return false;
    }
}

// イベントが来た際に呼ばれる
void MQTTHandle(char *topic, byte *payload, unsigned int length)
{
}

// 文字列を投げる
void MQTTPubStr(String msg)
{
    char buf[1024];
    msg.toCharArray(buf, 1024);
    mqtt.publish(MQTT_PUB_TOPIC, buf);
}

// NTPで時刻合わせ
void setTimeNTP()
{
    Serial.print("Waiting NTP...");
    configTime(NTP_UTC_OFFSET, 0, NTP_SERVER_PRIMARY, NTP_SERVER_SECONDARY);
    // NTPの取得が終わるまで終わるまで待つ
    // 0と比較すると起動してからのカウントアップがあるので、ループから抜けてしまう
    while (time(NULL) < 365 * 24 * 60 * 60)
    {
        Serial.print(".");
        delay(1000);
    }
    Serial.println(getTime());
}

// 現在時刻を文字列で得る
String getTime()
{
    time_t t = time(NULL);
    struct tm timeinfo;
    gmtime_r(&t, &timeinfo);
    return asctime(&timeinfo);
}

// Macアドレスを文字列で得る
String getMacAddr()
{
    byte mac[6];
    char buf[50];
    WiFi.macAddress(mac);
    sprintf(buf, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return String(buf);
}
4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?