気の向くままに辿るIT/ICT/IoT
webzoit.net
IoT・電子工作

ESP32液晶時計データをTCP通信でブラウザ・Processingにも表示

ウェブ造ホーム前へ次へ
サイト内検索
カスタム検索
ESP8266って?

ESP32液晶時計データをTCP通信でブラウザ・Processingにも表示

ESP32液晶時計データをTCP通信でブラウザ・Processingにも表示

2018/09/01

HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード

 先日作ったESP32とTFT 1.8インチ液晶、DHT11モジュールを使った温度計・湿度計付きデジタル時計のデータをTCPを介してブラウザとProcessingのウィンドウにもリアルタイム表示させてみるページ。

 と言っても、やっつけ仕事(遊び)なので、あしからず。

HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード

waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送

waves ESP-WROOM-32 + 開発基盤 セット 国内発送

waves ESP8266 WiFiモジュール(技適取得済み) ESP-WROOM-02 キット 赤基盤

[スイッチサイエンス] ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》

[スイッチサイエンス] ESP-WROOM-02ピッチ変換済みモジュール《フル版》

HiLetgo 2個セット ESP8285 ESP-M2 CH340開発ボード WIFIシリアルポートモジュール CH340 ESP8266に対応

HiLetgo® 2個セット ESP8266 NodeMCU LUA CP2102 ESP-12E モノのインターネット開発ボード ESP8266 無線受信発信モジュール WIFIモジュール Arduinoに適用 [並行輸入品]

HiLetgo OTA WeMos D1 CH340 WiFi 開発ボード ESP8266 ESP-12F Arduino IDE UNO R3に対応 

Rasbee WeMos D1 R3 簡易デザイン WiFi 開発ボード NODEMCU LUA ESP8266 Arduino UNO 適用 1個 [並行輸入品]

Rasbee OTA WeMos D1 CH340 WiFi 開発ボード ESP8266 ESP-12F For IDE UNO R3 Arduino用 1個 [並行輸入品]

Rasbee アップグレード版 WeMos D1 R2 WiFi UNO 開発板 ESP8266 Arduino 1個 [並行輸入品]

概要

 ESP32をサーバ、ProcessingをクライアントとしたTCP通信によるデータ送受信であり、シリアル通信ではないのでWiFi環境さえあれば、ESP32とパソコン間は、無線化できる。

 可変のIP指定は非現実的であり、Processingは、Avahi(Bonjour)をインストール可能なパソコン上にあるのでmDNSを使ってホスト名.local(今回は、esp32clock.local)で常にサーバにアクセスできるようにした。

 ちなみにESP32とProcessing何れをクラサバにしてもTCP通信は可だし、パソコン側をサーバとした場合でもOS上のホスト名.localで同様にアクセスできる。

 今回は、パケット到達が保証されるTCP通信だが、パケットの到達には無関心な一方、高速でリアルタイム向きなUDP通信でもできるだろう。

前提

 ここでは、Arduino IDEを使うのでArduino IDEの[ツール] => [ボード]から[espressif/arduino-esp32]を選択、ESPにスケッチをアップロードできる状態であること。

 Arduino IDE 1,8,6で追加された[ツール] => [ライブラリを管理...]メニューか、従来の[スケッチ] => [ライブラリをインクルード...] => [ライブラリを管理...]メニューを辿ってライブラリ管理画面を開き、TFT 1.8液晶用に[Adafruit GFX Library]、[Adafruit ST7735 and ST7789 Library]、DHT11/DHT22センサーモジュール用に[DHT sensor library for ESPx]を検索、インストールしておくこと。

 尚、ESP32でmDNSを使う場合、ESPmDNSヘッダファイルをincludeする(ESP-01やESP-12などでは、ESP8266mDNS)。

 Processingをインストール、使える状態にあること。

 相応のブラウザが使える状態にあること。

 参考までに自身の使用しているOSは、Debian(Linux)、ブラウザは、Mozilla Firefox、Arduino IDEのバージョンは、1.8.6(1.8.6を素直に起動できない場合、起動方法参照)。

