MQTTを使いたい

(1)セキュリティの高いMQTT ブローカー

注: このページは NodeMCU マイコンを用いた IOT 基礎実験 の続きですので、 OSOYOO NodeMCUボード v1.0 を初めて使う方は、MQTTを利用するプログラムを作成する前に、抵抗、スイッチ、可変抵抗、トランジスタ、超音波センサ、温度センサ、OLEDモジュール、リレーモジュール、赤外線センサ、赤外線LED を NodeMCU で使用する基本的なプログラムを学ぶ必要がありますので、まず NodeMCU マイコンを用いた IOT 基礎実験 をご覧ください。 ) 

 MQTTを初めて使う方は、マイコンなどにMQTTクライアントプログラムを書き込んで、無料の MQTT broker を使い動作確認をすると思います。私も、無料の公開 MQTTブローカー
broker.emqx.io を使用しました。( broker.emqx.io の紹介ページは
https://www.emqx.com/ja/mqtt/public-mqtt5-broker です。この紹介ページの下部の「オンラインで使用」をクリックすると使用のための設定ができます )

 ただ動作確認が済んで、リレーモジュールなどのコントロールに常時使用したいとなると、無料の公開MQTTブローカーではセキュリティの心配があります。トピックが偶然にも他の利用者と一致したとき、その人の通信データを読めたり、逆にこちらのデータを読まれたりしますよね。そこで、セキュリティを確保するためMQTT ではどのような認証方法が使われているか調べてみました。

 MQTTの認証方法として主に下記の2つの方法がありました。
(1)ユーザー名とパスワード認証
 MQTTクライアントは、接続時に認証情報としてユーザー名とパスワードをブローカーに送信し、ブローカーはそれらを検証します。
(2)TLS/SSL による認証
 TLS/SSL 証明書を使用して、クライアントとブローカー間の通信を暗号化し、認証を行います。 MQTTS ( MQTT over TLS/SSL ) は、この方法でセキュリティを強化したMQTTプロトコルです。

 この2つの認証方法を使用でき、無料で使用できそうな MQTTブローカーを探してみました。

〇 無料で公開されているブローカー、 broker.emqx.io の
ユーザー名とパスワードにようる認証について
 無料で公開されているブローカー、 broker.emqx.io はユーザー名とパスワードによる認証はできないようですね。

〇 無料で公開されているブローカー、 broker.emqx.io の
TLS/SSL による認証について
 無料で公開されているブローカー、 broker.emqx.io の上記URLの紹介ページには SSL/TLS ポート番号があり、Certificate Authority があるので TLS/SSL による認証はできそうです。

〇 無料で公開されているブローカーではないけれど、無料で使用できそうなブローカーを探してみました
 調べたところ、無料で使用できるデータ量の枠があり、上記の2つの認証方法が使える MQTT ブローカーがありました。 EMQX のサーバーレス MQTT ブローカー
             EMQX Platform Serverlessです。
 EMQXを開発してる EMQ は中国の会社ですが、カリフォルニアに本社を置き、中国の杭州にオフィス、スウェーデンのストックホルムに開発センターを置く、グローバルに事業展開している国際的な組織として運営されています。製品の安全性は高く評価されてます。

〇 EMQX Platform Serverless — 毎月100万セッション分の無料枠があり、無料枠を超えると使用料が発生しますが、無料枠分しか使用しないという設定ができます!!
 そしてユーザー名とパスワードによる認証と、TLS/SSL による認証を使用できます。

〇 EMQX Platform Serverless は、登録したユーザーに対してユニークなMQTTブローカーアドレスを発行するので、他のユーザーは使用できない– これは安全ですね!!
 EMQX Platform Serverless は公開ブローカーではないので、登録ユーザーにユニークなブローカーアドレスを発行してくれます。このアドレスをマイコンや PC のクライアントアプリに書き込んで使用しますので、公開ブローカーアドレスのように他のユーザーは知ることができないので安全ですね。

〇 EMQX Platform Serverless タイプの設定方法

(2)早速、EMQX Serverless を使ってみよう

〇 EMQX Serverless タイプブローカーを使う実験を行うために下図の回路を作ります。

 使用するボードは、OSOYOO NodeMCUボード v1.0 です。NodeMCUボードは ESP8266 を用いており、Type C USBポートで電源供給、プログラム転送、シリアルモニタによる通信ができます。 amazon で3個で税込み 2,241円です(1個で747円ですね。2025年8月時点)。
 他に、抵抗、スイッチ、可変抵抗、トランジスタ、超音波センサ、温度センサ、OLEDモジュール、リレーモジュール、赤外線センサ(赤外線レシーバとも呼びます)、赤外線LED を使用します。
 リレーモジュールは、amazon で5個で税込み 750円です(1個で150円ですね。2025年8月時点)。型番は1CH リレーモジュール SRD-5VDC-SL-C です。

注: OSOYOO NodeMCUボード v1.0 を初めて使う方は、MQTTを利用するプログラムを作成する前に、抵抗、スイッチ、可変抵抗、トランジスタ、超音波センサ、温度センサ、OLEDモジュール、リレーモジュール、赤外線センサ、赤外線LED を NodeMCU で使用する基本的なプログラムを学ぶ必要がありますので、NodeMCU マイコンを用いた IOT 基礎実験 をご覧ください。

A.  NodeMCU、OLED、スイッチ、可変抵抗の回路図です

B.  NodeMCU、LED2個、温度センサDHT22、超音波センサHC-SR04の回路図です

C.  NodeMCU、スイッチ、LED、可変抵抗、赤外線レシーバの回路図です

D.  NodeMCU、2個のLEDと2個のスイッチの回路図です

E.  NodeMCU、1個のLEDと1CHリレーモジュールと1個のスイッチの回路図です。リレーモジュールは SRD-5VDC-SL-C です

F.   NodeMCU、赤外線LEDと赤外線レシーバの回路図です。
 赤外線レシーバは VS1838B です。
 赤外線LED は外径5mm(直径5mmですよ)、ピーク波長940nm、
順方向電圧VF 1.25V~1.8V、動作電流 20mA、カラーは透明です。
型番は OSI5LA5113A だと思っています( ? )

 これらの回路図をブレッドボードを用いて作成しましょう。

〇 MQTTを用いて、NodeMCUボードと PC間で通信を行いますが、PC用 MQTT クライアントツールとして MQTTX を用います

 MQTTX は EMQ がオープンソースで提供する MQTT 検証ツールです。
・ダウンロードして使用する MQTTX Client
・ダウンロード、インストール不要で、Webブラウザ上で使用する
MQTTX Web
などがありますが、今回はURLを指定するだけで使用できる
 MQTTX Web を使用します。

(3)MQTT 実験項目

M1.NodeMCU が出力する文字列 “ Hello World “ を MQTTX Webに出力しよう。

M2. MQTTX Web から NodeMCU の LED を点灯・消灯させよう。(上の回路図 B 参照)。

M3.MQTTX Web からリレーモジュールを操作しよう。
(上の回路図 E 参照)。

M4.MQTTX Web でスイッチの ON と OFF を読み取ろう。
(上の回路図 C 参照)。

M5.応用問題

① MQTTX Web からリレーモジュールと LED を操作しよう。
リレーモジュールが ON で LED を ON、リレーモジュールが OFF で LED を OFF する。   (上の回路図 E 参照)。

M6.MQTTX Web で可変抵抗の電圧を読み取ろう。 
(上の回路図 C 参照)。

M7.MQTTX Web から文字列を NodeMCU に送り OLED モジュールで表示しよう(上の回路図 A 参照)。
 ( OLED とは Organic Light Emitting Diode  の略で、発光材料に
有機物質( Organic ) を使った LED(発光ダイオード)のことです )

M8.MQTTX Web で温度センサ DHT22 の値を読み取ろう。
 DHT22 ( AM2302 ) はデータ交換と制御が1本のデータ線で行うシングルワイヤ通信を採用しています。シングルワイヤ通信は、1-WIre と書きます。そのため、電源VCC、GND、データ線の3本で温度・湿度データを出力できます。(上の回路図 B 参照) 

M9.MQTTX Web で超音波センサ HC-SR04 の値を読み取ろう。(上の回路図 B 参照)

M10.MQTTX Web でダイソーの赤外線リモコンの各スイッチの信号値を読み取ろう。(上の回路図 C 参照)
 使用するのはダイソーで税込み330円で売っている リモコンライト [イルミネーション] です。同じ価格で似た品物があるので間違えないで下さいね。

 赤外線リモコンとライトのセットです。ライトはリモコンで点灯します。この赤外線リモコンの信号の値を読み取ります。

 リモコンの各スイッチに上図のように番号を付けましたの、このスイッチ番号に対して、信号値を読み取って下さい。
 赤外線レシーバは VS1838B を使用します。

M11.MQTTX Web から赤外線リモコンのスイッチ番号を送り、NodeMCU に接続している赤外線LED でスイッチ番号に対応した信号値を送り、ダイソーのリモコンライトを点灯させよう。
(上の回路図 F 参照)

M12.応用問題

① NodeMCU に接続している赤外線センサでダイソーの赤外線リモコンのスイッチの信号値を読み取り、それをスイッチ番号に変換し、MQTT を用いて離れた所に設置している 確認用NodeMCU 回路にスイッチ番号を送り、確認用NodeMCU 回路に接続した赤外線LED からスイッチ番号に対応した信号値を送り、ダイソーのリモコンライトを点灯させよう。
(上の回路図 F 参照)

〇 M1から M11 までのサンプルプログラム

 プログラムの編集、ビルド、ダウンロードはすべて、Arduino IDE 1.8.19 を使用しています。NodeMCU のボードマネージャや必要なライブラリのインストールは必ず行ってください。ボードマネージャーのインストールについては、
https://osoyoo.com/ja/2023/02/23/getting-started-with-nodemcu-using-arduinoide/
 MQTT ブローカーと通信するためのMQTTライブラリ PubSubClient のインストールについては、
https://osoyoo.com/ja/2017/05/10/nodemcu-lesson-6-mqtt-basic-example/
を参考にしてください。

M1_HelloWorld
NodeMCU の出力した “HelloWorld” を MQTTX Web で表示

// M1_HelloWorld.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*  NodeMCU から "HelloWorld" を PC の MXTTX Web に送る */
//*                                                    */
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.18                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "*******";      // 接続するWiFiのssid
const char *password = "******";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "*******************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------");
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int ii=0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

  //////////////////////////////////////////////////////////////////
  // 以下で NodeMCU から "Hello World" を2秒ごとにブローカーに送ります //
  //////////////////////////////////////////////////////////////////
  unsigned long now = millis(); // NodeMCUマイコンの現在時間を
                                // ミリ秒単位で取得
  if (now - lastMsg > 2000) {
    // 2秒間待機
    lastMsg = now;
    ++value;
    snprintf (msg, MSG_BUFFER_SIZE, "#%ld: Hello World ", value);
//    Serial.print("[ Publish message ] ");
//    Serial.println(msg);
    mqtt_client.publish(mqtt_topic, msg);
  }   
}

