PR

ESP32-WROOM-32EのデータをWebソケットでクライアントに送信する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

Webソケットを実装するとESP32-WROOM-32Eが取得したデータをクライアントのHTMLで使用することができます。ESP32-WROOM-32Eを起点としてクライアントにデータを送信して履歴を更新する方法をまとめました。

ESP32-WROOM-32E開発ボード(秋月電子)を使用しArduino IDEで開発を行います。また、温湿度気圧センサーのAE-BME280(秋月電子)を使用しています。

ESP32-WROOM-32EにWebソケットを実装する

上記記事ではWebサーバーが応答すると同時にWebソケットを接続して履歴をグラフ表示しています。本記事ではWebソケットを接続している状態で温湿度気圧センサー(以下ではBME280とする)からデータを取得したタイミングで履歴のグラフを更新します。

ESP32-WROOM-32E(以下ではESP32とする)を使って動作確認したことをまとめています。

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

Webソケットでクライアントにデータを送信する

Webソケットでクライアントにデータを送信する構成
Webソケットでクライアントにデータを送信する構成

スマホなどのクライアントから接続要求を受けるとWebサーバーが応答してHTMLファイルで応答します。同時にWebソケットの接続要求により接続が確立します。

HTMLでJavaスクリプトを参照するように指定した場合はJavaスクリプト専用のJSファイルを呼び出します。JSファイルはグラフに必要なChart.jsライブラリの設定やWebソケットの接続イベントの処理をまとめたファイルです。

ESP32がBME280から取得したデータをJSONデータに変換し、Webソケットで送信するとクライアント側でBME280のデータを展開して履歴をグラフ表示することができます。

クライアントのブラウザーを閉じなければWebソケットが接続が維持されるためESP32を起点としてWebソケットを使用することができます。

BME280からデータを取得する定期的なタイミングでWebソケットで通信を行うことで履歴のグラフを更新して表示することができます。

スポンサーリンク

ライブラリを追加する

本記事で使用するWebソケット、BME280ライブラリ、Arduino_JSONライブラリを追加する必要があります。ライブラリの追加の方法と使用例を説明します。

WebSocketsライブラリ

WebSocketライブラリを追加
WebSocketライブラリを追加

Arduino IDEのライブラリマネージャの検索欄にWebsocketsを入力するとライブラリの候補が表示されます。候補の中からWebSockets by Markus Sattlerをインストールします。Webソケットの初期化の例は以下の通りです。

#include <WebSocketsServer.h>

WebSocketsServer webSocket = WebSocketsServer(81);

void setup() {

  webSocket.begin(); //Webソケットの初期化
  webSocket.onEvent(webSocketEvent); //イベント登録
}

void loop() {
  webSocket.loop(); //接続の待機
}

WebSocketをサーバーとして使用するためWebSocketsServer.hをインクルードします。WebSocketsServerクラスの変数としてwebSocketをインスタンス化して、WebSocketsServer(81)で81番のポートを使用する設定を行います。

WebSocketsServerクラスのbegin()関数でWebソケットの初期化を行います。onEvent()関数でWebソケットのイベントが発生した時に遷移させる関数を指定します。例ではイベント発生時にwebSoketEvent()関数に遷移します。

loop()関数でクライアントからの接続要求を待機します。

/* WebSocketパケット処理*/
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {

  switch(type) {
    case WStype_CONNECTED:      
      webSocket.sendTXT(num, jsonString); //クライアントへの応答
      break;
  }
}

クライアントから接続要求を受けるとwebSocketEvent()関数に遷移します。引数にパケットのタイプが格納されているので接続要求(WStype_CONNECTED)の場合にsendTXT()関数でクライアントへの応答を送信します。

sendTXT()関数の第1引数は、クライアントの受付番号、第2引数はペイロードのアドレスを指定します。例ではペイロードのアドレスは生成したJSONデータを指定しています。

if( webSocket.clientIsConnected(Clientnum)){//接続確認
  webSocket.sendTXT(Clientnum, jsonString);
}

