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

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

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

Webソケットを実装するとESP32-WROOM-32Eが取得したデータをクライアントのHTMLで使用することができます。温湿度データからJSON形式のデータを生成してWebソケットでHTMLに送信して履歴をグラフ表示する方法をまとめました。

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

ESP32-WROOM-32Eで温湿度データをグラフで表示する

上記記事で紹介している方法をHTMLファイルとJSファイルを分けて管理し、グラフ表示する方法を説明しています。ESP32-WROOM-32E(以下ではESP32とする)を使って動作確認したことをまとめています。

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

WebサーバーとWebソケットの関係

WebサーバーとWebソケットの関係
WebサーバーとWebソケットの関係

スマホなどのクライアントから接続要求を受けるとWebサーバーが応答してHTMLファイルで応答します。HTMLでJavaスクリプトを参照するように指定した場合はJavaスクリプト専用のJSファイルを呼び出します。

JSファイルはグラフを表示するためのChart.jsライブラリの設定やWebソケットの接続イベントの処理をまとめたファイルです。

ESP32が温湿度センサーから取得したデータをJSONデータに変換し、Webソケットでクライアントに返信することでクライアント側のHTMLは温湿度データを表示することができます。

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

Webソケット及びJSONデータを生成するためにArduino IDEにライブラリを追加して使用します。

スポンサーリンク

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, jsonXString); //クライアントへの応答
      break;
  }
}

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

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

クライアントからの接続要求に関わらず、ESP32の起点のイベントで非同期でクライアントにデータを送信する場合はESPAsyncWebSrvライブラリなどがあります。

広告

Arduino_JSONライブラリを追加する

Arduino_JSONライブラリの追加
Arduino_JSONライブラリの追加

JSONデータはJavaScriptのオブジェクトの書き方に対応したデータのフォーマットであり、C++などの言語でサポートされています。また文字列、数値、配列、bool(true,false)、オブジェクトに対応しているため幅広く使用されています。

Arduino環境ではB版(2023年12月下旬)としてArduino_JSON by Arduinoが公開されています。本記事ではArduino_JSONライブラリを追加して温湿度データをJSONデータに変換します。

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

#include <Arduino_JSON.h>

String jsonString;//JSONの文字列