M2_LED LEDの利用
  MQTTX Web から NodeMCU の LED を点灯・消灯させる

 NodeMCU に対して、MQTTX Web から数字の 1 の文字が送られると LED が点灯し、数字の 0 の文字が送られると LED が消灯します (上の回路図 B 参照)。

// M2_LED.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : 数字を送る                 */
//*   数字 1 で LED を点灯する                    */
//*   数字 0 で LED を消灯する                    */    
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.19                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "*******";      // 接続するWiFiのssid
const char *password = "******";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "********************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

int red_led = 5;

void setup() {
    pinMode( red_led, OUTPUT ); // GPIO5 ( D1pin ) を出力ピンにする設定
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------");

    //********************************************************************//
    //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED は使えないようです //
    //   必ず外付けの LED を使用してください                                //
    //                                                                    //
    //  以下は PC から送られる数字によって、LED を点灯、消灯します               //
    //  数字 1 でLED点灯、数字 0 でLED消灯です                              //
    //*******************************************************************//
    if ((char)payload[0] == '1') {
      digitalWrite( red_led, HIGH ); //LED 点灯
    } else if((char)payload[0] == '0'){
      digitalWrite( red_led, LOW ); //LED 消灯
    }     
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int ii=0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();
}

M3_RELAY RELAY の利用
  MQTTX Web から NodeMCU の RELAY を ON・OFF させる

 NodeMCU に対して、MQTTX Web から数字の 1 の文字が送られると RELAY をONし、数字の 0 の文字が送られると RELAY を OFFします (上の回路図 E 参照)。

// M3_RELAY.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : 数字を送る                 */
//*   数字 1 で LELAY をONする             */
//*   数字 0 で LELAY をOFFする             */    
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.20                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "*********";      // 接続するWiFiのssid
const char *password = "********";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "*******************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

int relay = 4;