clientIsConnected()関数はクライアントが接続しているかを確認します。引数には接続したクライアントの受付番号を指定します。

クライアントとWebソケットの接続が継続している場合はsendTXT()関数でJSONデータを送信します。

接続が継続しているWebソケットを使用することでESP32を起点としてクライアントにデータを送信することができます。

広告

Arduino_JSONライブラリを追加する

Arduino IDEのライブラリマネージャの検索欄にjsonを入力するとライブラリの候補が表示されます。候補の中からArduino_JSON by Arduinoをインストールします。

BME280から取得した温度湿度気圧のデータをJSON形式に変換するために使用します。使用例は以下の通りです。

#include <Arduino_JSON.h>

String jsonString;//JSONの文字列

void charDataSet(void){
  JSONVar json;
  uint16_t i;
  char pres_s[8];

  for(i=0; i < CHART_SZ; i++ ){
    json["X"][i] = i;
  }
  for(i=0; i < CHART_SZ; i++ ){
    sprintf(&pres_s[0], "%6.2f",bme280data[BME280_PRES][i]);
    json["Press"][i] = pres_s;
  }

  jsonString = JSON.stringify(json);//JSONファイル生成
}

Arduino_JSON.hをインクルードします。生成したJSONデータの文字列を格納して使用する場合はString型の変数を準備して格納します。

JSONVarクラスの変数としてjsonをインスタンス化してJSONデータの生成要素を指定します。例ではid:”X”の配列とid:”Press”の配列を生成するように指定しています。

Pressの配列は数値を指定するとfloatの型の数値が文字列として変換されるため、文字列が大きくなってしまいます。そのためsprintf()で小数点2桁に変換して文字列を削減しています。

floatの型の数値を100倍(小数点2桁を表示)してから整数の型にして文字列を削減する方法もありますが、JSファイルで数値を100で割る処理が必要になります。

JSONオブジェクトのstringify()関数でJSONデータに変換します。今回の例では以下のようなJSONデータの文字列が生成されます。変換後の文字列はString型の変数に格納して使用します。

{“X”:[0,1,2,・・・・,299],”Press”:[“1014.16″,”1014.18″,・・・・,”0.00”]}

“”で囲まれているのは文字列で、囲まれていないものは数値になります。HTMLで表示する場合はテキストデータで使用するため数値と文字列はどちらでも特に問題になりません。

生成したJSONデータをWebソケットで送信するとクライアントのJavaScriptがJSONデータを展開して使用するため温湿度気圧の履歴を表示することができます。

JSONデータは文字列で生成するため可能な限りサイズが大きくならないように管理することが必要です。サイズが大きくなるにつれてスタックオーバーの原因に繋がるため注意が必要です。

BME280ライブラリを追加する

Arduino IDEのライブラリマネージャの検索欄にbme280を入力するとライブラリの候補が表示されます。候補の中からGrove-Barometer Sensor BME280 by Seeed Studioをインストールします。使用例は以下の通りです

#include "Seeed_BME280.h"

BME280 bme280;

void setup() {

  if(!bme280.init()){
    Serial.println("Device error!");
  }
}
//データ取得の例
bme280Meas[BME280_TEMP] = bme280.getTemperature();//温度
bme280Meas[BME280_HUMI] = bme280.getHumidity();//湿度
press = bme280.getPressure();//気圧

Seeed_BME280.hをインクルードします。BME280クラスのbme280をインスタンス化して使用します。

BME280クラスのinit()関数でBME280の初期化を行います。BME280ライブラリのスレーブアドレスはデフォルトで0x76になっているため、AE-BME280のスレーブアドレスを0x76で使用する必要があります。

getTmeperature()関数は温度情報を取得します。getHumidity()関数は湿度情報を取得します。getPressure()関数は気圧情報を取得します。気圧の単位はPaなのでhPaで表示する場合は100で割った値に換算する必要があります。

スポンサーリンク

Webサーバーで表示するページを作成する。

Webサーバーの設定は以下の通りとします。WebサーバーでHTMLファイルとJSファイルを読み込んで使用します。