void charDataSet(void){
  JSONVar json;
  uint16_t i;
  char temp_s[7];

  for(i=0; i < CHART_SZ; i++ ){
    json["X"][i] = i;
  }
  for(i=0; i < CHART_SZ; i++ ){
    sprintf(&temp_s[0], "%6.2f",tempdat[i]);
    json["Temp"][i] = temp_s;
  }

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

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

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

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

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

{“X”:[0,1,2,・・・・,599],”Temp”:[“23.16″,”23.16″,・・・・,”0.00”]}

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

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

スポンサーリンク

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

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

WebServer Wserver(80);

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

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

void HtmlSet2(void){

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

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

HTMLファイルを作成する

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

<!DOCTYPE html>
<html lang = "ja">
<head>
    <meta charset = "UTF-8">
    <title>Sensor SHT35</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-temp">--.--</code>
        <code>℃</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-temp”で指定した温度データが表示します。

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

広告

JSファイルを作成する

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

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;
    
    obj = JSON.parse(evt.data);//JSONファイルを展開
    xval = obj["X"];
    tempval = obj["Temp"];
    document.getElementById("latest-temp").innerHTML = tempval[0];

    for(i=0; i<600;i++){
        window.tempHumidChart.data.labels.push(xval[i]);
        window.tempHumidChart.data.datasets[0].data.push(tempval[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が”Temp”の配列をtempvalに格納します。

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

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

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

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-WROOM-32EとSHT35-DISの配線例を示しています。回路図の番号はESP32 -WROOM-32Eの左上を1ピンとした時反時計回りにピンを数えた場合の番号としています。ピン番号横の()内の番号はシルク印刷されているピンの名称です。

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

温湿度情報のグラフ表示の結果
温湿度情報のグラフ表示の結果

スマホのGoogle ChromeでIPアドレス「192.168.11.2」を入力しリクエストを送るとESP32-WROOM-32Eからスマホに返信し温湿度データと時間経過による温湿度情報がグラフ表示されました。

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

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

HTMLファイルとJSファイルの記述で表示するページが作成できるためStringで文字を追加してHTMLデータを生成する方法よりもページの作り込みができるようになります。CSSファイルを追加してページの見栄えを良くしたりできます。

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

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

ESP32のソースコード:

#include <Wire.h>
#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>
#include <Arduino_JSON.h>
#include <WebSocketsServer.h>

#define SLAVE_ADRS 0x45
#define POLYNOMIAL 0x31
#define PIN_DI  22
#define CHART_SZ 600
#define TIME_OFF -1 //タイマーを使用しない場合
#define TIME_UP 0 //タイムアップ
#define BASE_CNT 10 //ベースタイマカウント値
#define TIME_SHT35_MAX 6000 //SHT35の計測タイマ  
#define TIME_OUT_MAX 20  //SHT35の通信タイムアウト

typedef enum{
  SHT35_MEASURE = 0,
  SHT35_WAIT,
  SHT35_READ,       
  SHT35_MAX  
}SHT35_MODE;

const char *ssid = "EngKapi1"; //SSID
const char *pass = "22223333"; //password
const IPAddress ip(192,168,11,2); //IPアドレス
const IPAddress subnet(255,255,255,0); //サブネットマスク
const char* websockets_server_host = "serverip_or_name"; //Enter server adress
const uint16_t websockets_server_port = 81; // Enter server port

/* 変数宣言 */
WebServer Wserver(80);
WebSocketsServer webSocket = WebSocketsServer(81);

SHT35_MODE mode = SHT35_MEASURE;
uint32_t beforetimCnt = millis();
float temp;
float humi;
float tempdat[CHART_SZ];
float humiddat[CHART_SZ];

int16_t timSht35start;
int16_t  timSht35Out = TIME_OFF;
uint8_t singleshot[2] = { 0x2C, 0x06};
uint8_t chksum[2];
uint8_t rxdata[6];

String jsonString;

/* プロトタイプ宣言 */
void mainApp(void);
void Sht35Measure(uint8_t dev_id,uint8_t* cmd, uint16_t len);
bool Sht35GetData(uint8_t dev_id,uint8_t* reg_data, uint16_t len);
uint8_t Crc8Calc(uint8_t *data, uint8_t sz );
void HtmlSet(void);
void HtmlSet2(void);
void handleNotFound(void);
void charDataSet(void);

void setup() {

  Serial.begin(115200);
  Wire.begin();

  WiFi.softAP(ssid, pass); //WiFiのアクセスポイントの設定
  WiFi.softAPConfig(ip, ip, subnet); //アクセスポイントのIP及びサブネットマスク
  SPIFFS.begin();
  Wserver.on("/",HTTP_GET,HtmlSet2);
  Wserver.serveStatic("/", SPIFFS, "/"); //SPIFFSでアクセス
  Wserver.onNotFound(handleNotFound); //URLが存在しない場合の処理する関数を指定
  Wserver.begin(); //Webサーバーの開始
  webSocket.begin(); //Webソケットの開始
  webSocket.onEvent(webSocketEvent); //イベント登録
}
/* WebSocketパケット処理*/
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {

  switch(type) {
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected!\n", num);
      break;
    case WStype_CONNECTED:
      {
        IPAddress ip = webSocket.remoteIP(num);
        Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
        webSocket.sendTXT(num, jsonString);
      }
      break;
    case WStype_TEXT:
    case WStype_BIN:
    case WStype_ERROR:			
    case WStype_FRAGMENT_TEXT_START:
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
			break;
  }
}
/* クライアントから接続要求の応答 */
void HtmlSet2(void){

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

void loop() {

  mainTimer();
  mainApp();
  webSocket.loop();
  Wserver.handleClient();
}
/* タイマ管理 */
void mainTimer(void){

  if ( millis() - beforetimCnt > BASE_CNT ){
    beforetimCnt = millis();

    if( timSht35start > TIME_UP ){
      --timSht35start;
    }
    if( timSht35Out > TIME_UP ){
      --timSht35Out;
    }
  }
}
/* メイン処理関数 */
void mainApp(void){
    
  switch(mode){
    case SHT35_MEASURE:
      if( timSht35start == TIME_UP ){
        timSht35start = TIME_OFF;
        timSht35Out = TIME_OUT_MAX;
        Sht35Measure(SLAVE_ADRS, &singleshot[0], sizeof(singleshot));
        mode = SHT35_WAIT;
      }
      break;
    case SHT35_WAIT:
      if( digitalRead(PIN_DI) == 1){
        mode = SHT35_READ;
      }
      break;
    case SHT35_READ:   
      if( Sht35GetData(SLAVE_ADRS, &rxdata[0], sizeof(rxdata))){
        uint16_t tempHex;
        uint16_t humiHex;

        chksum[0] = Crc8Calc(&rxdata[0],2); //tempのCRCチェック
        chksum[1] = Crc8Calc(&rxdata[3],2); //humiのCRCチェック
            
        if( chksum[0] == rxdata[2] && chksum[1] == rxdata[5]){
          tempHex = ((uint16_t)rxdata[0] << 8) | rxdata[1];
          humiHex =((uint16_t)rxdata[3] << 8) | rxdata[4];
          temp = (tempHex / 65535.00) * 175 - 45;
          humi = (humiHex / 65535.0) * 100.0;

          Serial.print("temp: ");
          Serial.print(temp);
          Serial.print("℃  ");
          Serial.print("humi :");
          Serial.print(humi);
          Serial.print("%  ");
          Serial.println();
          charDataSet();
        }
            
        timSht35start = TIME_SHT35_MAX;
        timSht35Out = TIME_OFF;
        mode = SHT35_MEASURE;
       }
      break;
    default:
        break;
    }
    
  if( timSht35Out == TIME_UP ){
    timSht35Out = TIME_OFF;
    mode = SHT35_MEASURE;
    timSht35start = TIME_SHT35_MAX;
  }
}
/* SHT35へコマンド送出 */
void Sht35Measure(uint8_t dev_id,uint8_t* cmd, uint16_t len){

  Wire.beginTransmission(dev_id); //スレーブが存在するか確認
  byte error = Wire.endTransmission();
  if( error == 0){ //スレーブが存在する場合下の処理
    Wire.beginTransmission(dev_id);
    for( uint16_t i=0; i < len; i++ ){
      Wire.write(*cmd); //lenサイズ分だけデータを書き込む
      ++cmd;
    }
    Wire.endTransmission(); //ストップ・コンディションの発行
  }
}
/* SHT35からデータを取得 */
bool Sht35GetData(uint8_t dev_id,uint8_t* reg_data, uint16_t len){
  bool  ret = false;

  Wire.beginTransmission(dev_id); //スタート・コンディションの発行
  byte error = Wire.endTransmission();
  if( error == 0){ //スレーブが存在する場合下の処理
    if( Wire.requestFrom(dev_id, len) == len ){
      for( uint16_t i=0; i < len; i++ ){
        *reg_data = Wire.read(); //len分だけデータをリードする
        ++reg_data;
      }
      ret = true;
    }
  }
  return ret;
}
/* CRC8計算関数 */
uint8_t Crc8Calc(uint8_t *data, uint8_t sz ){
  uint8_t crc = 0xFF;
  uint8_t i,j;
    
  for( i = 0; i < sz; i++){
    crc ^= *data;
        
    for( j = 0; j < 8; j++ ){
      if( crc & 0x80 ){
        crc = ( crc << 1 ) ^ POLYNOMIAL;
      }
      else{
        crc = crc << 1;
      }
    }
    ++data;
  }
  return crc;
}
/* 温湿度データをセット*/
void charDataSet(void){
  JSONVar json;
  uint16_t i;
  char temp_s[7];
  char humi_s[6];

  memmove( &tempdat[1], &tempdat[0],(CHART_SZ-1)*sizeof(float));
  memmove( &humiddat[1], &humiddat[0],(CHART_SZ-1)*sizeof(float));
  tempdat[0] = temp;
  humiddat[0] = humi;

  for(i=0; i < CHART_SZ; i++ ){
    json["X"][i] = i;
  }
  for(i=0; i < CHART_SZ; i++ ){
    sprintf(&temp_s[0], "%6.2f",tempdat[i]);
    json["Temp"][i] = temp_s;
  }

  for(i=0; i < CHART_SZ; i++ ){
    sprintf(&humi_s[0], "%5.2f",humiddat[i]);
    json["Humid"][i] = humi_s;
  }

  jsonString = JSON.stringify(json);
  //Serial.println(jsonString.c_str());
  //Serial.println();
}

/* URLが存在しない場合の処理 */
void handleNotFound(void) {

  String message = "File Not Found\n\n";
  message += "URI: ";
  message += Wserver.uri();
  message += "\nMethod: ";
  message += (Wserver.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += Wserver.args();
  message += "\n";

  for (uint8_t i = 0; i < Wserver.args(); i++) {
    message += " " + Wserver.argName(i) + ": " + Wserver.arg(i) + "\n";
  }
  Wserver.send(404, "text/plain", message); //テキストファイルであることを示している。
}

HTMLファイルのソースコード:

<!DOCTYPE html>
<html lang = "ja">
<head>
    <meta charset = "UTF-8">
    <title>Sensor SHT35</title>
    <link rel="icon" href="myfavicon.ico">
    <script src ="chart.umd.js"></script>
    <script src ="chartText.js" defer ></script>
</head>
<body>
    <h1>ESP32-SHT35-DIS温湿度センサ</h1>
    <h2>WebServerライブラリを使用</h2>
    <h2>WebSocketsライブラリを使用</h2>

    <h2>温度:
        <code id="latest-temp">--.--</code>
        <code>℃</code>
    </h2>    
    <h2>湿度:
        <code id="latest-humi">--.--</code>
        <code>%</code>
    </h2> 
    <div class="height: 900px; width: 800px; margin: auto;">
        <canvas id="temp-humid-chart" class="chart"></canvas>
    </div>
</body>
</html>

JSファイルのソースコード:


var xval;
var tempval;
var humival;

const chartConfig = {
//以下にチャート表示するデータをセットしていく
    type: 'line',
    data: {
        labels: xval,

        datasets: [{
            type:'line',
            label: '温度',
            data: tempval,
            borderColor: 'red', //線の色
            backgroundColor: 'red', //下を塗りつぶした時の色
            fill: false, //プロット点から下を塗りつぶす
            borderWidth: 1, //線の幅
            pointRadius: 1, //プロットした点の大きさ
            pointHoverBorderWidth: 1, //プロットした点を選択した時に大きく表示するサイズ
            pointStyle: 'triangle', //プロットした点の表示(△)
            tension: 0.5, //線の伸縮
            yAxisID: 'y', //軸のスケールを選択
        },
        {
            type:'line',
            label: '湿度',
            data: humival,
            borderColor: 'blue', //線の色
            backgroundColor: 'blue', //下を塗りつぶした時の色
            fill: false, //プロット点から下を塗りつぶす
            borderWidth: 1, //線の幅
            pointRadius: 1, //プロットした点の大きさ
            pointHoverBorderWidth: 1, //プロットした点を選択した時に大きく表示するサイズ
            pointStyle: 'rect', //プロットした点の表示(△)
            tension: 0.5, //線の伸縮
            yAxisID: 'y2', //軸のスケールを選択
        }]
    },
    options: {
        responsive: true,
        
        //表示タイトルをつける場合 タイトルを含めたキャンパスの高さとなる
        plugins: { 
            title: {
                display: true,
                text: '温度湿度の変化',
                font: {
                    size: 20
                }
            },
        },
        //表示タイトルをつける場合--end--
        scales: {
            x: {
                display: true,
                ticks: {
                    display: true
                },
                //X軸のタイトルを入れる場合
                title: {
                    display: true,
                    text: 'プロット数/minute',
                    color: 'black',
                    font: {
                        size: 16
                    }
                },
    
                //X軸のタイトルを入れる場合--end--
                gridLineItems: {
                    display: true,
                }
            },
            y: {
                display: true, //y軸を表示するか
                min: 0,
                max: 100,
                ticks: {
                    display: true, //目盛りを表示するか
                    color: 'red',
                },
                //Y軸のタイトルを入れる場合
                title: {
                    display: true, //y軸のタイトルを表示するか
                    text: '温度[℃]',
                    color: 'red',
                    font: {
                        size: 16
                    }
                },
                //Y軸のタイトルを入れる場合--end--
            },
            y2: {
                display: true, //y2軸を表示するか
                min: 0,
                max: 100,
                position: 'right', //軸を右側に表示
                ticks:{
                    display: true,  //目盛りを表示するか
                    color: 'blue',
                    
                },
                //y2軸のタイトルを入れる場合
                title: {
                    display: true, //y2軸のタイトルを表示するか
                    text: '湿度[%]',
                    color: 'blue',
                    font: {
                        size: 16
                    }
                },
                //y2軸のタイトルを入れる場合--end--       
            }
        }
    }
};

/* 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;
    
    obj = JSON.parse(evt.data);//JSONファイルの展開
    xval = obj["X"];
    tempval = obj["Temp"];
    humival = obj["Humid"];

    document.getElementById("latest-temp").innerHTML = tempval[0];
    document.getElementById("latest-humi").innerHTML = humival[0];

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

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

関連リンク

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

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

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

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

広告
漠然としたキャリア形成の不安を打ち破る!

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

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