void setup() {
    pinMode( relay, OUTPUT ); // GPIO4 ( D2pin ) を出力ピンにする設定
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------");

    //********************************************************************//
    //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED        //
    //           は使えないようです                                          //
    //   必ず外付けの LED を使用してください                                //
    //                                                                    //
    //  以下は PC から送られる数字によって、relay を ON、OFF します      //
    //  数字 1 で relay をON、数字 0 で relay をOFF です    //
    //*******************************************************************//
    if ((char)payload[0] == '1') {
      digitalWrite( relay, HIGH ); //relay ON
    } else if((char)payload[0] == '0'){
      digitalWrite( relay, LOW ); //relay OFF 消灯
    }     
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int ii=0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();
}

M4_READING_SWITCH
スイッチの ON と OFF を読み取ろう(上の回路図 C 参照)

// M4_READING_SWITCH.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : スイッチON、OFF を送る     */
//*   スイッチ OFF で HIGH (3.3V)             */
//*   スイッチ ON で LOW (0V)             */    
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.20                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "********";      // 接続するWiFiのssid
const char *password = "*******";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "***********************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

int switch_onoff = 4;

void setup() {
    pinMode( switch_onoff, INPUT ); // GPIO4 ( D2pin ) を入力ピンにする設定
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 
}

//unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int flag_i = 0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

    //************************************************************//
    //  以下で NodeMCU からスイッチの状態を PC に送る                 //
    //                                                           //
    //  スイッチが HIGH ならば、スイッチ OFF です                    //
    //  スイッチが LOW ならば、スイッチ ON です                      //
    //**********************************************************//
    int switchState = digitalRead( switch_onoff );
    if ((switchState == HIGH)&&(flag_i == 0)) { // SWITCH OFF
      flag_i = 1;
      ++value;
      snprintf(msg, MSG_BUFFER_SIZE, "#%ld: Swtch OFF ", value);
      mqtt_client.publish(mqtt_topic, msg);
    } else if ((switchState == LOW)&&(flag_i == 1)) { // SWITCH ON
      flag_i = 0;
      ++value;
      snprintf(msg, MSG_BUFFER_SIZE, "#%ld: Swtch ON ", value);
      mqtt_client.publish(mqtt_topic, msg);
    }       
}

M6_READING_VR
MQTTX Web で可変抵抗の電圧を読み取ろう (上の回路図 C 参照)

// M6_READING_VR.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC <- NodeMCU : 可変抵抗の AD 変換値を   */
//*                   MQTTX Web に送る        */
//*  A0ピンに入力された 0-3.3V の電圧をAD変換する  */
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.21                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "**********";      // 接続するWiFiのssid
const char *password = "*********";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "************************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
//int value = 0;
//int flag_i = 0;

int val; // AD変換後の値を代入するint型変数:0~1023の値
float vol; // AD変換されたint型変数を0-3.3V にする

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

    //***************************************************************//
    // 以下で NodeMCU から可変抵抗の電圧値を1秒ごとにブローカーに送ります //
    //**************************************************************//
    unsigned long now = millis(); // NodeMCUマイコンの現在時間を
                                // ミリ秒単位で取得
    if (now - lastMsg > 1000) {
      // 1秒間待機
      lastMsg = now;
      val = analogRead(A0); // A0 ピンから可変抵抗の電圧値を読み、
                        // AD 変換結果を val に入力する
      vol = val * 3.3 / 1024;                     
      snprintf(msg, MSG_BUFFER_SIZE, "AD値 %4d : 電圧値 %3.2fV", val, vol ); 
      mqtt_client.publish(mqtt_topic, msg);
    }       
}

M7_OLED MQTTX Web から文字列を NodeMCU に送り
OLED モジュールで表示しよう(上の回路図 A 参照)。
 ( OLED とは Organic Light Emitting Diode  の略で、発光材料に
有機物質( Organic ) を使った LED(発光ダイオード)のことです )

// M7_OLED.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : MQTTX Web からの文字列を    */
//*                   NodeMCU の OLED に表示する  */
//*  OLED モジュール使います。端子の接続は以下です。*// 
//*  OLEDコントローラーは SSD1306 です            *//
//*  I2C でNodeMCUと接続します                   *//
//*     VDD -- 3.3V                            *//
//*     GND -- GND                             *//
//*     SCK -- D1                              *//
//*     SDA -- D2                              *//
//*  D1,D2 を I2C ピンとして使います              *//
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.21                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

////////////////////////  OLED Setting  ///////////////////////
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 //OLED display width, in pixels
#define SCREEN_HEIGHT 64 //OLED display height, in pixels
// Declaration for SSD1306 display connected using I2C
#define OLED_RESET -1 // Reset pin このOLEDは
                    // Resetピンがないので使用しないことを示す
#define SCREEN_ADDRESS 0x3C // OLED の I2Cアドレス
// 次は Adafruit_SSD1306クラスのオブジェクトを作るためのコンストラクタです
Adafruit_SSD1306 display(SCREEN_WIDTH, 
SCREEN_HEIGHT, &Wire, OLED_RESET);
////////////////////////////////////////////////////////////////

// WiFi credentials
const char *ssid = "*******";      // 接続するWiFiのssid
const char *password = "********";   // 接続するWiFiのパスワード
// EMQX Serverless type broker
const char *mqtt_broker = "************************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();

    ////////////////////////////////////////////////////////
    //                  OLED Initialize                   //
    ////////////////////////////////////////////////////////
    // initialize the OLED object : display.begin
    // SSID1306_SWITCHCAPVCC で OLED内部の電圧を上げる
    //チャージポンプを ON にする
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;); // Don't proceed, loop forever
    }

    // Clear the buffer. OLED の画面用メモリクリア
    display.clearDisplay();

    // Display Text "I am ready!"を表示します
    display.setTextSize(1);      //フォントサイズ設定
    display.setTextColor(WHITE); //黒い背景のときWHITE 
    //明るい背景のときBLACK と書く
    display.setCursor(0,28); //カーソル位置の設定
    // (X,Y) X=horizontal, Y=vertical
                    //(0,0)は左上端です。(63,127)が右下端です。
    display.println("I am ready!"); //画面にメッセージを出力します
        // print(" ")は改行無し、println(" ")は改行します。
    display.display();//メッセージを画面に表示します
    delay(2000);
    display.clearDisplay();  //OLED の画面用メモリクリア
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 

    //********************************************************************//
    //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED        //
    //           は使えないようです                                          //
    //   必ず外付けの LED を使用してください                                //
    //                                                                    //
    //  以下は MQTTX Web から送られた文字列を OLED に表示します                //
    //*******************************************************************//
    display.clearDisplay();  //OLED の画面用メモリクリア    
    display.display();       //描画画面クリア     
    // (X,Y) X=horizontal, Y=vertical
                    //(0,0)は左上端です。(63,127)が右下端です。
    display.setCursor(0,28); //カーソル位置の設定
    for (unsigned int ii = 0; ii < length; ii++) {                
      display.print( (char)payload[ii]); //画面に文字列を出力します。改行無し。
        // print(" ")は改行無し、println(" ")は改行します。
    }
    display.display();//メッセージを画面に表示します
    delay(2000);
    memset(payload, '\0', length); //配列 payload 初期化
}

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();     
}