WebServer Wserver(80);

Wserver.on("/",HTTP_GET,HtmlSet);
Wserver.serveStatic("/", SPIFFS, "/"); //SPIFFSでアクセス

ポートを80番としon()関数で指定した関数であるHtmlSet()関数に遷移させます。serveStatic()関数はSPIFFSを使用してファイルにアクセスするための設定です。

void HtmlSet(void){

  File file = SPIFFS.open("/bme280Html.html", "r");
  Wserver.streamFile(file, "text/html"); //ファイルを読み込む
  file.close();
}

SPIFFSオブジェクトのopen()関数でbme280Html.htmlのファイルを開きます。WebサーバークラスのstreamFile()関数でファイルの内容をtext/htmlとして送信して応答します。ファイルの内容をすべて送信するとclose()関数でファイルを閉じます。

HTMLファイルを作成する

Webサーバーで読み込むファイルを作成します。HTMLデータはテキストドキュメントの拡張子をhtmlに変更すると作成できます。HTML形式でソースコードを記述してページを作成します。

<!DOCTYPE html>
<html lang = "ja">
<head>
    <meta charset = "UTF-8">
    <title>Sensor BME280</title>
    <link rel="icon" href="myfavicon.ico">
    <script src ="chart.umd.js"></script>
    <script src ="chartText.js" defer ></script>
</head>
<body>
    <h2>気圧:
        <code id="latest-press">--.--</code>
        <code>hPa</code>
    </h2>    
    <div class="height: 900px; width: 800px; margin: auto;">
        <canvas id="temp-humid-chart" class="chart"></canvas>
    </div>
</body>
</html>

<head></head>の間にタイトル、ファビコンのリンク設定、スクリプトファイル(JSファイル)のアドレスの指定を行います。

Webサーバーが指定しているアドレスにそれぞれのファイル参照できるようにアドレスパスを指定します。例では同一の場所に配置しているためファイル名を指定しています。

VScodeでHTMLファイルを編集するとアドレス指定に対しリンク先の表示をクリックしてファイルが開いてリンクの確認ができます。

<body></body>の間はコンテンツを追加します。気圧情報はJSファイルで関連付けしたid=”latest-press”で指定した気圧データが表示します。

<canvas></canvas>はJSファイルで関連付けしたid=”temp-humid-chart”で指定した履歴のグラフをキャンパスで描写します。

PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!

JSファイルを作成する

JSファイルはJavaScriptを記述します。JSファイルにはWebソケットで取得したJSONデータの展開とidの関連付けやChart.jsによるグラフ表示の設定などを行います。

var xval;
var tempval;

/* Chartのグラフを生成 */
window.onload = () => {
    let ctx = document.getElementById("temp-humid-chart").getContext("2d");
    window.tempHumidChart = new Chart(ctx, chartConfig);
};
/* SebSocketイベントのインスタンスを生成 */
var ws = new WebSocket('ws://' + window.location.hostname + ':81/');

/* WebSocketがメッセージを受信 */
ws.onmessage = function(evt){
    var i;
    var p0 = new Array();
    
    obj = JSON.parse(evt.data);//JSONファイルを展開
    xval = obj["X"];
    pressval = obj["Press"];
    document.getElementById("latest-press").innerHTML = pressval[0];
    window.tempHumidChart.data.datasets[2].data = p0;

    for(i=0; i<300;i++){
        window.tempHumidChart.data.labels.push(xval[i]);
        window.tempHumidChart.data.datasets[2].data.push(pressval[i]);
    }
    window.tempHumidChart.update();
};
/* WebSocketの廃棄 */
ws.onclose = function(evt) {
    ws.close();
};

フォームが読み込まれるとonLoad()プロパティでイベントを検出します。履歴のグラフを表示するChartの設定を行いtempHumidChartでインスタンス化します。

Chart()の第1引数にキャンパスのid(“temp-humid-chart”)を指定します。第2引数にグラフの設定(configデータ)を指定します。id及びグラフの設定が間違えていなければ履歴のグラフが表示されます。