必要なモノ

 今回は、WiFiモジュールの載ったESP32の開発ボードを使ったが、技適はさておき、GPIOピンの数は足りるはずなのでESP12あたりでもできるだろう。

必要な電子部品類

 ここでは、ESP32開発ボードを使ったが、ESP-WROOM-32モジュール単体の場合には、ピンホールが半円だし、ブレッドボードとピッチも合わないため、別途、市販のブレイクアウトボードを買うか、自作でごにょごにょする必要があるだろう。

 尚、ESP32は、入力電圧定格3.3V、今回使ったTFT1.8インチもDHT11センサーも3.3Vでいけた。

HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード

Amazonベーシック USB2.0 A(オス) - マイクロBケーブル 0.9m ブラック

エレコム エコ USBケーブル 2.0 A-microB 0.6m U2C-JAMB06BK

HiLetgo 1.8インチ ST7735R SPI 128x160 TFT液晶ディスプレイ

KKHMF DHT11 温度/湿度センサーモジュール(ジャンパワイヤ付属)

Rasbee 400穴 ブレッドボード 8.5*5.5cm 1個

HiLetgo 400穴 ブレッドボード 8.5*5.5cm 5個セット

HiLetgo 400穴 ブレッドボード 8.5*5.5cm 10個セット

Rasbee SY-170 ミニブレッドボード カラフルブレッドボード 5個

ELEGOO 120pcs多色デュポンワイヤー、arduino用、オス-メス オス-オス メス ?メス ブレッドボードジャンパーワイヤー

ESP32開発ボードへのスケッチのアップロード

 ESP32開発ボードの内、DOIT製DevKit V1及び互換ボードをArduino IDEで使う場合、スケッチのアップロード時、書き込む際にボード上のRESET/RST押したまま、BOOTボタンを押し、RSTを放す、もしくは、BOOTボタンのみを押して放す必要があったが、Arduino IDE 1.8.6では、その必要がなくなったようだ。

 少なくとも後者の操作が必要だった自身のボードにおいては、この必要がなくなったことを確認済み。

 よってArduino IDE 1.8.6を使っている場合、特記すべきことはない。

回路

ESP32TFT1.8DHT11
19MISO-
5CS-
18SCL-
23SDA-
17A0-
(16)(RESET)-
4-DATA
3.3VVCC
LED+-
GNDGND
LED--

 配線は、目的は違えど流れ着いたMhageGH/esp32_ST7735_Movieを参考にさせて頂いたが、スケッチと回路を一致させれば、特殊なピンを避ければ、なお面倒がないが、どのピンでも問題ないだろう。

 今回、ESP32開発ボードには、GPIOが38ピンあるEspressif DevKitC V4と同じっぽい一方、4隅の穴からDOIT DevKit風にも見えるものを使ったが、30ピンのDOIT DevKit V1や互換機には、GPIO17はないようなので他の空いたピンを使い、スケッチにも反映させればよい。

 注意すべきは、このTFT 1.8(KMR-1.8 SPI)は、なぜか、SPIというかI2Cっぽく、ケーブル2本は、スケッチとは別の配線をする必要がある点。(ちなみにArduinoでは同様に4本別の配線を要した)

 具体的には、スケッチ上では、KMR-1.8液晶のSCLK/MOSIに接続することになっているESP32のピン(今回は18/23)を回路上では、SCL/SDAに配線する必要がある。

 そうしないと液晶に何も表示されず戸惑うことになる。(バックライトは点くし、もにょもにょ何やら映そうとはするが、結果何も表示されない。)

 ちなみに、このTFT液晶、購入当時、そうとは知らず、返送不要で返金してもらったものの、後で使えることがわかり、再支払いした経緯がある。

ESP32側スケッチ

// Server ESP32/ESP-WROOM-32
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiClient.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <DHTesp.h>
 