M8_DHT22
MQTTX Web で温度センサ DHT22 の値を読み取ろう。
 DHT22 ( AM2302 ) はデータ交換と制御が1本のデータ線で行うシングルワイヤ通信を採用しています。シングルワイヤ通信は、1-WIre と書きます。そのため、電源VCC、GND、データ線の3本で温度・湿度データを出力できます。(上の回路図 B 参照) 

// M8_DHT22.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる                  */
//*       ポート番号 8883                           */
//*   PC <- NodeMCU : 温度センサ DHT22 からの温度と湿度を   */
//*                   MQTTX Web に送る        */
//*              ピンの接続                        *//
//*              VCC = 3.3V                     *//
//*              S   = GPIO13 ( D7pin )         *//
//*              GND = GND                      *//
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.23                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "********";      // 接続するWiFiのssid
const char *password = "********";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "*********************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

////////////////////////////////////////////
///////////   温度センサ 設定    /////////////
////////////////////////////////////////////
#include "DHT.h"
#define DHTPIN 13     // NodeMCU D7pin = GPIO13
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11 
#define DHTTYPE DHT22   // DHT 22  (AM2302)
// Initialize DHT sensor.
DHT dht(DHTPIN, DHTTYPE);

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();

    Serial.println("DHTxx test!");
    dht.begin();   
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

    //***********************************************************************//
    // 以下で NodeMCU から温度センサDHT22の温度と湿度を2秒ごとにブローカーに送ります //
    //***********************************************************************//
    unsigned long now = millis(); // NodeMCUマイコンの現在時間を
                                // ミリ秒単位で取得
    if (now - lastMsg > 2000) {
      // 1秒間待機
      lastMsg = now;
      // Reading temperature or humidity takes about 250 milliseconds!
      // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
      // humidity 湿度
      float h = dht.readHumidity();
      // Read temperature as Celsius  セルシウス温度 (the default)
      float t = dht.readTemperature();
      // Read temperature as Fahrenheit  華氏温度 (isFahrenheit = true)
      float f = dht.readTemperature(true);
  
      // Check if any reads failed and exit early (to try again).
      if (isnan(h) || isnan(t) || isnan(f)) {
        Serial.println("Failed to read from DHT sensor!");
        return;
      }

      snprintf(msg, MSG_BUFFER_SIZE, "湿度 %5.2f% : 温度 %5.2f度", h, t ); 
      mqtt_client.publish(mqtt_topic, msg);
      
      Serial.print("Humidity: "); 
      Serial.print(h);
      Serial.print(" %\t");
      Serial.print("Temperature: "); 
      Serial.print(t);
      Serial.println(" *C ");
    }       
}

M9_HCSR04
 MQTTX Web で超音波センサ HC-SR04 の値を読み取ろう。
(上の回路図 B 参照)

// M9_HCSR04.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる                  */
//*       ポート番号 8883                           */
//*   PC <- NodeMCU : 超音波センサ HC-SR04 からの距離データを   */
//*                   MQTTX Web に送る        */
//*           ピンの接続                        *//
//*               VCC = VIN 5V                 *//
//*               Trig = GPIO12 ( D6pin )      *//
//*               Echo = GPIO14 ( D5pin )      *//
//*               GND = GND                    *//
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.12.23                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "********";      // 接続するWiFiのssid
const char *password = "********";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "**************************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

//////////////////////////////////////////////////////
///////////   超音波センサ HC-SR04 設定    /////////////
//////////////////////////////////////////////////////
const int trigPin = 12;
const int echoPin = 14;
//define sound velocity in cm/uS
#define SOUND_VELOCITY 0.034
long duration;
float distanceCm;

void setup() {
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();

    //// HC-SR04 pin setting ////
    pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
    pinMode(echoPin, INPUT); // Sets the echoPin as an Input
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

    //***********************************************************************//
    // 以下で NodeMCU から超音波センサの測定距離を1秒ごとにブローカーに送ります //
    //***********************************************************************//
    unsigned long now = millis(); // NodeMCUマイコンの現在時間を
                                // ミリ秒単位で取得
    if (now - lastMsg > 1000) {
      // 1秒間待機
      lastMsg = now;
      // Clears the trigPin
      digitalWrite(trigPin, LOW);
      delayMicroseconds(2);
      // Sets the trigPin on HIGH state for 10 micro seconds
      digitalWrite(trigPin, HIGH);
      delayMicroseconds(10);
      digitalWrite(trigPin, LOW);
      
      // Reads the echoPin, returns the sound wave travel time in microseconds
      duration = pulseIn(echoPin, HIGH);
      
      // Calculate the distance
      distanceCm = duration * SOUND_VELOCITY/2;
      
      snprintf(msg, MSG_BUFFER_SIZE, "距離 %7.2f cm", distanceCm ); 
      mqtt_client.publish(mqtt_topic, msg);
      
      // Prints the distance on the Serial Monitor
      Serial.print("Distance (cm): ");
      Serial.println(distanceCm); 
    }       
}

M10_IRremote_MQTT
 MQTTX Web で赤外線リモコンの値を読み取ろう。
 赤外線レシーバは VS1838B を使用します。(上の回路図 C 参照)

// M10_IRremote_MQTT.ino
/* 
 *  赤外線レシーバを NodeMCU に接続し、赤外線リモコンのキーが押されると送出される
 *  赤外線信号を、この赤外線レシーバで受信し、NodeMCU に IRコードを送信します。
 *   さらに読み取ったIRコードを、MQTTを用いて、MQTTX Web に publishします
 *   MQTTブローカーは EMQX serverless を使用します。
 *   2026.2.7
 *  IRremoteESP8266 ライブラリはバージョン 2.8.6 です。2025年12月24日にインストールしました。
 *     2025.12.24
 */
 ////// 赤外線に関する設定 //////
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
// An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU
// board).
// Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts.
// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot.
#ifdef ARDUINO_ESP32C3_DEV
const uint16_t kRecvPin = 10;  // 14 on a ESP32-C3 causes a boot loop.
#else  // NodeMCU
const uint16_t kRecvPin = 14;
#endif
IRrecv irrecv(kRecvPin);
decode_results results;

