スケッチの入力
Arduino IDE に以下のスケッチを入力します。
素人コードですので、無駄が多く、誤っている場合も有り得ることをご了承ください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include "ESP32_LCD_ILI9341_SPI.h" //beta ver 1.2- #include "ESP32_Button_Switch.h" #include "ESP32_SD_ShinonomeFNT.h" //beta ver 1.22- #include "ESP32_SD_UTF8toSJIS.h" //beta ver 1.22- #include "ESP32_SD_EasyWebSocket.h" //beta ver 1.60- const int8_t sck = 18; // SPI clock pin const int8_t miso = -1; // MISO(master input slave output) don't using const int8_t mosi = 23; // MOSI(master output slave input) pin const int8_t cs = 14; // Chip Select pin const int8_t dc = 27; // Data/Command pin const int8_t rst = 33; // Reset pin const int8_t LCD_LEDpin = 32; const uint8_t CS_SD = 4; //SD card CS ( Chip Select ) const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください const char* UTF8SJIS_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく const char* Shino_Zen_Font_file = "/font/shnmk16.bdf"; //全角フォントファイル名を定義 const char* Shino_Half_Font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義 const char* HTM_head_file1 = "/EWS/LIP2hed1.txt"; //HTMLヘッダファイル1 const char* HTM_head_file2 = "/EWS/LIP2hed2.txt"; //HTMLヘッダファイル2 const char* dummy_file = "/EWS/dummy.txt"; //HTMLファイル連結のためのダミーファイル ESP32_LCD_ILI9341_SPI LCD(sck, miso, mosi, cs, dc, rst, LCD_LEDpin); ESP32_SD_ShinonomeFNT SFR(CS_SD, 40000000); //SPI 24MHz ESP32_Button_Switch BTN; SD_EasyWebSocket ews; IPAddress LIP; //ローカルIPアドレス自動取得用 //------LCD文字表示系 引数初期化------------ uint8_t max_txt = 40; //1x1倍 最大文字表示数 uint8_t ws_txt_sj_txt[ 400 ] = {}; //Shift_JIS文字コード格納 uint16_t ws_txt_sj_length; uint8_t ws_txt_font_buf[2][16] = {}; //16x16フォント全角1文字格納バッファ uint8_t Scl_Buf[ 16 ][ 640 ] = {}; //文字列スクロールpixelバッファ //65k color red (0-31), green (0-63), blue (0-31) uint8_t red = 31, green = 63, blue = 31; uint8_t V_size = 3, H_size = 3; //V_size(垂直方向文字サイズ)、H_size(水平方向サイズ) uint8_t prev_V_size = V_size; uint16_t X0 = 0, Y0 = 0; int32_t scl_speed = 0; //スクロール速度(0が最速) bool scl_pause = false; int8_t Scl_Cnt = {}; //文字スクロールカウント uint16_t Fnt_Cnt = {}; //フォント半角1文字スクロールカウント uint8_t Zen_or_Han = {}; //フォント読み取り時に関数から返ってきた全角または半角かの数値を格納 uint8_t num = 0; //文字列番号 //-----ボタンスイッチ 引数初期化----------- const uint8_t buttonA_GPIO = 39; const uint8_t buttonB_GPIO = 38; const uint8_t buttonC_GPIO = 37; uint8_t btn_stateA = _Release; uint8_t btn_stateB = _Release; uint8_t btn_stateC = _Release; boolean V_size_down = false; //------Easy WebSpclet関連 引数初期化---------------- String ret_str; //ブラウザから送られてくる文字列格納用 int PingSendTime = 10000; //ESP32からブラウザへPing送信する間隔(ms) bool get_http_req_status = false; //ブラウザからGETリクエストがあったかどうかの判定変数 uint8_t WS_Status = 0; //***********セットアップ**************************** void setup() { Serial.begin(115200); delay(1000); pinMode(buttonA_GPIO, INPUT); //GPIO #39 は内部プルアップ無し pinMode(buttonB_GPIO, INPUT); //GPIO #38 は内部プルアップ無し pinMode(buttonC_GPIO, INPUT); //GPIO #37 は内部プルアップ無し SFR.SD_Shinonome_Init3F(UTF8SJIS_file, Shino_Half_Font_file, Shino_Zen_Font_file); //ライブラリ初期化。3ファイル同時に開く LCD.ILI9341_Init(false, 40000000); //microSDを使う場合、必ず false にする LCD.Display_Clear(); LCD.Brightness(0); //LCD LED Full brightness //---------オープニング画面表示-------------- String test_str[ 4 ] ; test_str[0] = "M5stack"; test_str[1] = "Wi-Fi WebSocket"; test_str[2] = "Message Board"; test_str[3] = "by microSD"; uint8_t test_buf[ 4 ][ 17 ][ 16 ] = {}; uint16_t test_sj_len[ 4 ] = {}; for( num = 0; num < 4; num++ ){ test_sj_len[ num ] = SFR.StrDirect_ShinoFNT_readALL(test_str[ num ], test_buf[ num ]); Serial.printf("test_sj_len = %d\r\n", test_sj_len[ num ]); } num = 0, X0 = 48; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 1, X0 = 48; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 2, X0 = 48; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 3, X0 = 80; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); X0 = 0; for( int i = 0; i < 256; i++){ LCD.Brightness(i); delay(10); } //--------メッセージウィンドウ表示--------- red = 15, green = 30, blue = 15; Status_Message("WiFi Connecting...", red, green, blue); //--------Wi-Fiアクセスポイント接続--------- WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println(F("WiFi connected")); LIP = WiFi.localIP(); //ESP32のローカルIPアドレスを自動取得 Serial.println( LIP ); red = 31, green = 63, blue = 31; Status_Message( "IP : " + LIP.toString(), red, green, blue); ews.EWS_server_begin(); //ESP32 server生成 LCD.Display_Clear(0, 0, 319, 4 * 48 - 1); //---------スクロール文字列のセットアップ------------- num = 0, red = 31, green = 63, blue = 31; LCD.Scrolle_Font_SetUp(num, max_txt, red, green, blue); Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size()); //---------マルチタスク定義------------------- TaskHandle_t th; //マルチタスクハンドル定義 xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動 } //***********メインループ**************************** void loop() { WebSocket_handshake(); //WebSocket ハンドシェイクでブラウザにHTML送信 WebSocket_txt_receive(); //ブラウザからのテキスト受信 if( !scl_pause ){ //文字列スクロール num = 0, X0 = 0, Y0 = 0; if( LCD.Scrolle_Inc_HVsizeUp_8x16_Font_DisplayOut(num, Zen_or_Han, scl_speed, H_size, V_size, &Scl_Cnt, ws_txt_sj_length, X0, Y0, ws_txt_font_buf, Scl_Buf) ){ Zen_or_Han = SFR.Sjis_inc_FntRead(ws_txt_sj_txt, ws_txt_sj_length, &Fnt_Cnt, ws_txt_font_buf); } if( V_size_down ) { //ボタン操作の Task1 で、文字サイズダウンした場合、 //LCDはVSPI接続でmicroSDと共用のため、同じループタスクで画面消去を行う。 LCD.Display_Clear( 0, V_size * 16 + Y0 - 1, 319, prev_V_size * 16 + Y0 - 1 ); V_size_down = false; } prev_V_size = V_size; } if( ews.WebSocket_Status() != WS_Status ){ WS_Status = ews.WebSocket_Status(); switch( WS_Status ){ case 0: red = 31, green = 0, blue = 0; Status_Message( "WebSocket CLOSE", red, green, blue); break; case 1: red = 0, green = 63, blue = 31; Status_Message( "WebSocket Connected", red, green, blue); break; } Serial.printf("WS_Status = %d\r\n", WS_Status); Serial.printf("ews.WebSocket_Status() = %d\r\n", ews.WebSocket_Status()); } } //************ マルチタスクループ **************************** void Task1(void *pvParameters) { while(1){ button_action(); delay(1); //most important } } //****************************************************** void WebSocket_txt_receive(){ ret_str = ews.EWS_ESP32CharReceive(PingSendTime); if(ret_str != "_close"){ if(ret_str != "\0"){ Serial.println(ret_str); if(ret_str != "Ping"){ if(ret_str[0] != 't'){ int ws_data = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30); uint8_t picker_red; uint8_t picker_green; uint8_t picker_blue; float deg; float deg2; char col_c_65k[9] = {}; //ILI9341 65kカラー文字列収納 char col_c_code[8] = {}; //HTMLカラーコード文字列格納 String col_str; switch(ret_str[4]){ case 'M': //スクロールスタート scl_pause = false; red = 0, green = 63, blue = 0; Status_Message( "Scrolle Start", red, green, blue); break; case 'P': //スクロール一時停止 scl_pause = true; red = 31, green = 0, blue = 0; Status_Message( "Scrolle Pause", red, green, blue); break; case 'S': //スクロール速度、文字サイズアップ switch(ret_str[9]){ case '1': //スクロール速度設定 scl_speed = 100 - floor(ws_data/2); break; case '2': //水平文字サイズ変更 H_size = floor(ws_data/20); if( H_size > 10 ) H_size = 10; if( H_size < 1 ) H_size = 1; Serial.printf("H_size = %d\r\n", H_size); break; case '3': //垂直文字サイズ変更 V_size = floor(ws_data/20); if( V_size > 10 ) V_size = 10; if( V_size < 1 ) V_size = 1; if( prev_V_size > V_size ) V_size_down = true; Serial.printf("V_size = %d\r\n", V_size); break; } break; case 'C': //カラーピッカー数値変換 //HTMLカラーコード変換 picker_red = (ret_str[10]-0x30)*100 + (ret_str[11]-0x30)*10 + (ret_str[12]-0x30); picker_green = (ret_str[14]-0x30)*100 + (ret_str[15]-0x30)*10 + (ret_str[16]-0x30); picker_blue = (ret_str[18]-0x30)*100 + (ret_str[19]-0x30)*10 + (ret_str[20]-0x30); sprintf( col_c_code, "#%02X%02X%02X", picker_red, picker_green, picker_blue ); //ILI9341 65k カラー変換 deg = (float)31 / (float)255 ; deg2 = (float)63 / (float)255 ; red = round( (float)picker_red * deg ); green = round( (float)picker_green * deg2 ); blue = round( (float)picker_blue * deg ); Serial.printf("red, green, blue = %d, %d, %d\r\n", red, green, blue ); LCD.Scrolle_Font_SetUp(0, max_txt, red, green, blue); sprintf( col_c_65k, "%02d,%02d,%02d", red, green, blue ); col_str = String( col_c_code ) + " "; col_str += String( col_c_65k ); Status_Message( col_str, red, green, blue); break; } }else if(ret_str[0] == 't'){ //ブラウザからのテキスト文字列受信 String txt = ret_str.substring(ret_str.indexOf('|')+1, ret_str.length()-1); Serial.println( txt ); StrFontConv( txt ); } ret_str = ""; } } }else if(ret_str == "_close"){ Serial.println("---------------WebSocket Close"); ret_str = ""; } } //******** Status メッセージ表示*************** void Status_Message(String str, uint8_t Red, uint8_t Green, uint8_t Blue){ //ILI9341 ディスプレイに 半角19文字まで表示可能 uint8_t f_buf[ 40 ][ 16 ] = {}; uint16_t len = SFR.StrDirect_ShinoFNT_readALL(str, f_buf); if( len > 38 ) len = 19; X0 = 8, Y0 = ( 4 * 48 + 1 ) + 10; LCD.Display_Clear(1, Y0 + 1, 318, 238); LCD.HVsizeUp_8x16_Font_DisplayOut(2, 2, len, X0, Y0, Red, Green, Blue, f_buf); LCD.Draw_Rectangle_Line(0, 4 * 48, 319, 239, 31, 63, 31); } //********* String 文字列フォント変換*********** void StrFontConv(String str){ Fnt_Cnt = 0; ws_txt_sj_length = SFR.UTF8toSJIS_convert(str, ws_txt_sj_txt); Zen_or_Han = SFR.Sjis_inc_FntRead(ws_txt_sj_txt, ws_txt_sj_length, &Fnt_Cnt, ws_txt_font_buf); Serial.printf("ws_txt_sj_length = %d\r\n", ws_txt_sj_length); Scl_Cnt = 0; } //********** ボタンスイッチ操作関数************** void button_action(){ btn_stateA = BTN.Button(0, buttonA_GPIO, true, 10, 300); switch( btn_stateA ){ case _MomentPress: Serial.println("Button A Moment Press"); V_size--; if(V_size < 1) V_size = 1; if( prev_V_size > V_size ) V_size_down = true; Serial.printf("V_size =%d\r\n", V_size); break; case _ContPress: Serial.println("-------------Button A Cont Press"); V_size++; if(V_size > 10) V_size = 10; Serial.printf("V_size =%d\r\n", V_size); break; default: break; } btn_stateB = BTN.Button(1, buttonB_GPIO, true, 10, 300); switch( btn_stateB ){ case _MomentPress: Serial.println("Button B Moment Press"); H_size--; if(H_size < 1) H_size = 1; Serial.printf("H_size =%d\r\n", H_size); break; case _ContPress: Serial.println("-------------Button B Cont Press"); H_size++; if(H_size > 15) H_size = 15; Serial.printf("H_size =%d\r\n", H_size); break; default: break; } btn_stateC = BTN.Button(2, buttonC_GPIO, true, 10, 300); switch( btn_stateC ){ case _MomentPress: Serial.println("Button C Moment Press"); scl_speed++; if(scl_speed > 100) scl_speed = 100; Serial.printf("scl_speed =%d\r\n", scl_speed); break; case _ContPress: Serial.println("-------------Button C Cont Press"); scl_speed--; if(scl_speed < 0) scl_speed = 0; Serial.printf("scl_speed =%d\r\n", scl_speed); break; default: break; } } //*********************************************** void WebSocket_handshake(){ if(ews.Get_Http_Req_Status()){ //ブラウザからGETリクエストがあったかどうかの判定 red = 15, green = 30, blue = 15; Status_Message( "WS connecting", red, green, blue ); String html_str1="", html_str2="", html_str3="", html_str4="", html_str5="", html_str6="", html_str7=""; //※String変数一つにEWS_Canvas_Slider_T関数は2つまでしか入らない html_str1 += "<body style='background:#000; color:#fff;'>\r\n"; html_str1 += "<font size=3>\r\n"; html_str1 += "ESP-WROOM-32(ESP32)\r\n"; html_str1 += "<br>\r\n"; html_str1 += "SD_EasyWebSocket Beta1.60 Sample\r\n"; html_str1 += "</font><br>\r\n"; html_str1 += ews.EWS_BrowserSendRate(); html_str1 += "<br>\r\n"; html_str1 += ews.EWS_Status_Text2("WebSocket Status","#555", 20,"#FF00FF"); html_str1 += "<br><br>\r\n"; html_str2 += ews.EWS_TextBox_Send("txt1", "Hello Easy WebSocket ","送信"); html_str2 += "<br><br>\r\n"; html_str2 += "Scrolle \r\n"; html_str2 += ews.EWS_On_Momentary_Button("Move", "Start", 80,25,15,"#000000","#AAAAAA"); html_str2 += ews.EWS_On_Momentary_Button("Pause", "Pause", 80,25,15,"#FFFFFF","#555555"); html_str2 += "<br>\r\n"; html_str3 += "<br>Scrolle Speed \r\n"; html_str3 += ews.EWS_Canvas_Slider_T("Speed1",200,40,"#777777","#CCCCFF"); //CanvasスライダーはString文字列に2つまでしか入らない html_str3 += "<br>H_Size \r\n"; html_str3 += ews.EWS_Canvas_Slider_T("Speed2",200,40,"#777777","#FFFFCC"); //CanvasスライダーはString文字列に2つまでしか入らない html_str4 += "<br>V_Size \r\n"; html_str4 += ews.EWS_Canvas_Slider_T("Speed3",200,40,"#777777","#CCFFCC"); //CanvasスライダーはString文字列に2つまでしか入らない html_str4 += "<br><br>Text Color Picker \r\n"; html_str4 += ews.Color_Picker( 0, 100, "#FFFFFF", "Color"); html_str5 += "<br><br><br><br>\r\n"; html_str5 += ews.EWS_WebSocket_Reconnection_Button2("WS-Reconnect", "grey", 200, 40, "black" , 17); html_str5 += "<br><br>\r\n"; html_str5 += ews.EWS_Close_Button2("WS CLOSE", "#bbb", 150, 40, "red", 17); html_str5 += ews.EWS_Window_ReLoad_Button2("ReLoad", "#bbb", 150, 40, "blue", 17); html_str5 += "</body></html>"; //WebSocket ハンドシェイク関数 ews.EWS_HandShake_main(3, CS_SD, HTM_head_file1, HTM_head_file2, dummy_file, dummy_file, LIP, html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7); red = 0, green = 63, blue = 0; Status_Message( "Websocket SET", red, green, blue ); } }
【解説】
過去の記事と重複しているところは省略するので、ザッとした解説です。
●24-26行:
以前、EasyWebSocket ライブラリを使っていた方は気を付けてください。
ファイル名が変わっています。
ややこしい名前でスイマセン。
●31行:
EasyWebSocket ライブラリのクラス名を指定しています。
●127行:
ここで、ESP32 server を生成しています。
●141行:
ブラウザからM5Stack ( ESP32 )にアクセスがあった場合に、WebSocket ハンドシェイクを行い、HTML をブラウザに出力する関数で、343行以下で関数化しています。
●142行:
ここで、ブラウザからWebSocket通信で送信されてきたテキストを受信して、その後の動作を選別しています。
183-265行で関数化しています。
●159-173行:
ここで、WebSocket通信の状態を画面下部のメッセージウィンドウに表示させています。
●183-265行:
ブラウザから WebSocket 通信で送信されてきたテキストデータを振り分けしています。
HTML Canvas スライダーは 0~200 の間の数値を返すので、うまく引数に割り当てる計算式を作っています。
ここはちょっと頭を使う所です。
また、WebSocket のデータ送受信方法については以下の記事を参照してください。
Arduino化 WROOM で WebSocket データ送受信方法
231-250行では、ブラウザの カラーピッカー ( Color Picker )ボタンで選択したカラーコードを、LCD ILI9341 の 65k カラーに変換しています。
カラーピッカーからは白色の場合、スマホから以下のようなテキストで送られてきます。
100|Color,255,255,255;
赤、緑、青それぞれ0~255 の値です。
それを赤(0-31)、緑(0-63)、青(0-31) の値に変換しています。
抽出したカラーコードをLCDディスプレイに表示させるようにしてみました。
252-257行で、ブラウザのテキストボックスに入力されたテキストを東雲フォントに変換しています。
●343-386行:
ブラウザから M5Stack ( ESP32 ) server へアクセスがあったら、ブラウザへ HTML を出力し、WebSocket ハンドシェイクを行います。
HTML ヘッダは micro SD カードに保存した、LIP2hed1.txt ファイルと、LIP2hed2.txt ファイルをマージして出力します。
HTML の Body要素内は、String 変数にして出力しています。
String 型の一つの html_str 変数にEWS_Canvas_Slider 関数は2つまでしか代入できませんのでご注意ください。
この HTML は M5Stack の ESP32 から出力しなくても、ブラウザに HTML ファイルを保存しておいても同様の動作を行うことが出来ます。
そうすれば、メモリを節約できます。
また、Body要素内を micro SD に保存しておいても良いと思います。
コンパイル書き込み実行
では、Arduino IDE でコンパイル書き込みしてみてください。
最初に紹介した動画のように動作すればOKです。
シリアルモニターも同時に起動しておけば、詳細なログを見ることができます。
最初の接続時は、WebSocket ハンドシェイクに時間がかかると思います。
一度接続出来れば、次に再接続する時は速いと思います。
もし、スマホを2台以上持っていたら、スマホを切り替えて通信してみてください。
同時に2台からコントロールできません。
一旦、1代目の WebSocket を CLOSE してから切り替える形になります。
また、ブラウザで別の WEB ページに切り替えると、WebSocket が切断されることが分かると思います。
Google Chrome の場合、下図の様な画面になると思います。
カラーピッカーボタンをタッチすると下図の様に表示され、よく使うプリセット色があります。
「設定」をタッチすると、M5Stack ( ESP32 )に送信され、文字色が変わります。
他の色や中間色にしたい場合は「もっと見る」をタッチすると、下図の様に表示され、微調整できます。
因みに、iPad ( iOS 11.3.1 ) の Safari の場合は、下図の様になります。
この場合のカラーピッカーは、数値入力になります。
カラーコードを編集ではなく、一旦すべて削除して、最初から文字を入力しないと反映されないと思います。
この辺は Google Chrome の方が断然良いですね。
以上、いろいろと操作してみてください。
編集後記
どうでしょうか?
スマホでリアルタイムに文字サイズが変えられるというのは、意外と面白いと思いました。
ただ、M5Stack があまりにも完成されているので、電子工作的な面白さが薄れたような気がしました。
それでも、やっぱり M5Stack は便利ですね。
これからは、M5Stack で実験してから、電子工作へ落とし込むというやり方に変わりそうです。
今回はここまでです。
近々、これをグローバル環境でメッセージ送信したり、Google Home と連携したりしてみたいと思っています。
ではまた・・・。
コメント
こんにちわ
動作チェック完了しました。
ちょっとてこずりました。
まずSDメモリーが読み込めませんでした。
microSDHCカード なるものを買ったら動きました。
なにか相性があるみたいです。
もう一点はポカミスです。SDのディレクトリー名をEMSってやってました。EWSと訂正すると完動の感動でした。
最近WebカメラにはまっていてESP32でカメラを作る実験をしています。
なにかできましたらUPします。
マッキーさん
いつもブログにお越しいただき、ありがとうございます。
単なるmicroSD カードで読み込み不良になるとは初めて聞きました。
手持ちには SDHC しかないので検証できませんが、そういえば、単なるmicroSDを入手する方が難しいかもしれませんね。
今、殆どが SDHC か SDXC ですし。
とりあえず、完動して感動していただけたら、それこそ感動です!!
試していただき、ありがとうございました。
m(_ _)m
Webカメラは私には全く未知です。
面白そうですが、難しそうですね。
UP画像期待しております。
こんばんわ
私は下記のようなキットを買いましたが、中華なのでまだ届きません。
接続図もソースコードもありますのでなんとか動くと思います。
この値段は買いかと思ってポチりました。
https://www.aliexpress.com/item/TTGO-ESP32-Camera-OV7670-1-8-TFT-display-Module-KIT/32846659999.html
動作画像です。
https://youtu.be/q93Z98rNX64
作り過ぎかな(´・ω・`;)
おぉ、これは安いですね。
なぜか動作画像が表示できなくなっていますが、何となく分かりました。
自分は画像処理は殆どやったことが無いので、いつか挑戦したいと思っています。
情報ありがとうございました。
m(_ _)m