DHTesp DHT;
 
#define DHT11_PIN 4
 
// You can use any (4 or) 5 pins
#define sclk 18  // SCL
#define mosi 23  // SDA
#define cs 5   // CS
#define dc 17   // A0
#define rst 16 // you can also connect this to the Arduino reset
 
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst); 
 
// WiFi Setting
#define WIFI_SSID  "SSID"
#define WIFI_PASSWORD  "PASSWORD"
#define JST   3600*9
 
// TCP server at port 80 will respond to HTTP requests
WiFiServer server(80);
 
void setup(void) {
 Serial.begin(115200);
 
 // Use this initializer if you're using a 1.8" TFT
 tft.initR(INITR_BLACKTAB);  // initialize a ST7735S chip, black tab
 
 Serial.println("init");
 
 uint16_t time = millis();
 tft.fillScreen(ST77XX_BLACK);
 time = millis() - time;
 
 Serial.println(time, DEC);
 delay(500);
 
 Serial.println("done");
 delay(1000);
 
 // WiFi starting
 Serial.println("WiFi connecting...");
 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 while(WiFi.status() != WL_CONNECTED) {
  Serial.print('.');
  delay(500);
 }
 Serial.println();
 Serial.printf("Connected, IP address: ");
 Serial.println(WiFi.localIP());
 Serial.println("WiFi connected!");
 
 // print the received signal strength:
 long rssi = WiFi.RSSI();
 Serial.print("signal strength (RSSI):");
 Serial.print(rssi);
 Serial.println(" dBm");
 
 // Set up mDNS responder:
 // - first argument is the domain name, in this example
 //  the fully-qualified domain name is "esp8266.local"
 // - second argument is the IP address to advertise
 //  we send our IP address on the WiFi network
 if (!MDNS.begin("esp32clock")) {
  Serial.println("Error setting up MDNS responder!");
  while(1) {
    delay(1000);
  }
 }
 Serial.println("mDNS responder started");
 
 // Start TCP (HTTP) server
 server.begin();
 Serial.println("TCP server started");
 
 // Add service to MDNS-SD
 MDNS.addService("http", "tcp", 80);
 
 // NTP start
 configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
 DHT.setup(DHT11_PIN, DHTesp::DHT11);
 delay(1000);
}
 
