EasyWebSocket ライブラリの使い方解説

WebSocket

前回の記事ではArduino core for ESP8266 の自作ライブラリ、EasyWebSocket のインストールや設定方法を紹介しました。
今回はそのプログラミング方法を紹介したいと思います。

ライブラリをバージョンアップしました。
現在 BETA 1.3 です。
こちらのページで紹介してます。再接続しやすくなり、更に使いやすくなりました。
以下、古い記事ですのでご了承ください。
2016/2/20

その前にちょっとした課題点があります。
これにはSPIFFSファイルシステムを使っていますが、5つのHTMLテキストファイルと、ユーザーが書き込んだSSIDやパスワードなどの文字列をマージして読み出しているので、コネクション確立に少々時間がかかってしまいます。
ユーザーがHTMLを改変しないのであれば、ライブラリ内にHTMLも含んだ方が良いかもしれません。インストールがちょっと手間というのもあり、SPIFFSファイルシステムは別の方法で使った方が良いかもしれません。幸い、ESP-WROOM-02には膨大なSRAMがあるので、ライブラリにHTMLを組み込んでも問題ないような気がします。ユーザーは編集しにくいですが、より簡単に早く実行できますからね。この辺は次期バージョンアップで対応したいと思います。

では、プログラミング方法を解説していきます。
ちなみに、このライブラリ作成初心者の私が作成したので、いろいろと問題が有るかも知れません。動作を補償するものではありませんのでご了承ください。あくまでベータのベータ版です。

スポンサーリンク

1.EasyWebSocketライブラリの概要

EasyWebSocketライブラリはGitHubの こちら にあります。

これはブラウザのボタンやスライダー、テキスト表示に特化したライブラリです。

みなさんも既にご存知かと思いますが、GitHub に公開してある ESP8266 Community Forum にある Arduino core for ESP8266 ライブラリを使用していて、それをincludeしています。

ご自分のアクセスポイント(ルーター)のSSIDとパスワード、ESP-WROOM-02 ( ESP8266 )のローカルIPアドレスを入力するだけで、WebSocketハンドシェイク(コネクション確立)が可能です。

ブラウザへ送信するHTML文のヘッダやBODY要素まではライブラリ内、およびサンプルスケッチEasyWebSocket_Testフォルダの中のdataフォルダにある5つのHTMLテキストファイルを連結(マージ)して出力します。これには前回の記事で述べたSPIFFSファイルシステムを使用して、予めWROOM(ESP8266)のフラッシュメモリにアップロードしておく必要があります。これをしないと動作しませんのでご注意ください。
ということで、ユーザーはArduinoスケッチ内ではBODY要素内のHTMLタグをString型の文字列で記入するだけでOKです。
しかも、そのHTMLタグ内のボタンやスライダー、テキスト表示タグをライブラリ化していますので、HTML文が良く分からない方にもある程度簡単にプログラムが組めるようにしてみました。

HTMLタグとESP-WROOM-02からのデータはテキスト文字列をIDとして関連付けています。
このID文字列でどのボタンが押されたかなどを振り分けることができます。

WROOMからPingを送信して、ブラウザからPongを受信し、Pongが無いとWROOMからコネクション切断します。
その数秒後に再コネクション可能です。

ネットワークやスマートフォンの性能によっては、スライダーなどの連続データ送信によって通信切断が起きる場合があります。
その場合、スマートフォンからの間引き送信によって、不意の通信切断を防ぐことができます。
ただ、その場合、操作の追従性は悪くなります。

2.クラスおよびプログラミング使用方法

前回の記事でもあげましたが、今回も一応サンプルスケッチを記載しておきます。
これをご覧いただきながら解説していきます。

ライブラリをバージョンアップしました。
現在 BETA 1.3 です。
こちらのページで紹介してます。再接続しやすくなり、更に使いやすくなりました。
2016/2/20

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <EasyWebSocket.h>
#include <ESP8266WiFi.h>
#include <Hash.h>

const char* ssid = "xxxx";
const char* password = "xxxx";
const char* LocalIPaddress = "xxx.xxx.xxx.xxx";

