/

WebSocketによる中継

最新版
無線 LAN 子機として振る舞い、LAN 上の WebSocket サーバに受信したパケットデータ文字列を中継するサンプルスケッチ spot-router の解説です。

ソースコードの入手

GitHub (monowireless/spot-router) から入手できます。

システムの概要

spot-router は、TWELITE 親機が受信したデータに基づき出力した文字列(App_Wings の ModBus ASCII 形式)を WebSocket サーバへ転送します。

開発に必要なもの

環境整備

IDE とツールチェインの導入

Arduino IDE 1.x による開発環境の構築方法 をご覧ください。

ライブラリの導入

はじめに、Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino) に libraries フォルダがない場合は、これを作成します。

WebSocket ライブラリ

  1. GitHub (Links2004/arduinoWebSockets) から Zip ファイルをダウンロードします
  2. Zip ファイルを展開し、libraries フォルダに arduinoWebSockets-<バージョン> フォルダを配置します

プロジェクトファイルの入手

  1. GitHub (monowireless/spot-router) から Zip ファイルをダウンロードします
  2. Zip ファイルを展開し、フォルダ名を spot-router-main から spot-router に変更します
  3. Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino)に spot-router フォルダを配置します

ユーザ設定の変更

Arduino IDE 上部のタブから config.h を開き、無線 LAN や WebSocket サーバに関する設定( 詳細 )を変更してください。

プロジェクトファイルの書き込み方法

ESP32 へのスケッチの書き込み方法 をご覧ください。

スケッチ

Arduino スケッチ spot-router.ino の解説です。

ライブラリのインクルード

Arduino および ESP32 公式ライブラリ

4-5行目では、Arduino および ESP32 の公式ライブラリをインクルードしています。

#include <Arduino.h>
#include <WiFi.h>
ヘッダファイル内容備考
Arduino.hArduino の基本ライブラリ省略できる場合もあるが念のため記載
WiFi.hESP32 の WiFi を使う

サードパーティのライブラリ

8行目では、サードパーティのライブラリをインクルードしています。

#include <WebSocketsClient.h>
ヘッダファイル内容備考
WebSocketsClient.hWebSocket クライアントになる

MWings ライブラリ

11行目では、MWings ライブラリをインクルードしています。

#include <MWings.h>

ユーザ設定の定義

14行目では、config.h をインクルードしています。

#include "config.h"

無線 LAN 設定の定義

config.h の4-5行目では、TWELITE SPOT に搭載された ESP32 に適用する無線 LAN 設定を定義しています。

const char* WIFI_SSID = "YOUR SSID";            // Modify it
const char* WIFI_PASSWORD = "YOUR PASSWORD";    // Modify it
名称内容
WIFI_SSID接続するネットワークの SSID
WIFI_PASSWORD接続するネットワークの パスワード

WebSocket 設定の定義

config.h の8-10行目では、WebSocket クライアントの設定を定義しています。

const char* WS_SERVER_IP = "YOUR ADDRESS";    // Modify it
const int WS_SERVER_PORT = 8080;
const char* WS_SERVER_PATH = "/";
名称内容
WS_SERVER_IP送信するサーバの IP アドレス
WS_SERVER_PORT送信するサーバのポート番号
WS_SERVER_PATH送信するサーバの WebSocket サーバのパス

ピン番号の定義

17-21行目では、ピン番号を定義しています。

const uint8_t TWE_RST = 5;
const uint8_t TWE_PRG = 4;
const uint8_t LED = 18;
const uint8_t ESP_RXD1 = 16;
const uint8_t ESP_TXD1 = 17;
名称内容
TWE_RSTTWELITE の RST ピンが接続されているピンの番号
TWE_PRGTWELITE の PRG ピンが接続されているピンの番号
LED基板上の ESP32 用 LED が接続されているピンの番号
ESP_RXD1TWELITE の TX ピンが接続されているピンの番号
ESP_TXD1TWELITE の RX ピンが接続されているピンの番号

TWELITE 設定の定義

24-27行目では、TWELITE SPOT に搭載された TWELITE 親機に適用する設定を定義しています。

const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
const uint8_t TWE_RETRY = 2;
const uint8_t TWE_POWER = 3;
名称内容
TWE_CHTWELITE の 周波数チャネル
TWE_APPIDTWELITE の アプリケーション ID
TWE_RETRYTWELITE の 再送回数(送信時)
TWE_POWERTWELITE の 送信出力

グローバルオブジェクトの宣言

30行目では、グローバルオブジェクトを宣言しています。

WebSocketsClient webSocket;
名称内容
webSocketWebSocket クライアントのインタフェース

関数プロトタイプの宣言

33行目では、関数プロトタイプを宣言しています。

String createPacketStringFrom(const BarePacket& packet);
名称内容
createPacketStringFrom()受信したパケットデータから書式文字列を再構築します

TWELITE の設定

42-47行目では、Twelite.begin() を呼び出し、TWELITE SPOT に搭載された TWELITE 親機の設定と起動を行っています。

Serial2.begin(115200, SERIAL_8N1, ESP_RXD1, ESP_TXD1);
    if (Twelite.begin(Serial2,
                      LED, TWE_RST, TWE_PRG,
                      TWE_CH, TWE_APPID, TWE_RETRY, TWE_POWER)) {
        Serial.println("Started TWELITE.");
    }