void loop() {
 time_t t;
 struct tm *tm;
 static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
 char rdate[30], rday[30], rtime[30], rsec[30];
 
 t = time(NULL);
 tm = localtime(&t);
 
 Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
    wd[tm->tm_wday],
    tm->tm_hour, tm->tm_min, tm->tm_sec);
 sprintf(rdate, "%04d/%02d/%02d",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
 sprintf(rday, " (%s)",
    wd[tm->tm_wday]);
 sprintf(rtime, " %02d:%02d",
    tm->tm_hour, tm->tm_min);
 sprintf(rsec, ":%02d",
    tm->tm_sec);
 delay(1000 - millis()%1000);
 
 float humidity = DHT.getHumidity();
 float temperature = DHT.getTemperature();
 
 Serial.print(DHT.getStatusString());
 Serial.print("\t");
 Serial.print(humidity, 1);
 Serial.print("\t\t");
 Serial.print(temperature, 1);
 Serial.print("\t\t");
 Serial.print(DHT.toFahrenheit(temperature), 1);
 Serial.print("\t\t");
 Serial.print(DHT.computeHeatIndex(temperature, humidity, false), 1);
 Serial.print("\t\t");
 Serial.println(DHT.computeHeatIndex(DHT.toFahrenheit(temperature), humidity, true), 1);
 
 Serial.print("Date : ");
 Serial.print(rdate);
 Serial.println(rday);
 Serial.print("Time : ");
 Serial.print(rtime);
 Serial.println(rsec);
 
 tft.setCursor(0, 20);
 tft.fillScreen(ST77XX_BLACK);
 tft.setTextColor(ST77XX_WHITE);
 
 tft.setTextSize(2);
 tft.println(rdate);
 tft.setCursor(0, 40);
 tft.println(rday);
 tft.setCursor(0, 70);
 
 tft.setTextSize(3);
 tft.print(rtime);
 tft.setTextSize(1);
 tft.println(rsec);
 tft.setCursor(0, 110);
 
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(temperature, 1);
 tft.setTextSize(1);
 tft.println(" C");
 
 tft.setCursor(0, 130);
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(humidity, 1);
 tft.setTextSize(1);
 tft.println(" %");
 
 // listen for incoming clients
 WiFiClient client = server.available();
 if (client) {
  Serial.println("new client");
  // an http request ends with a blank line
  boolean currentLineIsBlank = true;
  while (client.connected()) {
   if (client.available()) {
    char c = client.read();
    Serial.write(c);
    // if you've gotten to the end of the line (received a newline
    // character) and the line is blank, the http request has ended,
    // so you can send a reply
    if (c == '\n' && currentLineIsBlank) {
     // send a standard http response header
     client.println("HTTP/1.1 200 OK");
     client.println("Content-Type: text/html");
     client.println("Connection: close"); // the connection will be closed after completion of the response
     client.println("Refresh: 5"); // refresh the page automatically every 5 sec
     client.println();
     client.println("<!DOCTYPE HTML>");
     client.println("<html>");
 
     client.print("TARGET : ");
     client.print(rdate);
     client.print(rday);
     client.print(" ");
     client.print(rtime);
     client.print(rsec);
     client.print(" ");
     client.print(temperature, 1);
     client.print(" C");
     client.print(" ");
     client.print(humidity, 1);
     client.println(" %");
     client.println("<br />");
 
     client.println("</html>");
     break;
    }
    if (c == '\n') {
     // you're starting a new line
     currentLineIsBlank = true;
    } else if (c != '\r') {
     // you've gotten a character on the current line
     currentLineIsBlank = false;
    }
   }
  }
  // give the web browser time to receive the data
  delay(1);
 
  // close the connection:
  client.stop();
  Serial.println("client disonnected");
 }
}

 今回、サーバとしたESP32のスケッチは、これ。

 そのまま使えるが、少なくとも自身で利用可能なWiFiルーター、アクセスポイント用にSSID/PASSWORDは書き換える必要がある(WiFi接続できないと時計も表示されない)。

Processing側スケッチ

// Client Processing
import processing.net.*;
 
String serverAddress = "esp32clock.local";
int port = 80;
 
Client c;
 
String[] arraySTR;
String s;
 
void setup()
{
 size(500, 200);
 textFont(createFont("SanSerif", 16));
}
 
void draw()
{
 c = new Client(this, serverAddress, port);
 c.write("GET / HTTP/1.1\n\n"); // Use the HTTP "GET" command to ask for a webpage
}
 
void clientEvent(Client c) {
 s = c.readString();
 if (s != null) {
  arraySTR = s.split("\n");
  for(int i = 0;i < arraySTR.length; i++){
   if (arraySTR[i].contains("TARGET")) {
    println(arraySTR[i]);
    background(0);
    text(arraySTR[i], 15, 40);
   }
  }
 }
 else { println("That is " + s); }
}
 

 今回、クライアントとしたProcessingのスケッチは、これ。

 ただ、トライ&エラーの結果であって機能はするも書き方が、これで正しいのか、自信はない。

実行

ESP32温湿度計付き液晶時計データをブラウザ・Processingで表示
  1. 社内、宅内無線LANルーターまたは、アクセスポイントにアクセスできるよう準備・確認
  2. ESP32とProcessingそれぞれにスケッチをアップロード
  3. サーバとしたESP32をシリアルモニタ、もしくはTFT液晶表示などで動作を確認
  4. Processingのスケッチを実行
  5. ブラウザのURL入力欄にスケッチそのままならesp32clock.localと入れてEnter

のようにすれば、TFT液晶、Processingのウィンドウ、ブラウザ上に日付、時刻、温度、湿度が表示されるはず。

 リセット・再起動なく進めるためには、ルーター、サーバ(今回はESP32)、クライアント(今回はProcessingやブラウザ)の順番は重要。

 もし、うまく表示されない場合は、ESP32ボード上のRST/RESETボタンを押下、それでも表示されない場合は、配線を見直し、Processingについては、再起動してみるなりする。

 ただし、ブラウザ表示は、無理矢理、5秒毎にリフレッシュしている為、短時間で通信が切断されることがままある、同時にProcessingウィンドウを開いていた場合、これもハングアップする。

 ただ、Processingについては、Processing 3.4にしてから、別件でもフリーズしたり、保存するか否かで挙動が異なったり、スケッチだけでなく、Processingごと再起動する必要があったりするため、Processingのバージョンによる可能性もある。

 Arduino IDEのシリアルモニタ、Processingのポップアップウィンドウとコンソール、そしてブラウザ、それぞれの表示上の1秒程度の微妙なズレは気にしない。

$ cat auto_processing_sketch.sh
# Processingをインストール済みの場合
processing-java --sketch=/path/to/Sketch_Directory --run
# Processingを展開のみでインストールしていない場合
#/path/to/processing-java --sketch=/path/to/Sketch_Directory --run
$ sh auto_processing_sketch.sh

 スクリプトからProcessingのスケッチを自動実行させることもできる。

 端末から実行させるとProcessingのポップアップウィンドウだけでなく、端末上にもテキストベースの通信結果が表示される。(ちなみに、この時、もちろん、Processing IDEは起動しないというか表示されない。)

 OSごとに相応の設定をすれば、ラズパイなどのマイコンやパソコン起動時にProcessingのスケッチを自動実行させることもできるだろう。

ESP32で温度計・湿度計付き時計

 TFT液晶表示した写真(は、時計を作った時の画像を流用している為、当然、日付時刻は異なる)。

 今回は、このようにTFT液晶に表示をしつつ、このデータを元にブラウザ上にも、Processingのウィンドウ上にも日付時刻、温度、湿度を表示してみた、また、無線LAN経由なのでWiFi機能搭載のESP32時計は、電波が届く範囲なら、どこにあってもよいという話。

 とは言え、冒頭書いたように、やっつけなのでProcessingやブラウザへの表示については、実用には程遠い。

既知の不具合・現象

 本来、ブラウザでリアルタイム表示させる場合、AjaxやWebSocket、WebSocket他の手法を内包したnode.jsベースのSocket.ioなどを使うのが正攻法なのだろうが、覚える気力はなく、横着してHTTPプロトコルのrefreshを使うに留めたため、前述の通り、重すぎてブラウザ上は途中、通信が遮断される。

 この時、Processingも巻き添えになるように見受けられるが、液晶+Processingのみなら落ちることはなさ気。

 リアルタイムと言いつつ、ブラウザ上では5秒ごとにリフレッシュしている(リフレッシュで毎秒は厳しすぎる)が、実際のところ、再表示まで約11秒かかっている。

 また、リアルタイムと言っても先のスクリーンショットのようにESP32、ブラウザ、Processing間で若干タイムラグがあり、表示が不一致となることがままある。(Processingのコンソール上はそんなことないようだが、ポップアップウィンドウ上は表示が間に合わないかのように秒飛びすることがある。)

 Processingのウィンドウ上において、なぜか、行末方向の情報が任意の文字数欠落することがあるが、ウィンドウだけでなく、コンソールもなので受信及び抽出・表示が1秒では間に合っていない可能性も。(Processingとブラウザ上の表示データは、ESP32(サーバ)側で成形したものをそのまま表示しているだけ。)

 これも前述の通り、Processingのバージョンに依存するのか、正しい挙動を得るために実行を止め、スケッチを保存し、再実行、時には、スケッチだけでなく、Processingごと再起動を要することもある。(再起動しないと[最近開いたファイル]メニューが消えていることもある。)

 TFT液晶表示だけの時はなかったような気もするが、そもそも当然、あり得ることなのか、入力電圧が不足することがあるのか、配線の接触によるのか、たまに温度、湿度の値が、[nan]になることがある。

 時にブラウザは、ESP32をリセットしないとアクセスできないことがある。

 Processingに至っては、リセットしてもUnknownHostExceptionやNullPointerExceptionなどとなって機能しなくなることがあり、原因は、ホスト名.localにアクセスできないこと、pingしても「名前またはサービスが不明」となるし、avahiデーモンを再起動してもダメ...ただ、ESP32側の電源を入れ直すと回復する...これは、avahiの入ったパソコンの電源をOFFにし、その後、ONにしたからかもと思って再起動してみたが、ちゃんと機能する...OFFからONまでが短時間と長時間で違うのか...も?