long CountTestTime;

byte cnt = 0;

EasyWebSocket ews;

String html_str;
String ret_str;
String view_scale = "1.1";

byte s_rate = 10;
int PingSendTime = 3000;

#define ledPin1 12
#define ledPin2 13
#define ledPin3 14

void setup() 
{

  html_str = ews.EWS_Body_style("WHITE", "BLACK");
  html_str += "<h2 style='color:#5555FF'><center>ESP-WROOM-02(ESP8266)<br>\r\n";
  html_str += "EasyWebSocket</center></h2>\r\n";
  html_str += "from WROOM DATA = \r\n";
  html_str += "<font size=4>\r\n";
  html_str += ews.EWS_Receive_Text("text","25px","RED");
  html_str += "<br><br>";
  html_str += "BLUE \r\n";
  html_str += ews.EWS_OnOff_Button("BLUE",60,25,15,"#FFFFFF","#0000FF");
  html_str += ews.EWS_Slider_BoxText("vbox1",30,20,15,"BLACK");
  html_str += ews.EWS_Touch_Slider("BLUE", "vbox1");
  html_str += "<br><br>\r\n";
  html_str += "GREEN \r\n";
  html_str += ews.EWS_OnOff_Button("GREEN",60,25,15,"#000000","#00FF00");
  html_str += ews.EWS_Slider_BoxText("vbox2",30,20,15,"#000000");
  html_str += ews.EWS_Touch_Slider("GREEN", "vbox2");
  html_str += "<br><br>\r\n";
  html_str += "RED\r\n";
  html_str += ews.EWS_OnOff_Button("RED",60,25,15,"#FFFFFF","#FF0000");
  html_str += ews.EWS_Slider_BoxText("vbox3",30,20,15,"BLACK");
  html_str += ews.EWS_Touch_Slider("RED", "vbox3");
  html_str += "<br><br>\r\n";
  html_str += "B-G-R\r\n";
  html_str += ews.EWS_Slider_BoxText("vbox4",40,20,15,"BLACK");
  html_str += ews.EWS_Touch_Slider("BGR", "vbox4") ;
  html_str += "<br><br>\r\n";
  html_str += "<div id='msg' style='font-size:25px; color:#FF0000;'></div><br>\r\n";
  html_str += ews.EWS_Close_Button("WS CLOSE",150,40,17);
  html_str += "<br>\r\n";

  
  ews.AP_Connect(ssid, password);

  ews.EWS_HandShake(LocalIPaddress, view_scale, s_rate, html_str);
  
  CountTestTime = millis();
}

void loop() {
  String str;
  if(ret_str != "_close"){
    if(millis()-CountTestTime > 500){
      if(cnt > 3){
        cnt = 0;
      }
      switch(cnt){
        case 0:
          str = "Hello! World";
          break;
        case 1:
          str = "WebSocket";
          break;
        case 2:
          str = "How are you";
          break;
        case 3:
          str = "Fine!!";
          break;
      }
      
      ews.EWS_String_DATA_SEND(str, "text");
      CountTestTime = millis();
      cnt++;
    }

    ret_str = ews.EWS_Char_Receive(PingSendTime);
    
    if(ret_str != "\0"){
      String led_id = ret_str.substring(ret_str.indexOf("|")+1, ret_str.indexOf(";"));
      Serial.print("ret_str = ");
      Serial.println(ret_str);
      Serial.print("led_id=");
      Serial.println(led_id);
      
      byte ws_data = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30);
      LED_PWM(led_id, ws_data);
    }
  }else if(ret_str == "_close"){
    delay(100);
    ews.EWS_HandShake(LocalIPaddress, view_scale, s_rate, html_str);
    CountTestTime = millis();
    ret_str = String('\0');
  }
}