WebSocket()でポートを指定してWebソケットを設定しwsでインスタンス化します。ESP32のWebソケットとポート番号を同一にするため81番と指定します。wsを操作してWebソケットのイベントを管理します。

Webソケットがデータを受信しイベントを検出するとonmessage()プロパティでイベントを検出します。ESP32が送信したJSONデータをJSONオブジェクトのparse()メソッドで展開します。

展開したデータからidが”X”の配列をxvalに格納し、idが”Press”の配列をpressvalに格納します。

document.getElementById()メソッドで指定したidの配列の0番目の値をセットすると最新のデータがidを介して引き渡させるため、HTMLでidを指定すると最新のデータを表示することができます。

グラフのデータをセットする前にグラフのデータをクリアします。Array()で新たな配列を生成しグラフのデータに指定することでグラフのデータが初期化できます。

グラフの設定(chartConfig)でlabelsは横軸なのでxvalの配列の値を順にpushしてセットします。グラフの設定でdata.datasets[0]は縦軸なのでpressvalの配列の値を順にpushしてセットします。履歴のサイズ分の300回繰り返すことでグラフの値がセットできます。

グラフのデータを初期化せずに受信したデータをpushすると前のグラフのデータが残っている状態でデータが追加されてしまいます。

update()関数でグラフを更新するとセットした値でグラフが表示できます。

PR:技術系の通信教育講座ならJTEX

FLASHにファイルを格納する

Flashに書き込むファイル
Flashに書き込むファイル

dataフォルダにHTMLファイル、JSファイル、ファビコン、グラフ表示用のChart.jsライブラリを配置します。

dataフォルダのファイルをFLASH領域に格納することでSPIFFSでそれぞれのファイルを読み出せるようになります。

下記記事ではSDカードを使用してファイルを書き込む方法やESP32 Sketch Data Uploadプラグインを使用してFLASHにファイルを書き込む方法をまとめています。

ESP32-WROOM-32EにSDカードのファイルをアップロードする

ESP32 Sketch Data Uploadプラグインを使用する場合は、Arduino IDEのバージョンが2.00未満のものを使用する必要があります。

動作確認

動作確認用の回路図
動作確認用の回路図

ESP32とBME280の配線例を示しています。回路図の番号はESP32の左上を1ピンとした時反時計回りにピンを数えた場合の番号としています。ピン番号横の()内の番号はシルク印刷されているピンの名称です。

BME280はSDOをVDDまたはGNDに接続するとスレーブアドレスを変更することができますが、BME280ライブラリは0x76がデフォルトになるためSDOをGNDに接続しています。

WiFiのアクセスポイント「EngKapi1」を選択しパスワードに「22223333」を入力してアクセスポイントに接続します。

温湿度気圧の履歴表示
温湿度気圧の履歴表示

スマホでGoogle ChromeでIPアドレス「192.168.11.2」を入力しリクエストを送ると、HTMLデータで応答し最新の温湿度気圧のデータ及び履歴のグラフが表示できます。

Webサーバーが応答すると同時にWebソケットも接続状態となるためESP32がBME280からデータを取得したタイミング(本記事では5秒毎にしている)でスマホのHTMLがWebソケットに応答して最新のデータ及び履歴が更新されます。

スマホのHTMLを確認するとプロット数が0から299に向かって履歴をプロットして表示していることが確認できました。

IPアドレス「192.168.11.2/ti」と存在しないURLを指定するとFile Not Foundが表示されることも確認しています。

アクセスポイント、パスワード、IPアドレスはソースコードで変更している場合は指定したものと置き換えて接続してください。

本記事の内容はデータの最新値の確認と最新値付近の履歴の確認をする方法に適しています。一方でトリガーを起点として履歴を保存している場合は、最新の値である必要がないためWebソケットで繰り返し更新する必要はありません。

PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!

ソースコード全体

ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。

リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。

ソースコードをダウンロード

本ソースコードでグラフが表示する場合、FLASHにファイルを格納するで説明しているように各種ファイルをFLASHに書き込む必要があります。

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました