hivemq-mqtt-web-client／ChatGPT で作った HTMLファイルを使ってローカルの MQTTブローカーにブラウザ上のクライアントから接続

今回の記事は、ブラウザ上で動く MQTTクライアントを使って、MQTT を使った通信を試す話です。1つは ChatGPT に作ってもらったもので進めてみます。

なお MQTTブローカーは、過去に何度も利用してきた「shiftr.io のデスクトップ版アプリ」を使います。


## クライアント 2種
今回、MQTTクライアントとして使うものは 2つあります。

1つは、HiveMQ により提供されている「MQTT Websocket Client」で、もう 1つは ChatGPT（GPT-4o）に作ってもらった MQTTクライアントです。

それぞれについて補足します。

### HiveMQ により提供されている「MQTT Websocket Client」
HiveMQ により提供されている MQTT Websocket Client は、以下でオープンソースとして公開されているものです。

●hivemq/hivemq-mqtt-web-client: A websockets based MQTT Client for your browser.
　https://github.com/hivemq/hivemq-mqtt-web-client?tab=readme-ov-file

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/dfdb81cb-c94e-e87c-b4a4-3d125d16e7fb.png)

さらに、それについてのデモページがあり、以下にアクセスするだけで簡単に試せます。

●MQTT Websocket Client
　https://www.hivemq.com/demos/websocket-client/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/cb5a41f7-163f-e2ab-4d20-95e83fbf77b2.png)

### ChatGPT（GPT-4o）に作ってもらった MQTTクライアント
もう 1つの ChatGPT（GPT-4o）に作ってもらった MQTTクライアントについても軽く説明します。

ChatGPT（GPT-4o） を使って、以下のプロンプトで HTML を作ってもらいました。

- プロンプト
   1. HTMLでmqtt.jsを使ってMQTTクライアントを作ります。どんな要素／機能がページに必要？
   1. bulmaを使って

プロンプトでは、まずはライブラリを指定した MQTTクライアントの作成を依頼し（※ 要素、機能について回答してもらいつつ）、さらに追加の内容として、「[CSSフレームワークの Bulma](https://bulma.io/)」を使ってもらうように書きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/15154c68-9368-c699-5c91-fd166b3f85f6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/733a5e5e-744b-e467-f1d7-34a84ba1df17.png)
　
最終的に出力された内容は以下のとおりです。ざっと内容を見た感じでは、問題はなさそうです。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>MQTT Client</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
  <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
  <section class="section">
    <div class="container">
      <h1 class="title">MQTT Client</h1>

      <!-- Connection Form -->
      <div class="box">
        <h2 class="subtitle">Connect to Broker</h2>
        <div class="field">
          <label class="label" for="brokerUrl">Broker URL</label>
          <div class="control">
            <input class="input" type="text" id="brokerUrl" placeholder="ws://broker.hivemq.com:8000/mqtt">
          </div>
        </div>
        <div class="field">
          <label class="label" for="clientId">Client ID</label>
          <div class="control">
            <input class="input" type="text" id="clientId" placeholder="clientId">
          </div>
        </div>
        <div class="control">
          <button class="button is-primary" id="connectButton">Connect</button>
        </div>
      </div>

      <!-- Subscription Form -->
      <div class="box">
        <h2 class="subtitle">Subscribe to Topic</h2>
        <div class="field">
          <label class="label" for="subscribeTopic">Subscribe Topic</label>
          <div class="control">
            <input class="input" type="text" id="subscribeTopic" placeholder="topic">
          </div>
        </div>
        <div class="control">
          <button class="button is-link" id="subscribeButton">Subscribe</button>
        </div>
      </div>

      <!-- Publish Form -->
      <div class="box">
        <h2 class="subtitle">Publish Message</h2>
        <div class="field">
          <label class="label" for="publishTopic">Publish Topic</label>
          <div class="control">
            <input class="input" type="text" id="publishTopic" placeholder="topic">
          </div>
        </div>
        <div class="field">
          <label class="label" for="publishMessage">Message</label>
          <div class="control">
            <input class="input" type="text" id="publishMessage" placeholder="message">
          </div>
        </div>
        <div class="control">
          <button class="button is-success" id="publishButton">Publish</button>
        </div>
      </div>

      <!-- Messages Area -->
      <div class="box">
        <h2 class="subtitle">Messages</h2>
        <div class="content" id="messages" style="height: 200px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px;"></div>
      </div>
    </div>
  </section>

  <script>
    let client;

    document.getElementById('connectButton').addEventListener('click', () => {
      const brokerUrl = document.getElementById('brokerUrl').value;
      const clientId = document.getElementById('clientId').value;
      client = mqtt.connect(brokerUrl, { clientId });

      client.on('connect', () => {
        console.log('Connected');
        document.getElementById('messages').innerHTML += '<p>Connected to broker</p>';
      });

      client.on('message', (topic, message) => {
        const msg = document.createElement('p');
        msg.textContent = `Topic: ${topic}, Message: ${message.toString()}`;
        document.getElementById('messages').appendChild(msg);
      });

      client.on('error', (err) => {
        console.log('Connection error: ', err);
        document.getElementById('messages').innerHTML += `<p>Connection error: ${err}</p>`;
      });
    });

    document.getElementById('subscribeButton').addEventListener('click', () => {
      const topic = document.getElementById('subscribeTopic').value;
      client.subscribe(topic, (err) => {
        if (err) {
          document.getElementById('messages').innerHTML += `<p>Subscription error: ${err}</p>`;
        } else {
          document.getElementById('messages').innerHTML += `<p>Subscribed to: ${topic}</p>`;
        }
      });
    });

    document.getElementById('publishButton').addEventListener('click', () => {
      const topic = document.getElementById('publishTopic').value;
      const message = document.getElementById('publishMessage').value;
      client.publish(topic, message, (err) => {
        if (err) {
          document.getElementById('messages').innerHTML += `<p>Publish error: ${err}</p>`;
        } else {
          document.getElementById('messages').innerHTML += `<p>Published to: ${topic}, Message: ${message}</p>`;
        }
      });
    });
  </script>
</body>
</html>
```

これを HTMLファイルとして保存してブラウザで開いたところ、以下のようにシンプルな MQTTクライアントが表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/200b31fa-4934-795b-d282-3292401c998a.png)

（完全に余談ですが、MQTTブローカーの接続先を入力するボックスの placeholder のところに、broker.hivemq.com と、[HiveMQ が提供するパブリックな MQTTブローカー](https://www.hivemq.com/mqtt/public-mqtt-broker/)が偶然出てきていました ← 他にも有名どころのパブリックな MQTTブローカーはいくつかあったと思われるけれど）

## クライアント 2種の間で MQTT を使った通信を試す
上記のクライアント 2種を使って、それらの間で MQTT を使った通信を試します。

今回、MQTTブローカーは PC にアプリをインストール済みだった、[shiftr.io のデスクトップアプリ](https://www.shiftr.io/desktop/)を使います。以下は起動画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d8375fca-dcc2-f311-af43-045ef6e77e66.png)

ここに、それぞれのクライアントから接続します。

### クライアントからの接続
#### HiveMQ により提供されている MQTT Websocket Client
HiveMQ により提供されている MQTT Websocket Client のほうから進めます。

以下のように「Host」「Port」のテキストボックスと、「SSL」のチェックボックスに以下の対応をします。ClientID はデフォルトで生成されたものを流用しました。

- 設定内容
   - Host： localhost
   - Port： 1884
   - SSL： チェックボックスを外す（wss ではなく ws でつなぐ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/82af9cb7-7304-fbc3-44a7-3bc14a34b899.png)

#### ChatGPT に作ってもらった MQTTクライアント
ChatGPT に作ってもらった MQTTクライアントのほうへ進みます。

こちらは、以下の内容を入力しました。基本的に、上記の HiveMQ により提供されている MQTT Websocket Client で設定した内容と、意味合い的には同じものです。

- 設定内容
   - Broker URL： ws://localhost:1884
   - Client ID： 適当な内容（※ 以下では id1234 としました）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2b9d1780-b364-c92f-1e73-5a0c3a72b456.png)

### 2つのクライアントを使った Pub/Sub
それでは、2つのクライアントを使った Pub/Sub を行います。

#### テスト1
まず、クライアントそれぞれで、以下の内容にしてみます。

- HiveMQ により提供されている MQTT Websocket Client： Publish
- ChatGPT に作ってもらった MQTTクライアント： Subscribe

以下が Publish の設定で、「Topic」「Message」を指定しました。
ここで指定した「Topic」を Subscribe側で指定して、その後に、こちらでの「Publish」ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/af1b7721-61e4-24ea-19f5-70bc7f322239.png)

以下が、Subscribe側で Publish された内容を受信できた時の様子です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/b545fcbe-b886-4155-49d4-34fe0b36c7f1.png)

MQTTブローカー上の表示でも、Pub/Sub が行われている様子が確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/521e808b-fbb1-4210-dd35-02505935c8e3.png)


#### テスト2
次は先ほどと逆方向を試します。

- HiveMQ により提供されている MQTT Websocket Client： Subscribe
- ChatGPT に作ってもらった MQTTクライアント： Publish

以下が Publish の設定で、「Topic」「Message」を指定しました。
「Topic」「Message」のどちらも、先ほどとは異なるものにしてみました。

先ほどと同様、ここで指定した「Topic」を Subscribe側で指定して、その後に、こちらでの「Publish」ボタンを押下します（※ 以下は、Publish　を行った後のキャプチャになってます）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ab078a80-843d-f50a-01e6-8e13762631b9.png)

以下が Subscribe側です。
Publish された内容を受信できているのが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/aa63fc4d-05f5-d7e0-6d4f-6f74784d5820.png)

さらに、MQTTブローカー上の表示でも、2回目のテストの内容が表示されているのが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/55075590-c2e7-d6d2-c885-13178f9cda64.png)

ブラウザで動かせる　MQTTクライアントを使った、MQTT による通信を試すことができました。