void LED_PWM(String str_id, byte data_b)
{
  if(str_id == "BLUE"){
    analogWrite(ledPin1, data_b*2.5);
  }
  if(str_id == "GREEN"){
    analogWrite(ledPin2, data_b*2.5);
  }
  if(str_id == "RED"){
      analogWrite(ledPin3, data_b*2.5);
  }
  if(str_id == "BGR"){
    if(data_b < 34){
      analogWrite(ledPin1, data_b*8);
      analogWrite(ledPin2, 0);
      analogWrite(ledPin3, 0);
    }else if(data_b > 33 && data_b < 67){
      analogWrite(ledPin2, (data_b-33)*8);
      analogWrite(ledPin1, 0);
      analogWrite(ledPin3, 0);
    }else if(data_b > 66){
      analogWrite(ledPin3, (data_b-66)*7.78);
      analogWrite(ledPin1, 0);
      analogWrite(ledPin2, 0);
    }
  }
}

まずはライブラリのそれぞれのクラスの使い方を説明します。

5~7行目ではルーターのSSID、パスワード、WROOMのローカルIPアドレスを入力します。
const char* というポインタで宣言しなければなりません。

15行目ではブラウザへ送信するHTML文字列を宣言してます。
HTTPレスポンスヘッダ、HTML内のJavaScriptのWebSocket設定などのヘッダなどは、事前にSPIFFSファイルシステムアップローダーでフラッシュメモリにアップした5つのファイルを読み込んでライブラリから出力していますので、ユーザーは<body>要素内のHTMLだけ作ればOKです。それが29~57行目になります。
これはグローバル変数領域でString文字列を宣言します。なぜならば、コネクション切断されて再接続するときにこの文字列が再び必要になるからです。

17行目では、ブラウザ表示サイズを定義していて、62行目のクラスで使用します。

13行目:
EasyWebSocket ews;

ews がクラス名です。これは任意に設定可能です。この宣言はグローバル変数領域で宣言してください。

29行目;
ews.EWS_Body_style(“文字色”, “背景色”);

ews. が先ほど宣言したクラス名です。
これはHTMLの<body>要素のタグをライブラリ化しただけです。つまり、String型のbodyタグを返します。
ですから、ブラウザ表示全体の文字色、背景色を決めます。

34行目:
ews.EWS_Receive_Text(“ID文字列”,”文字の大きさ”,”文字色”);

これはWROOMからのデータをブラウザが受信したテキストを表示するためのHTMLタグ文字列を返します。
これは実はネーミング間違えてしまいました。勘違いしてWROOMが受信するデータと思っちゃいますね。次期バージョンアップで変更したいと思います。失礼しました・・・。
ID文字列では、WROOMから送られて来るデータのID文字列と一致すれば、その文字列をブラウザに表示します。89行目でWROOMから送信するデータのID文字列を”text”としています。
文字の大きさは 25px のように “px” をつけます。これはHTMLでは一般的ですね。
文字色は”RED”でもいいですが、”#FF0000″でもよいです。

37行目:
ews.EWS_OnOff_Button(“ID文字列”,ボタン幅,ボタン高さ,文字サイズ,”文字色”,”ボタン色”);

サンプルスケッチではBLUEとかありますが、ボタンの色ではありません。ID文字列です。この文字列がブラウザから送られてくるときに ‘|’ という文字を挟んでデータとマージして送られてきます。ONにすると100というデータ。OFFは0というデータを送ります。終端は ‘;’ というセミコロンです。
100|BLUE;
という感じの連結文字列で送られてきます。
それを94行目で受信して、’|’という文字で分割してデータを取り出すわけです。
色の指定方法は先ほど紹介したものと同じですが、サイズの数値は文字列ではなく、数値となっています。統一してなくてスイマセンm_ _m。次期バージョンアップで対応したいと思います。

38行目:
ews.EWS_Slider_BoxText(“ボックスID文字列”,ボックス幅,ボックス高さ,文字サイズ,”文字色”);

これは、ブラウザのスライダーを動かした場合にボックステキストに数値を0-100の間で表示するものです。
このID文字列はスライダーのID文字列と同じにしてください。

39行目:
ews.EWS_Touch_Slider(“ID文字列”, “ボックスID文字列”);