////// ネットワーク・MQTTに関する設定 //////
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです
// WiFi credentials
const char *ssid = "**********";      // 接続するWiFiのssid
const char *password = "*********";   // 接続するWiFiのパスワード
// EMQX Serverless type broker
const char *mqtt_broker = "*******************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port
// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds
// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成
// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

void setup() {
  Serial.begin(115200);
  irrecv.enableIRIn(); // Start the receiver
  while (!Serial)  // Wait for the serial connection to be establised.
  delay(50);

  connectToWiFi();
  syncTime();  // X.509 validation requires synchronization time
  mqtt_client.setServer(mqtt_broker, mqtt_port);
  mqtt_client.setCallback(mqttCallback);
  connectToMQTT();
    
  Serial.println();
  Serial.print("IRrecvDemo is now running and waiting for IR message on Pin ");
  Serial.println(kRecvPin);
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];

void loop() {
unsigned long IRvalue;  
  if (!mqtt_client.connected()) {
        connectToMQTT();
  }
  mqtt_client.loop();  

  //***************************************************************//
  // 以下で NodeMCU からIRリモコンのコードを1秒ごとにブローカーに送ります //
  //************************************************************
  long now = millis();

  if (now - lastMsg > 1000) {
     lastMsg = now;
    if (irrecv.decode(&results)) {
      // results.value is typically a 32-bit integer
      Serial.print(results.value, HEX);Serial.print("  "); Serial.println(results.value);
      IRvalue = results.value;
      snprintf(msg, MSG_BUFFER_SIZE, "IR値 %#X", IRvalue ); 
      mqtt_client.publish(mqtt_topic, msg);
      irrecv.resume(); // Receive the next value
     }
  }
}

M11_IR_LED_MQTT
 MQTTX Web から赤外線リモコンのスイッチ番号を送り、NodeMCU に接続している赤外線LED でスイッチ番号に対応した信号値を送り、ダイソーのリモコンライトを点灯させよう。
(上の回路図 F 参照)

注意!! 必ずNO_1 の ONスイッチを押してから、他の色スイッチを押してください。 NO_2 の OFFスイッチを押して消灯した後で色スイッチを押しても点灯しません。 またこのプログラムはリモコンのスイッチ番号 NO_1、NO_2 とNO_7 ~ NO_18を指定したときだけ動作します。NO_3 ~ NO_6 は無反応です。

// M11_IR_LED_MQTT.ino
/* 
 *  MQTTX Webから Publish された、赤外線リモコンのスイッチ番号を受け取り、
 *   NodeMCU に接続している赤外線LEDで、スイッチ番号に対応した 
 *   IRコードをダイソーリモコンライトに送り点灯します。
 *   MQTTブローカーは EMQX serverless を使用します。
 *   2026.2.7
 *  IRremoteESP8266 ライブラリはバージョン 2.8.6 です。2025年12月24日にインストールしました。
 *     2025.12.24
 */
  ////// 赤外線に関する設定 //////
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>

///////////////////////////////////////////////////
//    赤外線LED から送出される信号の 「表」 です     //
//                                              //
//  プログラム N11_IRremote で読み込んだ           //
//  赤外線リモコンのスイッチを押したときに点灯する    //
//  赤外線LED から送出される信号の内容を以下に記入しましょう  //
//   16進数で書いてね: 0x1FE1234 とか          //
///////////////////////////////////////////////////
// スイッチ番号 / 信号16進数 / スイッチの意味
// 1 /  / ON スイッチ
const uint32_t NO_1 = 0x0  ;
// 2 /  / OFF スイッチ
const uint32_t NO_2 = 0x0  ;
// 3 /  / Mode スイッチ
const uint32_t NO_3 = 0x0  ;
// 4 /  / 4 hour timer
const uint32_t NO_4 = 0x0  ;
// 5 /  / 8 hour timer
const uint32_t NO_5 = 0x0  ;
// 6 /  / Multi 点灯
const uint32_t NO_6 = 0x0  ;
// 7 /  / 色スイッチ
const uint32_t NO_7 = 0x0  ;
// 8 /  / 色スイッチ
const uint32_t NO_8 = 0x0  ;
// 9 /  / 色スイッチ
const uint32_t NO_9 = 0x0  ;
// 10 /  / 色スイッチ
const uint32_t NO_10 = 0x0  ;
// 11 /  / 色スイッチ
const uint32_t NO_11 = 0x0  ;
// 12 /  / 色スイッチ
const uint32_t NO_12 = 0x0  ;
// 13 /  / 色スイッチ
const uint32_t NO_13 = 0x0  ;
// 14 /  / 色スイッチ
const uint32_t NO_14 = 0x0  ;
// 15 /  / 色スイッチ
const uint32_t NO_15 = 0x0  ;
// 16 /  / 色スイッチ
const uint32_t NO_16 = 0x0  ;
// 17 /  / 色スイッチ
const uint32_t NO_17 = 0x0  ;
// 18 /  / 色スイッチ
const uint32_t NO_18 = 0x0  ;

// 赤外線LEDを接続したピン (D2 = GPIO4)
const uint16_t kIrLed = 4;
IRsend irsend(kIrLed);

////// ネットワーク・MQTTに関する設定 //////
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです
// WiFi credentials
const char *ssid = "*******";      // 接続するWiFiのssid
const char *password = "*******";   // 接続するWiFiのパスワード
// EMQX Serverless type broker
const char *mqtt_broker = "********************"; // EMQX serverless の接続先URL を書きます
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port
// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds
// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成
// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