引数内容
Serial2HardwareSerial&TWELITE との通信に使うシリアルポート
LEDintステータス LED を接続したピンの番号
TWE_RSTintTWELITE の RST ピンを接続したピンの番号
TWE_PRGintTWELITE の PRG ピンを接続したピンの番号
TWE_CHANNELuint8_tTWELITE の 周波数チャネル
TWE_APP_IDuint32_tTWELITE の アプリケーション ID
TWE_RETRYuint8_tTWELITE の 再送回数(送信時)
TWE_POWERuint8_tTWELITE の 送信出力

イベントハンドラの登録

49-54行目では、すべてのアプリの子機からのパケットを受信した際に行う処理を登録しています。

Twelite.on([](const BarePacket& packet) {
    String packetStr = createPacketStringFrom(packet);
    if (not(packetStr.length() <= 0)) {
        webSocket.sendTXT(packetStr.c_str());
    }
});

ここでは、パケットデータから書式文字列(ModBus ASCII 形式)を再構成し、WebSocket サーバへ送信しています。

無線 LAN の設定

57-71行目では、無線 LAN の設定を行っています。

WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
    static int count = 0;
    Serial.print('.');
    delay(500);
    // Retry every 5 seconds
    if (count++ % 10 == 0) {
        WiFi.disconnect();
        WiFi.reconnect();
        Serial.print('!');
    }
}

ここでは、無線 LAN 子機として設定したうえで、指定のネットワークへ接続しています。

WebSocket の設定

76-77行目では、WebSocket を設定しています。

webSocket.begin(WS_SERVER_IP, WS_SERVER_PORT, WS_SERVER_PATH);
webSocket.setReconnectInterval(5000);

ここでは、WebSocket サーバと再接続間隔を指定しています。

また、78-97行目では、サーバとの接続が切断されたとき、サーバと接続したとき、そしてメッセージを受信したときのイベントを登録しています。

webSocket.onEvent([](WStype_t type, uint8_t* payload, size_t length) {
    switch (type) {
    case WStype_DISCONNECTED: {
        Serial.println("Disconnected!");
        break;
    }
    case WStype_CONNECTED: {
        Serial.print("Connected to url: ");
        Serial.println(reinterpret_cast<char*>(payload));
        webSocket.sendTXT("This is TWELITE SPOT to ground control");
        break;
    }
    case WStype_TEXT: {
        Serial.print("Got text: ");
        Serial.println(reinterpret_cast<char*>(payload));
        break;
    }
    default: break;
    }
});

なかでも、サーバと接続したときには、サーバへメッセージを送るようにしています。

webSocket.sendTXT("This is TWELITE SPOT to ground control");

TWELITE のデータの更新

102行目では、Twelite.update() を呼び出しています。

Twelite.update();

Twelite.update() は、TWELITE 親機から送信されるパケットデータ(ModBus ASCII 形式)を順次1バイトずつ読み出す関数です。

WebSocket のデータの更新

103行目では、WebSocket のデータを更新する処理を呼び出しています。

webSocket.loop();

<付録> WebSocket サーバによる動作確認

extra/python-websocket-server/server.py は、Python スクリプトによって WebSocket サーバを立て、ESP32 からのパケットデータ文字列を表示するサンプルスクリプトです。このスクリプトを使うことで、スケッチの動作を確認できます。

# -*- coding: utf-8-unix -*-
# Python 3.11

import logging
from websocket_server import WebsocketServer

def new_client(client, server):
    server.send_message_to_all("This is ground control to TWELITE SPOT")

def new_message(client, server, message):
    print("Received an message:")
    print(message)

server = WebsocketServer(host="YOUR IP ADDRESS", port=8080, loglevel=logging.INFO)
server.set_fn_new_client(new_client)
server.set_fn_message_received(new_message)
server.run_forever()

coding 変数を指定しているのは、筆者の環境が Emacs だからです。おまじないではありません。

動作確認の手順

スクリプトの実行

依存モジュール をインストールしてから実行します。


pip3 install websocket-server
python3 server.py

実行すると、下記のようなメッセージが表示されます。

INFO:websocket_server.websocket_server:Listening on port 8080 for clients..
INFO:websocket_server.websocket_server:Starting WebsocketServer on main thread.

クライアントの接続を確認

ESP32 が 無線 LAN への接続に成功すると、WebSocket サーバへの接続を試みます。

接続に成功すると、クライアント側のシリアルコンソールには下記のように出力されます。

Started TWELITE.
Connecting to WiFi .....
Connected. IP: xxx.xxx.xxx.xxx
Connected to url: /
Got text: This is ground control to TWELITE SPOT

一方で、サーバ側のターミナルには下記のように出力されます。

Received an message:
This is TWELITE SPOT to ground control

以降、TWELITE SPOT が子機からのパケットを受信すると、下記のようにサーバ側のターミナルへパケットデータ文字列が出力されます。

Received an message:
:80000000DE10098201BC8201800607003400038135001205350401000000113008020A8C1130010203AF0000000180050100020AC60102000211D7AF30

Received an message:
:80000000E4100A8201BC8201800607003400038135001205350401000000113008020A8C1130010203AC0000000180050100020AC40102000211DB0DCC

関連情報

TWELITE

Arduino

ESP32

コミュニティ

ライブラリ

プラグイン

ネットワーク関連

WebSocket

コミュニティ