これはブラウザにスライダーを表示させ、スライダー値を0-100の間でWROOMへ送信します。
ボタンのデータ送信と同様、文字 ‘|’ で区切られたデータを送信して、終端は ‘;’ です。
87|BLUE;
という連結文字列をWROOMに送信します。
ID文字列でWROOM側でどのスライダーが操作されたかを判断します。
ボックスID文字列で38行目のボックステキストの数値と連動します。
因みに、これはマウスでは動作しません。タッチパネル専用です。

56行目:
ews.EWS_Close_Button(“表示文字”,ボタン幅,ボタン高さ,文字サイズ);

これは、ブラウザ側のボタンでWebSocket通信を切断するものです。これはIDは不要で、単独使用です。
これを押すとcloseコマンドがWROOMに送信されます。

60行目:
ews.AP_Connect(ssid, password);

これはWROOMの電源を立ち上げたら、ルーターに接続するクラスです。

62行目:
ews.EWS_HandShake(LocalIPaddress, view_scale, s_rate, html_str);

これはブラウザとWebSocketのハンドシェイク(コネクション確立)させるためのクラスです。
ここでブラウザからのGETリクエストにHTTPレスポンスヘッダを返し、WebSocketスクリプトを含んだHTMLタグをブラウザに送信します。
view_scaleはHTMLの<META>タグに記述されます。つまり、全体の画面サイズを決めます。

重要なのが s_rate です。
ブラウザのスライダー値など、連続したデータをブラウザから送信する場合、ネットワークのトラフィックがオーバーフローする可能性があります。
この数値をゼロにするとデータの間引きはありません。
単位はミリセコンド(ms)です。
つまり、50にしたら 50ms の間、ブラウザからデータ送信しません。
そうすると、追従性が悪くなりますが、不意の通信切断を防ぐことができます。
スマートフォンが非力なCPUやメモリだったりすると、これの値を大きくするといいと思います。
スマートフォンから送信している間、WROOMからもデータ送信しているわけですから、トラフィックにかなり影響される要素です。
この辺はまだ改善しなければいけないと思っていますが、どうしたらいいかまだ分からない状態です。解る方がいたら是非教えていただきたいですね。
ちなみに、この間引きプログラムはブラウザ側のJavaScriptで組んでいます。

89行目:
ews.EWS_String_DATA_SEND(str, “ID文字列”);

これは、WROOMからブラウザにデータを送信するクラスです。
strはString型で、最大127文字です。
ID文字列では34行目の
ews.EWS_Receive_Text(“text”,”25px”,”RED”);
というクラスでブラウザ側のHTMLタグのIDと関連付けています。そこに文字が表示されます。

94行目:
ews.EWS_Char_Receive(PingSendTime);

これはブラウザから送信されてくる文字列を受信して、マスク処理を外して実文字列を抽出する関数です。WebSocket通信ではこのマスク処理がキモです。
PingSendTimeではWROOMからミリセコンド単位でブラウザにPing送信する間隔を決めます。ここでは3秒間隔(3000ms)で設定してます。
Ping送信して、ブラウザからPongが返って来なければWROOM側で通信を切断するようにしてます。Ping送信の間隔が短すぎるとトラフィックに影響してきますので、十分長く取ってください。3秒くらいが適当かと思います。

97~104行目でブラウザからの文字列を実データとID文字列とを分割して、LEDをPWMコントロールして調光しています。

108行目では通信切断されたときにブラウザのURL再入力で再接続できるようにハンドシェイククラスを持ってきています。

※以前のサンプルスケッチで、res_htmlという不要な引数を削除してますので、2行減っています(2015/12/2)

以上が、ライブラリのクラスの使用方法およびプログラミング方法です。

初めにも言いましたが、私はライブラリ作成初心者ですので、いろいろ不具合や誤り等があるかも知れません。
もし気付いたことがありましたらコメントやお問い合わせフォームでご連絡いただけると幸いです。

今後、このライブラリは予告なくバージョンアップやバグ修正しますのでご了承ください。
バージョンアップしたらこのブログやツィッターでご連絡させていただく予定です。