実用可能性

 数日経って、できた時計を眺めながら、ふと、できるよね?という思いがよぎり、確認するだけしてみるか...程度の動機だったこともあり、一応できるにはできたという時点で...というか、いまのところ、これ以上、手を入れる気もなく、中途半端な状態で公開するに至ったが、ちゃんと実装すれば、意外と使えるかも。

 今回の場合なら、日付、時刻、温度、湿度を、TFT液晶の前にいなくても無線LAN+スマホ、タブレット、パソコンのブラウザ経由で確認できるわけだが、日付時刻はともかく、どこか特定の場所の温湿度を確認したい場合には、温度・湿度が無線で遠隔でわかるというのはよいかも。

 例えば、天気予報APIからの情報とか、GPS位置情報とか、他のテキストデータでもいいわけだし、ESP32側にカメラでもあれば、ブラウザ上に画像や映像表示、ドアフォンや見守り・監視カメラも無線で遠隔で確認できたり...既にそんな商品もあるけど、この仕組みでできそうな気も。

省電力化・スリープモード

 ESP8266 Low-Power Solutionsによれば、ESP8266/ESP-WROOM-02/ESP-WROOM-32には、3種類のスリープモードがあるが、電池駆動を考えている為、最も省電力な数μAのDeep Sleepモードを使いたいところだが、未実装。

 ただ...、ACアダプタ付きUSB充電器やパソコンにUSB接続した場合には、機能するのだが、電池駆動の場合、プラス/マイナスをESP32開発ボードの5V/GNDピンに接続すればよいのだが、単3電池x2(eneloop)昇圧+USBモバイルバッテリでも、単3電池x4(eneloop)+電池ボックスでもWiFi用には、電流が不足するようで無負荷で入力電圧5V以上はあるが、接続すると2.6V台あたりまで電圧降下、液晶上にも表示しようという努力は認められるが、表示しきれない...。

単三電池2本で充電!i緊急充電用 乾電池式 USB充電器 CW-130DEN

SODIAL(R)オン/オフスイッチ 5.5"単三電池用の電池ホルダーボックスケース 4×1.5V

エレコム USB充電器 iPoda&iPhone&Android対応 1A出力 1ポート AC充電器 ホワイトフェイス AVA-ACUAN007WF

 スリープ以前に電池駆動にできるのだろうか...?できなければ、NTPなしでRTCモジュール併用?それか、コンセント接続前提?

 ESP8266の動作が不安定なときの対策案は、電池駆動においても解決策になり得るか?

 いや、それはそれとして乾電池で連続340日間動作するESP8266搭載ワイヤレスセンサESP32 WiFi 温湿度計の長期間動作が、電池駆動を含めた解決策なのか?

 ただ、データロガーではなく、モニタ付き温湿度計付き時計なのでどこまで取り入れられるかは未知数だが、中でもULP、これを使った場合、時計も秒を出力せず、温湿度も1分ごとに計測、Deep Sleepから復帰、表示、Deep Sleep...なんてこともできたりするのだとすれば、1筋の光明が...。

関連リンク

ウェブ造ホーム前へ次へ