void setup() {
  Serial.begin(115200);
  irsend.begin();
  while (!Serial)  // Wait for the serial connection to be establised.
  delay(50);

  connectToWiFi();
  syncTime();  // X.509 validation requires synchronization time
  mqtt_client.setServer(mqtt_broker, mqtt_port);
  mqtt_client.setCallback(mqttCallback);
  connectToMQTT();
  
  Serial.println();
  Serial.println("IR Sender Ready");
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------"); 

    //********************************************************************//
    //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED        //
    //           は使えないようです                                          //
    //   必ず外付けの LED を使用してください                                //
    //                                                                    //
    //  以下は MQTTX Web から送られたダイソーリモコンのスイッチ番号を受け取り        //
    //  スイッチ番号に対応するIRコードを赤外線LEDから発光して                     //
    //  リモコンライトを点灯します                           //
    //  注意!! 必ずNO_1のONスイッチを押してから、他の色スイッチを押してください。   //
    //   NO_2のOFFスイッチを押して消灯した後で色スイッチを押しても点灯しません      //
    //*******************************************************************//
    if( length == 1 ){
      if((char)payload[0] == '1') irsend.sendNEC(NO_1, 32); // ON スイッチ  
      else if((char)payload[0] == '2') irsend.sendNEC(NO_2, 32); // OFF スイッチ
      else if((char)payload[0] == '7') irsend.sendNEC(NO_7, 32); // 色 スイッチ 
      else if((char)payload[0] == '8') irsend.sendNEC(NO_8, 32); // 色 スイッチ 
      else if((char)payload[0] == '9') irsend.sendNEC(NO_9, 32); // 色 スイッチ 
    }
    else if(( length == 2 )&&( (char)payload[0] == '1' )){
      if((char)payload[1] == '0') irsend.sendNEC(NO_10, 32); // 色 スイッチ 
      else if((char)payload[1] == '1') irsend.sendNEC(NO_11, 32); // 色 スイッチ 
      else if((char)payload[1] == '2') irsend.sendNEC(NO_12, 32); // 色 スイッチ 
      else if((char)payload[1] == '3') irsend.sendNEC(NO_13, 32); // 色 スイッチ 
      else if((char)payload[1] == '4') irsend.sendNEC(NO_14, 32); // 色 スイッチ 
      else if((char)payload[1] == '5') irsend.sendNEC(NO_15, 32); // 色 スイッチ
      else if((char)payload[1] == '6') irsend.sendNEC(NO_16, 32); // 色 スイッチ 
      else if((char)payload[1] == '7') irsend.sendNEC(NO_17, 32); // 色 スイッチ 
      else if((char)payload[1] == '8') irsend.sendNEC(NO_18, 32); // 色 スイッチ                                                             
    }
}

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();     
}

〇 以下は参考ページです

・EMQX Serverless の オンラインデバッグ
・MQTT スマホアプリ MyMQTT
に興味のある方はご覧ください。

プログラムの参考にした URL は以下です。
https://docs.emqx.com/ja/cloud/latest/connect_to_deployments/esp8266.html   (詳しい説明が載っています)
https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino  (上記URLと同じプログラムです)

    プログラム1

// E1_MQTT_via_TLS_Rellay_LED.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : 数字を送る                 */
//*   数字 1 で LED を点灯し、リレーを ON にする   */
//*   数字 0 で LED を消灯し、リレーを OFF にする  */    
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.8.7                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "WiFiのssid";      // 接続するWiFiのssid
const char *password = "WiFiのパスワード";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "EMQX serverless の接続先を書きます";
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

int red_led = 5;
int RELLAY = 4;

void setup() {
    pinMode( red_led, OUTPUT ); // GPIO5 ( D1pin ) を出力ピンにする設定
    pinMode( RELLAY, OUTPUT ); // GPIO4 ( D2pin ) を出力ピンにする設定
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------");

  //********************************************************************//
  //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED は使えないようです //
  //   必ず外付けの LED を使用してください                                //
  //                                                                    //
  //以下は PC から送られる数字によって、LED を点灯、消灯、 リレーをオン、オフします   //
  //  数字 1 でLED点灯、リレーオン、数字 0 でLED消灯,リレーオフです          //
  //*******************************************************************//
  if ((char)payload[0] == '1') {
    digitalWrite( red_led, HIGH ); //LED 点灯
    digitalWrite( RELLAY, HIGH ); //リレーON
  } else if((char)payload[0] == '0'){
    digitalWrite( red_led, LOW ); //LED 消灯
    digitalWrite( RELLAY, LOW ); //リレーOFF
  } 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int ii=0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

  //////////////////////////////////////////////////////////////////
  // 以下で NodeMCU からvalue のカウント値を2秒ごとにブローカーに送ります //
  // 使用する時にコメントを消して使ってください
  //////////////////////////////////////////////////////////////////
//  unsigned long now = millis(); // NodeMCUマイコンの現在時間を
//                                // ミリ秒単位で取得
//  if (now - lastMsg > 2000) {
//    // 2秒間待機
//    lastMsg = now;
//    ++value;
//    snprintf (msg, MSG_BUFFER_SIZE, "#%ld", value);
//    Serial.print("[ Publish message ] ");
//    Serial.println(msg);
//    mqtt_client.publish(mqtt_topic, msg);
//  }   
}

このプログラム1で重要な箇所は23行目から32行目です。

** プログラム1の、23行目から32行目 **
// WiFi credentials
const char *ssid = "WiFiのssid";      // 接続するWiFiのssid
const char *password = "WiFiのパスワード";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "EMQX serverless の接続先を書きます";
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port の 8883 にします

 ssid には NodeMCU が接続する WiFi の SSID を書き、password には接続する WiFi のパスワードを書きます。
 mqtt_broker には先述した、EMQX Serverless の「概要」に表示される「MQTT接続情報」(下図)の「接続先」に書かれているアドレスをコピーします。
 mqtt_topic には、publish / subscribe で使用するトピック名を書きます。
 mqtt_username には、ユーザー名を書きます。EMQX Serverless の「認証」で登録するユーザー名になります。
 mqtt_password には、パスワードを書きます。EMQX Serverless の「認証」で登録するパスワードになります。

次に重要な個所は171行目から177行目です。

** プログラム1の、171行目から177行目 **
 if ((char)payload[0] == '1') {
    digitalWrite( red_led, HIGH ); //LED 点灯
    digitalWrite( RELLAY, HIGH ); //リレーON
  } else if((char)payload[0] == '0'){
    digitalWrite( red_led, LOW ); //LED 消灯
    digitalWrite( RELLAY, LOW ); //リレーOFF
  } 

数字1をNodeMCUが受信したら
 digitalWrite( red_led, HIGH ); // LEを点灯させる
 digitalWrite( RELLAY, HIGH ); // リレーON
させる
数字0をNodeMCUが受信したら
 digitalWrite( red_led, LOW ); // LEDを消灯させる
 digitalWrite( RELLAY, LOW ); // リレーOFF
させる

