WebSocketによる中継
spot-router
の解説です。本稿では、サードパーティのオープンソースソフトウェアを使用します。
サードパーティのソフトウェアについて、その詳しい使用方法を弊社からご案内することはいたしかねます。また、サードパーティのソフトウェアを使用されたことによるいかなる損害についても、弊社は一切の責任を負いません。
ソースコードの入手
GitHub (monowireless/spot-router) から入手できます。
システムの概要
spot-router は、TWELITE 親機が受信したデータに基づき出力した文字列(App_Wings の ModBus ASCII 形式)を WebSocket サーバへ転送します。
開発に必要なもの
-
無線LANゲートウェイ TWELITE SPOT
- 電源用 USB-C ケーブル
- USB AC アダプタ(1A 以上供給できるもの)
-
加速度センサー無線タグ TWELITE CUEなどの子機 (お持ちでない場合はご購入ください 👉 販売店一覧)
- CR2032 コイン電池 などの電源
-
USBアダプター TWELITE R3 (お持ちでない場合はご購入ください 👉 販売店一覧)
- 通信用 USB-C ケーブル
- 💻 WebSocket サーバ
- 💻 開発用コンピュータ
環境整備
IDE とツールチェインの導入
Arduino IDE 1.x による開発環境の構築方法 をご覧ください。
ライブラリの導入
はじめに、Arduino のスケッチブックの保存場所(Arduino IDE 環境設定に記載。例:C:\Users\foo\Documents\Arduino
) に libraries
フォルダがない場合は、これを作成します。
WebSocket ライブラリ
- GitHub (Links2004/arduinoWebSockets) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、
libraries
フォルダにarduinoWebSockets-<バージョン>
フォルダを配置します
プロジェクトファイルの入手
- GitHub (monowireless/spot-router) から Zip ファイルをダウンロードします
- Zip ファイルを展開し、フォルダ名を
spot-router-main
からspot-router
に変更します - 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.h | Arduino の基本ライブラリ | 省略できる場合もあるが念のため記載 |
WiFi.h | ESP32 の WiFi を使う |
サードパーティのライブラリ
8行目では、サードパーティのライブラリをインクルードしています。
#include <WebSocketsClient.h>
ヘッダファイル | 内容 | 備考 |
---|---|---|
WebSocketsClient.h | WebSocket クライアントになる |
MWings ライブラリ
11行目では、MWings ライブラリをインクルードしています。
#include <MWings.h>
ユーザ設定の定義
14行目では、config.h
をインクルードしています。
#include "config.h"
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_RST | TWELITE の RST ピンが接続されているピンの番号 |
TWE_PRG | TWELITE の PRG ピンが接続されているピンの番号 |
LED | 基板上の ESP32 用 LED が接続されているピンの番号 |
ESP_RXD1 | TWELITE の TX ピンが接続されているピンの番号 |
ESP_TXD1 | TWELITE の 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_CH | TWELITE の 周波数チャネル |
TWE_APPID | TWELITE の アプリケーション ID |
TWE_RETRY | TWELITE の 再送回数(送信時) |
TWE_POWER | TWELITE の 送信出力 |
グローバルオブジェクトの宣言
30行目では、グローバルオブジェクトを宣言しています。
WebSocketsClient webSocket;
名称 | 内容 |
---|---|
webSocket | WebSocket クライアントのインタフェース |
関数プロトタイプの宣言
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.");
}
引数 | 型 | 内容 |
---|---|---|
Serial2 | HardwareSerial& | TWELITE との通信に使うシリアルポート |
LED | int | ステータス LED を接続したピンの番号 |
TWE_RST | int | TWELITE の RST ピンを接続したピンの番号 |
TWE_PRG | int | TWELITE の PRG ピンを接続したピンの番号 |
TWE_CHANNEL | uint8_t | TWELITE の 周波数チャネル |
TWE_APP_ID | uint32_t | TWELITE の アプリケーション ID |
TWE_RETRY | uint8_t | TWELITE の 再送回数(送信時) |
TWE_POWER | uint8_t | TWELITE の 送信出力 |
イベントハンドラの登録
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 子機として設定したうえで、指定のネットワークへ接続しています。
while
文を出ません。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バイトずつ読み出す関数です。
loop()
内で繰り返し Twelite.update()
を呼ぶことで、TWELITE 親機から送信されるパケットデータの解釈が進みます。パケットデータの解釈を終えた際に 上記 のようなイベントが呼ばれる仕組みです。delay()
などの処理でこの関数の呼び出しをブロックすると、パケットデータ文字列の読み出しが間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、loop()
関数をできるだけ高速回転させるようにしてください。WebSocket のデータの更新
103行目では、WebSocket のデータを更新する処理を呼び出しています。
webSocket.loop();
Twelite.update()
と同様に、delay()
などの処理でこの関数の呼び出しをブロックすると、データ更新が間に合わないことがあります。時間のかかる処理は必ず非同期の実装として、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
- 公式サイト:Arduino - Home
ESP32
- 製品情報:ESP32 Wi-Fi & Bluetooth MCU I Espressif Systems
- データシート:esp32_datasheet_en.pdf
- Arduino 向けツールチェイン:espressif/arduino-esp32: Arduino core for the ESP32
- スタートガイド:Getting Started — Arduino-ESP32 documentation
- 導入方法:Installing — Arduino-ESP32 documentation
- API リファレンス:Libraries — Arduino-ESP32 documentation
- Wi-Fi API:Wi-Fi API — Arduino-ESP32 documentation
- チュートリアル:Tutorials — Arduino-ESP32 documentation
- トラブルシューティング:Troubleshooting — Arduino-ESP32 documentation