今回はここまでです。
なかなかこういう記事を書くのってホントに大変だということがしみじみ分かりました。
仕事から帰ったらパソコンに釘付けになってしまいます。
とても介護しながらは無理です・・・。
更新するのにもエライ労力と精神的ダメージを受けるもんですね。
こりゃぁ、何か良い方法を考えねばなりませんねぇ・・・。

ということで、まだまだプログラミング未熟者で、文章力も無く読みにくかったと思いますが、ここまでお読みいただきありがとうございます。
それではまた・・・。

最新記事ではWebSocketライブラリをさらにバージョンアップして、格段に使いやすくなりました。
現在BETA1.3です。
こちらのページをご覧ください。
2016/2/22

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/10/11 18:43時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥19,358(2024/10/12 03:29時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/10/11 13:04時点)

コメント

  1. cherry より:

    こんにちは
    以前他のコーナーで質問させていただいた者です。その節はありがとうございました。無事進んでいます。
    このモジュールで1つ教えてください。
    ews.AP_Connect(ssid, password);
    で接続が失敗したときは、無視して次を実行させたいのですが、どうすればいいでしょうか?
    今の状態では、接続をずっと待っているようです。
    WiFiがつながっていればWiFiからの制御を受け、未接続ならLocalで動くようにしたのです。
    よろしくお願いします。

    • mgo-tec mgo-tec より:

      cherryさん

      ご無沙汰しております。
      いつも当ブログをご覧いただき、ありがとうございます。

      AP_Connect 関数ではタイムアウト設定にすれば良いと思います。
      自作EasyWebSocketライブラリでは、そうしていませんでした。

      Beta ver 1.48 ライブラリの、EasyWebSocket.cpp をテキストエディタで開いていただき、
      62~66行目の以下のとこをを探してください。

      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
       yield();
      }
      

      そして、以下のように修正してみてください。

      uint32_t wifi_timeout = millis();
      
      while (WiFi.status() != WL_CONNECTED) {
        if(millis() - wifi_timeout > 30000){
          Serial.println(F("\r\nCannot connected to Wi-Fi AP"));
          break;
        }
        delay(500);
        Serial.print(".");
        yield();
      }
      

      これを上書き保存したら、Arduino IDE を必ず再起動してください。
      これで、30秒たってもWi-Fi接続できなかったら、ループを抜けるようになります。
      ただ、その後に
      Serial.println(F(“WiFi connected”));
      がありますので、接続されてなくても connected と出てしまうので、それはご自分でいい具合にプログラムを変えてみて下さい。

      この辺は次回のバージョンアップで考慮に入れてみたいと思います。

  2. cherry より:

    こんばんわ
    早急な返信ありがとうござます。
    教えていただいたとおり変更しましたら成功しました。
    ありがとうございました。

    Bata Ver 1.48のリリースノートを見ていたら、SoftAP機能があることに気づきました。今度この機能に変更してみます。
    そうすることで中継するWiFiルータが不要になるので、外部利用を考えている私としては非常に助かります。
    いろいろ参考にさせていただいています。

    ちなみに、16×16マトリクスLED×2個でfontサイズ16の文字スクロールの参考情報があると嬉しいです。

    • mgo-tec mgo-tec より:

      cherryさん
      成功して良かったです。

      SoftAP は市販のルーターほどの能力はありません。
      極小マイコンにWiFiが付いているだけの機能しかないので、電波もそれほど飛ばないし、通信もよく途切れます。
      私はちょっと使っただけで、今は殆ど使っていません。
      市販のルーターを使った方が確実な気がします。
      ホテルルーターなんかは小型で手軽ですし・・・。

      16×16のマトリクスLED の16×16ドットフォントは、私もチャレンジしようとしたのですが、それ用のSPI通信のLEDドライバが見つからなかったので、過去に諦めました。
      ですから、情報はございません。
      以下の記事で、ストロベリーリナックスのOLED が結構大きい文字で表示できたので、それで個人的に満足しちゃっています。

      大き目ドットの有機EL, WS0010 で Yahoo ニュース リアルタイム 電光掲示板を作ってみた

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