〇 NodeMCU のプログラム1でMQTT による通信のデバッグを行います。
 プログラム1を NodeMCU に書き込んだ後、MQTT による通信が正しく行われているかデバッグを行いますが、 なんと、EMQX Serverless の設定ページから「オンラインデバッグ」を行うことができますので、使てみましょう。

 以下に「オンラインデバッグ」の使い方を説明します。

〇 MQTT スマホアプリで NodeMCU のコントロールをします。
 MQTT スマホアプリ MyMQTT を使って NodeMCU をコントロールします( android スマホを使って実験しました)。
 MyMQTT を使った理由ですが、別のアプリで実験したとき、EMQX Serverless の「概要」に記載されている、「接続先」アドレスを入力すると、間違ったアドレスとみなして入力を受け付けないものがあったからです(アドレスを受け付けないアプリは少ないと思いますが、皆さんも他のMQTTアプリを使って確認して欲しいです)。
 PC上のEMQX Serverless のページは閉じておきましょう。
 スマホアプリを操作する前に、プログラム1をダウンロードしてある NodeMCU に電源を投入して動作状態にしておいてください。

気づいたこと 
 プログラム1で MQTT スマホアプリ MyMQTT から NodeMCU をコントロールすることができました。実際に LED とリレーを動作させた方は気づいたと思いますが、”1″ や “0” をPublish してもすぐには LED やリレー動作 LED は点灯、消灯せず、1~3秒程度遅れてから点灯、消灯します。これは MQTT による通信の特徴と思われます。

〇 NodeMCU のプログラム2 です。
 プログラム1では NodeMCU は “1” と “0” を受信して LED とリレーを動作させるだけですが、LED とリレーの動作と同時に、NodeMCU からスマホアプリ側に文字列 “* ON *” または “* OFF *” を送れば、スマホアプリで直接、LED リレーの動作確認ができます。直接、LEDやリレーを見て確認できない場合に有効ですね。
 文字列 “* ON *” または “* OFF *” の送信を追加したプログラムが以下の、プログラム2 です。 

プログラム2

// E2_MQTT_via_TLS_Rellay_LED.ino

// https://github.com/emqx/MQTT-Client-Examples/blob/master/mqtt-client-ESP8266/esp8266_connect_mqtt_via_tls.ino
//を参照しました (github)

//************************************************/
// EMQX Serverless MQTT Broker を使用する        */
//************************************************/
//*   NodeMCU MQTT client                        */
//*    MQTT の TLS/SSL 認証を用いる               */
//*       ポート番号 8883                       */
//*   PC -> NodeMCU : 数字を送る                 */
//*   数字 1 で LED を点灯し、リレーを ON にする   */
//*   同時に NodeMCU は *ON* を Publish する     */
//*   数字 0 で LED を消灯し、リレーを OFF にする  */ 
//*   同時に NodeMCU は *OFF* を Publish する    */   
//* NodeMCU用のMQTTクライアントライブラリ             */
//* pubsubclientをダウンロードすること Nick O'Leary作 */
//*    2025.8.14                               */
//***********************************************/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>   // TLS/SSL 認証を行うときは time.h が必要のようです

// WiFi credentials
const char *ssid = "**WiFi ssid**";      // 接続するWiFiのssid
const char *password = "**WiFi password**";   // 接続するWiFiのパスワード

// EMQX Serverless type broker
const char *mqtt_broker = "EMQX Serverlessで指定される接続先アドレス";
const char *mqtt_topic = "topi"; // 例として topi としました
const char *mqtt_username = "system"; // 例として system としました
const char *mqtt_password = "seigyo"; // 例として seigyo としました
const int mqtt_port = 8883; // MQTT SSL/TLS Port

// NTP Server settings
const char *ntp_server = "pool.ntp.org";     // Default NTP server
// 下の gmt_offset_sec = 0 のままでよさそうです
const long gmt_offset_sec = 0;   // GMT offset in seconds (adjust for your time zone)
const int daylight_offset_sec = 0;        // Daylight saving time offset in seconds

// WiFi and MQTT client initialization
BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient); //MQTT client生成

// Root CA Certificate
// Load DigiCert Global Root CA ca_cert, which is used by EMQX Cloud Serverless Deployment
// EMQX Serverless Deploymentで使用される証明書です
static const char ca_cert[]
PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// 関数プロトタイプ宣言
void connectToWiFi();
void connectToMQTT();
void syncTime();
void mqttCallback(char *topic, byte *payload, unsigned int length);

int red_led = 5;
int RELLAY = 4;

void setup() {
    pinMode( red_led, OUTPUT ); // GPIO5 ( D1pin ) を出力ピンにする設定
    pinMode( RELLAY, OUTPUT ); // GPIO4 ( D2pin ) を出力ピンにする設定
    Serial.begin(115200);
    connectToWiFi();
    syncTime();  // X.509 validation requires synchronization time
    mqtt_client.setServer(mqtt_broker, mqtt_port);
    mqtt_client.setCallback(mqttCallback);
    connectToMQTT();
}

void connectToWiFi() {
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}

void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client-" + String(WiFi.macAddress());// macAddressを使ってclient_id生成
        Serial.printf("Connecting to MQTT Broker as %s...\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe(mqtt_topic);
            mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP32 ^^");  // Publish message upon connection
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));         
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.print(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            Serial.println(" Retrying in 3 seconds.");
            delay(3000);
        }
    }
}

////////////////////////////////////////////////////////////////////
// この callback 関数はブローカーから NodeMCU にメッセージが            //
// 送られると実行されます                                             //
// MQTTの仕様では、ブローカーがクライアントからのメッセージ              //
// (このプログラムでは NodeMCU からのメッセージ)を受け取った後に、      //
// そのメッセージをブローカー自身が再びクライアント(NodeMCU)に          //
// 配信(エコー)する設定がデフォルトで有効になっています。               //
// これを無効にすることができるそうですが、いまのところ不明               //
// そのため、NodeMCU からブローカーにメッセージを送ると、                //
// ブローカーからエコーされた同じメッセージを、このcallback 関数が受け取り  //
// 以下の Serial.print() でシリアルモニタで表示されます。                // 
///////////////////////////////////////////////////////////////////
void mqttCallback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message received on topic: ");
    Serial.println(topic);
    Serial.print("Message: ");
    for (unsigned int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println("\n-----------------------");

  //********************************************************************//
  //  注意!! SSSL/TLS認証の設定をしたときは、NodeMCU の Builtin_LED は使えないようです //
  //   必ず外付けの LED を使用してください                                //
  //                                                                    //
  //以下は PC から送られる数字によって、LED を点灯、消灯、 リレーをオン、オフします   //
  //  数字 1 でLED点灯、リレーオン、数字 0 でLED消灯,リレーオフです          //
  //*******************************************************************//
  if ((char)payload[0] == '1') {
    digitalWrite( red_led, HIGH ); //LED 点灯
    digitalWrite( RELLAY, HIGH ); //リレーON
    mqtt_client.publish(mqtt_topic, "* ON *");
  } else if((char)payload[0] == '0'){
    digitalWrite( red_led, LOW ); //LED 消灯
    digitalWrite( RELLAY, LOW ); //リレーOFF
    mqtt_client.publish(mqtt_topic, "* OFF *");
  } 
}

unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
int ii=0;

void loop() {
    if (!mqtt_client.connected()) {
        connectToMQTT();
    }
    mqtt_client.loop();

  //////////////////////////////////////////////////////////////////
  // 以下で NodeMCU からvalue のカウント値を2秒ごとにブローカーに送ります //
  // 使用する時にコメントを消して使ってください
  //////////////////////////////////////////////////////////////////
//  unsigned long now = millis(); // NodeMCUマイコンの現在時間を
//                                // ミリ秒単位で取得
//  if (now - lastMsg > 2000) {
//    // 2秒間待機
//    lastMsg = now;
//    ++value;
//    snprintf (msg, MSG_BUFFER_SIZE, "#%ld", value);
//    Serial.print("[ Publish message ] ");
//    Serial.println(msg);
//    mqtt_client.publish(mqtt_topic, msg);
//  }   
}

プログラム1と異なる箇所は、下の赤い行だけです。

** プログラム2の、173行目から181行目 **
 if ((char)payload[0] == '1') {
    digitalWrite( red_led, HIGH ); //LED 点灯
    digitalWrite( RELLAY, HIGH ); //リレーON
    mqtt_client.publish(mqtt_topic, "* ON *");
  } else if((char)payload[0] == '0'){
    digitalWrite( red_led, LOW ); //LED 消灯
    digitalWrite( RELLAY, LOW ); //リレーOFF
    mqtt_client.publish(mqtt_topic, "* OFF *");
  } 

174~176行目
digitalWrite( red_led, HIGH ); //LED 点灯
digitalWrite( RELLAY, HIGH ); //リレーON
mqtt_client.publish(mqtt_topic, “* ON *”);
の3行で、LED点灯、リレーONと同時に文字列 “* ON *”を NodeMCU から Publish します。

178~180行目
digitalWrite( red_led, LOW ); //LED 消灯
digitalWrite( RELLAY, LOW ); //リレーOFF
mqtt_client.publish(mqtt_topic, “* OFF *”);
の3行で、LED消灯、リレーOFFと同時に文字列 “* OFF *”を NodeMCU から Publish します。

この2行を追加することにより、
 文字列 “* ON *” が Publish されることにより、LED点灯、リレーON の動作確認ができ、
 文字列 “* OFF *” が Publish されることにより、LED消灯、リレーOFF の動作確認ができます。

注意すること
 数字 ‘1’ や ‘0’ をNodeMCUからPublishするのではなく、
文字列 “* ON *” と “* OFF *” にしたのは、MQTTは仕様により、ブローカーに送られてきた文字列をエコーバックするためです。そのためNodeMCUから、数字 ‘1’ や ‘0’ をPublishすると、ブローカーが ‘1’ や ‘0’ をエコーバックするので、受信したNodeMCUは ‘1’ や ‘0’ をスマホアプリから受け取った時と同じ動作、「LEDの点灯、消灯、リレーのON、OFFを行い、同時に数字 ‘1’ や ‘0’ をPublishする」ので、またブローカーが数字 ‘1’ や ‘0’ をエコーバックします。すると受信したNodeMCUは ‘1’ や ‘0’ をスマホアプリから受け取った時と同じ動作をまた繰り返すので、無限に繰り返されることになります。これを避けるために、文字列 “* ON *” と “* OFF *” をPublish しました。

〇 NodeMCU のプログラム2 のオンラインデバッグ

〇 MQTT スマホアプリで NodeMCU の動作確認。
 次はMQTTスマホアプリでNodeMCU の動作確認を行います。
PC上のEMQX Serverless のページは閉じておきましょう。

(4)レポートについて

1.皆さんの google アカウントの google ドキュメント を使って作成してください。

2.レポート課題
(1)EMQX Serverless のアカウントを自身の学校のメールアドレスを用いて作成します。作成・設定方法はこのWebページに書いてあります。
(2)スマホに MQTT client アプリの MyMQTT をダウンロードして設定します。設定方法はこのWebページに書いてあります。設定するブローカアドレスは作成したEMQX Serverless の「MQTT接続情報」の「接続先」に表示されているアドレスです。Port 番号は 8883 にします。
(3)EMQX Serverless のオンラインデバッグから文字” ON “ と ” OFF “ (他の文字列でもOKです)をスマホの MyMQTT に送ります(Publishする)。MyMQTT で受信を確認します(Subscribeする)
(4)スマホの MyMQTT から EMQX Serverless のオンラインデバッグに、文字 “ ON “ と “ OFF “ (他の文字列でもOKです)を送ります(Publishする)。オンラインデバッグで受信を確認します(Subscribeする)。

3.レポートのまとめ方
EMQX Serverless の作成、設定、通信実験、実験結果の画面をキャプチャし、作業の順番が分かるようにレポートに貼り付ける。
MyMQTT のダウンロード、インストール、設定、通信実験、実験結果の画面を、スマホでキャプチャし、作業の順番が分かるようにレポートに貼り付ける。
・上記の貼り付けた写真に、読みやすく理解しやすい説明を付ける。特に実験結果については、成功した過程と結果、失敗した場合はその理由を詳しく分かりやすく説明を付ける。
・本人が作成したことが分かるように、皆さんのいろいろな顔写真を、説明文に付け加えて、見て楽しい説明文にしてください。公序良俗に反する写真は受け付けません。皆さんであることが分かる範囲での加工はOKです。

4.提出の仕方
 提出期限までに、レポートに下の表紙をつけて、googleドキュメントをPDF化したものを添付して、実験時にお知らせするメールアドレスに送信してください。googleドキュメントのPDF化はgoogleドキュメント上部のツールバーの「ファイル」「ダウンロード」からできます。

「レポート表紙」(word形式です。 googleドライブにダウンロードするとgoogleドキュメントで読み込めます)