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

ESP-01/12/ESP32でブラウザ(スマホ/タブレット/PC)越しLED制御

ホーム前へ次へ
ESP8266って?

ESP-01/12/ESP32でブラウザ(スマホ/タブレット/PC)越しLED制御

ESP-01/12/ESP32でブラウザ(スマホ/タブレット/PC)越しLED制御

2018/07/07

 Wi-Fi(wifi)モジュールESP8266の内、ESP-01を使ってブラウザからLEDをON/OFFしてみるページ。

 動作確認に過ぎず、実用品ではありませんが、基本操作ということで。

 もちろん、パソコンだけでなく、スマホやタブレットからも操作可能(WiFiに接続後、ブラウザでIPアドレス指定)。

 前半では、プログラム中にHTMLを直書きしますが、後半では、SPIFFSを使ってフラッシュメモリのファイルシステム上にHTMLファイルを置き、ブラウザからLEDを遠隔操作します。

 ただし、検索してみるとSPIFFSにに関しては、ESP-01でもモノによっては、当初できていたものが、後にできなくなったという情報もあるようなので、その場合は、上位機種を使うとよいでしょう。

 ちなみに自身が使用したESP-01は、直だった頃のAmazonマーケットプレイスHiLetgoから購入、2017/06/06に購入した(2017/06/17に届いた)もの。

 尚、今回は、ESP-01を使いましたが、LED操作にしてもSPIFFSにしても、これでできるということは、上位機種のESP-02...、ESP-12...、ESP-WROOM-02、ESP32などESP-xxでも、これら開発ボードでもできるということで。

 ただ、ESPシリーズの内、日本の技適を通っていて、モヤモヤすることなく、堂々と使えるものは、いまのところ、ESP-WROOM-02/ESP-WROOM-32(とこれらを搭載した開発ボード)のみ。

 何れにしても最も手軽で手間がなく、限りなく悩みどころが少ないのは、開発ボードです。

前提

 Arduino IDEが利用できることは、もちろん、ESP8266やESP32をArduino IDEで使えるようにしておくこと。

 ESP-01やESP-02〜ESP14などのESP8266チップなら、Arduino IDEの[ツール] => [ボード]から[Generic ESP8266 Module]を選択、ESPモジュールにスケッチをアップロードできる状態であること。

 ESP32なら、[espressif/arduino-esp32]の要領でESPモジュールにスケッチをアップロードできる状態であること。

 ちなみにこれらArduino IDEの環境設定で追加する方法の場合、カンマ区切りで複数指定可能。

 参考までに自身の使用しているOSは、Debian(Linux)、Arduino IDEのバージョンは、1.8.5。

開発ボードの場合[2019/05/14追記]

 尚、ESP8266やESP32の開発ボードである場合、先のようにArduino IDEで利用可能にするとボード情報に様々な関連ボードが追加されるので、使うボードに適したものを選択することになるわけですが、例えば、次のようになります。

 ESP8266 NodeMCUボードなら、Arduino IDEの[ツール] => [ボード]から[NodeMCU 0.9 (ESP-12 Module)]や[NodeMCU 1.0 (ESP-12E Module)]などを選びます。

 ちなみにESP8266 NodeMCUボードについては、ボード上のピン番号(D0-D8)と実際のGPIOが異なるのでプログラム・スケッチを書く場合、標準でインクルードされる模様のpins_arduino.hの定義に沿って指定します。

 また、ESP32 DevKitCやDevKitボードなら、[ESP Dev Module]を選びます。

 開発ボードについては、あとは基本的にArduinoボードと同様にスケッチをアップロード、電源としてUSBケーブルをつなぐか、VIN(5V)/G(ND)に5Vを供給して組んだ回路を実行するだけです。

ESP-01へのスケッチのアップロード準備

 ESP-01にプログラムをアップロードするには、USBポートを備えたCP2102やFTDI系のシリアルUSB変換モジュールを併用する必要があります。

 尚、USBポートを備えた開発ボードではないESP-01〜ESP-14単体及びブレイクアウトボード(≒ピッチ変換基板)との組み合わせは、全て同様にシリアルUSB変換モジュールを要します。

 ESP-01の定格電圧は3.3VなのでシリアルUSB変換モジュールも3.3V専用か3.3V/5V兼用なら3.3Vに切り替えて接続します。

 尚、ESP-01は、プログラム書き込み時と実行時でRSTとGPIO0のHIGH/LOWを巧みに切り替える必要がありますが、RTS/DTRピン(ホール)のあるFTDIモジュールなら、これらにESP8266のRST/GPIO0をそれぞれ接続することで自動でアップロードできる為、これを使うことをおすすめします。

 ただ、これらRTS/DTR、ブレッドボード上でピンホールにジャンパワイヤを挿す場合、ピンヘッダや3.3V/5V切り替え用ジャンパピンのハンダ部が隆起しているのですが、ピンホールが、この付近にある為、USBシリアル変換モジュール並びに挿したジャンパワイヤを手でうまく押さえる必要はあるでしょう。

 なんなら、手で持った方が、安定するでしょう。

 ちなみにESP-01については、SPIFFSを使おうにもフラッシュメモリ容量の関係でArduinoOTAは難しい模様。

ESP-01FTDI別電源
RXTX-
TXRX-
RSTRTS-
GPIO0DTR-
VCC-3.3V
GNDマイナス

 また、パソコンのUSBは最大500mAと大丈夫そうに思えるし、実際、たいていの場合、書き込みできますが、往々にしてWiFiモジュールは多くの電流を必要とすることがあり、ESP8266も300mAを超えることもあるとのことなのでそれらが要する電流量不足やパソコンのUSBポートの損傷回避などを考慮し、実行時はもちろんのこと、書き込み時も念の為、別電源をとった方がよさ気。

 尚、検証・運用時には、元電源が3.3Vだと過電流により2V程度まで電圧降下し、ESP8266にリセットがかかり、WiFi接続が途切れるなどの状況になる可能性があるので5Vから降圧、更に電圧降下に耐え得るよう、より短時間で通常電圧に戻る負荷過渡応答特性に優れた電圧レギュレータを使う必要がありそう。

 全ての回路で同じとは限りませんが、とある回路を作って9V電池で動かしてみたところ、7V切ったあたりで動作が不安定になり、電池を交換したら正常になりました。

 ただし、これは、ESP-01やESP-xx+ピッチ変換基板での話であって、開発ボードについては、何らかの対策が講じられているようで、この必要はなさそうです。

スケッチ(HTML直書き)

/*********
  Rui Santos
  Complete project details at http://randomnerdtutorials.com  
*********/
 
// Load Wi-Fi library
#include <ESP8266WiFi.h>
 
// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
 
// Replace any SSID/PW for ESP SoftAP Mode
#define SOFTAP_SSID "XXXXXX"
#define SOFTAP_PW "YYYYYY"
 
// Set web server port number to 80
WiFiServer server(80);
 
// Variable to store the HTTP request
String header;
 
// Auxiliar variables to store the current output state
String output2State = "off";
String output1State = "off";
 
// Assign output variables to GPIO pins
/*
const int output2 = 0;
const int output1 = 1;
*/
const int output2 = 2;
const int output1 = 3;
 
void setup() {
  Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(output2, OUTPUT);
  pinMode(output1, OUTPUT);
  // Set outputs to LOW
  digitalWrite(output2, LOW);
  digitalWrite(output1, LOW);
 
  // AP+STAモードの設定
  WiFi.mode(WIFI_AP_STA);
  // APとして振る舞うためのSSIDとPW情報
  WiFi.softAP(SOFTAP_SSID, SOFTAP_PW);
  Serial.print("Connecting to ");
  Serial.println(SOFTAP_SSID);
  Serial.println("----------");
 
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
//    Serial.print(".");
    Serial.print(". ");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}
 
void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients
 
  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // turns the GPIOs on and off
            if (header.indexOf("GET /5/on") >= 0) {
              Serial.println("GPIO 2 on");
              output2State = "on";
              digitalWrite(output2, HIGH);
            } else if (header.indexOf("GET /5/off") >= 0) {
              Serial.println("GPIO 2 off");
              output2State = "off";
              digitalWrite(output2, LOW);
            } else if (header.indexOf("GET /4/on") >= 0) {
              Serial.println("GPIO 1 on");
              output1State = "on";
              digitalWrite(output1, HIGH);
            } else if (header.indexOf("GET /4/off") >= 0) {
              Serial.println("GPIO 1 off");
              output1State = "off";
              digitalWrite(output1, LOW);
            }
            
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #77878A;}</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");
            
            // Display current state, and ON/OFF buttons for GPIO 2  
            client.println("<p>GPIO 2 - State " + output2State + "</p>");
            // If the output2State is off, it displays the ON button      
            if (output2State=="off") {
              client.println("<p><a href=\"/5/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
              
            // Display current state, and ON/OFF buttons for GPIO 1  
            client.println("<p>GPIO 1 - State " + output1State + "</p>");
            // If the output1State is off, it displays the ON button      
            if (output1State=="off") {
              client.println("<p><a href=\"/4/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");
            
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}
 

 スケッチは、ESP8266 Web Server with Arduino IDEのものを使わせて頂きました。

 が、これ、STA/APモードにも関わらず、APモードがあまりに無防備だったため、太字部分(2ブロック)を追記。

 というわけでSTAモードでクライアントとして宅内無線LANアクセスポイントに、APモードとして宅内無線LANの同一ネットワークアドレスにぶら下がる恰好で接続できることをnmapコマンドも併用して確認、ESP-01のアクセスポイントに無線カード内蔵PCを接続できることも確認しました。

ピンの差し替え

 ブレッドボード上に抵抗220Ω程度-LED-GNDを2系統つなぎ、プラス側にスケッチでoutput1/output2に格納したピン番号に該当するピンを挿します。

ブラウザでアクセス

Connecting to SSID
. . . . . . .
WiFi connected.
IP address:
192.168.***.***
 

 PCが認識したUSBポートが、Arduino IDE上で選択されていることを確認、シリアルモニタを開き、baudrateは115200、改行なし(WindowsならCR+LFかも)とします。

 もし、何も表示されなければ、ESP-01の入力電源を入れ直すなどして再起動、宅内無線アクセスポイントに接続、表示されるはずのDHCPで払い出されたIPを確認します。

 ブラウザのURL入力欄に、ここで表示されたIPアドレスを入れてアクセスするとスケッチのgpio1/gpio2、それぞれにON/OFFを切り替えるボタンが1つずつ、計2つ表示されます。

 出力ピンとして0〜3までの何れかを指定したのであれば、それらピンを正しくつないでいれば、何れもLEDが点灯・消灯するはずなのでボタンを押して確認します。

 尚、既にリモート操作できるようになっており、シリアルモニタでIPアドレスを確認するためだけにつないでおいたFTDIモジュールはなくてもLEDの点灯、消灯は切り替えられるのでPCからUSBを抜くなり、ESP-01間のジャンパワイヤを外すなりして当然ですが、無線で操作できることを確認します。

[追記] ESP8266製ドアベルのようにIPアドレスを固定したり、ESPでmDNSを使ってIPではなくホスト名.localでアクセスすることもできます。

ESP-01の出力ピン

 ちなみに先日、当てずっぽうで、ESP-01のGPIO0/GPIO2が出力ピンとなることを期待してpinModeの第1引数を0と1とした時、実際には、GPIO0/TXが出力ピンとして機能し、ブレッドボード上でLEDをつなげて確認してみたところ、TXピンの出力はオンボードの青いLEDと逆の挙動を示しました。

 続いて以下のような遠回りをした結果、結局のところ、出力ピンとする場合(入力ピンとしては未確認)、特別なにか設定する必要もなく、普通にpinMode(pin,OUTPUT)としてpinに0/1/2/3を指定すれば、GPIO0/TX/GPIO2/RXの4ピンを出力ピンとして機能させることができることがわかりました。

 esp8266_gpio_pin_allocationsによるとpinMode()宣言を使うとプログラム実行時には、TX、RXが、1、3として使えるように見える。

 また、そこにある表からするとpinMode()の第1引数に1(TX)、3(RX)、第2引数にFunction 0とするとTX/RX、Function 3とするとGPIO1/GPIO3になるかのように読み取れるも具体的な書き方は載っていない...。

 が、How to I make the tx and rx pins on an esp-8266-01 into GPIO pins?によれば、pinMode宣言の第2引数は、アンスコ付きのFUNCTION_0かFUNCTION_3で認識に間違いがなければ、TX/RXとGPIO1/GPIO3を同時に使うことはできないとあります。

 試してみたところ、pinModeの第1引数を1と3とし、第2引数を共にFUNCTION_0/FUNCTION_3(としても0/3)として宣言してもエラーにはならず、書き込めることは書き込めるが、TX/RXピン何れからも出力を得ることはできなかった...。

 結局、徒労に終わり、前述のように1ピンと3ピンとして普通の出力ピンと同様に扱えばよいことがわかりました。

 実は、当初、SPIFFS版のみ、記載するつもりでいたのですが、それ以前にAjaxだからなのか?自身にはよくわかりませんが、RX/TXをGPIOピンとして使うことができなかった為、急遽、RX/TXをGPIOピンとできたケースを前段に、SPIFFS版は、GPIO0/GPIO2のみGPIOピンとして使う方法と併せて後段に記載することにした次第。

ESP-01でSPIFFSを使いつつ、ブラウザからLEDをリモート操作

 ここまでは、プログラム中にHTMLを直書きしましたが、次にHTMLファイルをESP-01のフラッシュメモリ内のファイルシステム上に保存しておく方法でブラウザ経由でLED操作をしてみます。

 これには、Spiffsを使います。

 Spiffsとは、比較的低速ですが、少ない端子でマイコン内部で使われるデバイス同士を接続するバスであるSPI/Serial Peripheral Interfaceを使い、アクセスするNOR型フラッシュメモリ上にファイルシステムを配置できる、組み込み機器を想定した仕組み。

 言い換えると容量はさほど大きくないにしてもRAMにアクセスする必要もないフラッシュメモリ上にファイルを保存して、プログラム実行時には、そこから比較的高速に読み出せる(書き込みは、書き換え)という便利なもの。

 つまり、今回のケースでは、プログラムにHTMLを直書きしなくてもフラッシュメモリ上にHTMLファイルを置いておけるからプログラムがスッキリするし、スケッチ保存領域とは異なるメモリ上に保存できるからスケッチ保存領域を節約できるよという話。

 ちなみにファイル単位というわけにはいかないものの、Arduinoにも、これと同じような仕組みとしてフラッシュメモリにデータを格納するためのPROGMEMキーワードがある...と思ったら、Guide to PROGMEM on ESP8266 and Arduino IDEによれば、PROGMEMは、ESP8266にも移植されているそうな。

ESP-01でSPIFFSを使うための準備

 ESP-01でSPIFFSを使うためには、ちょっとした準備が必要となります。

 Arduinoのlibrariesフォルダがあるフォルダ上(librariesフォルダと同じ階層)に[tools]フォルダを作成、その中にUpdate plugin for newer Arduino IDEにアクセスし、最新の.zip(執筆時点では、ESP8266FS-0.3.0.zip)をダウンロード・展開。

 既にArduino IDEを開いていた場合は、再起動、そうでなければ、起動するだけ。

 これで[ツール]メニューを見ると[ESP8266 Sketch Data Upload]という新たなメニューが表示されているはず。

 ただし、Arduino IDEのバージョンによっては、Stable版ではSPIFFSに未対応である為、開発版にする必要があるといった情報もあるので確認の上、そうするか、他のバージョンを使うとよいでしょう。

Arduino IDEでSPIFFSを使うための準備

 Arduino IDEの[ツール] => [ボード]で[Generic ESP8266 Module]を選択した状態で[Flash Size:]を[512K (no SPIFFS)]以外のものを選びます。

 サイズを大きくすればするほど、かなり時間がかかる為、最小限のものを選ぶのが賢明。

 [Erase Flash:]は、それまでの作業を思い返しつつ、ボードへの書き込み状態によって適宜選択します。

SPIFFSにアップするデータの置き場

 フラッシュメモリにアップロードするデータは、パスの通ったArduino IDE用のスケッチごとのフォルダ下に作成した[data]フォルダ内に置きます。

フラッシュメモリへのアップロード

 Arduino IDEで当該スケッチを開いた上で[ツール]メニューから先程追加されたはずの[ESP8266 Sketch Data Upload]を選択すると当該スケッチフォルダ下の[data]フォルダがフラッシュメモリ内のファイルシステム上に書き込まれます。

 この時、開発モジュールではなくUSBシリアル変換モジュールを使う場合、リセットを自動で行なってくれるRTS/DTRピンホールのあるFTDIモジュールが便利、ESP-01ならRTS/DTRにRST/GPIO0を、ESP-12ならRTSにRESETをつなぐことを忘れずに。

 また、実行時に使うGPIOピンは回路につながない状態で行なうこと。(そうしないと書き込みに失敗します。)

 あと、たぶん、スケッチを開いた状態かつ、ESPボードにスケッチを書き込んだ後にこの作業はした方がよいかも。

 [SPIFFS Uploading Image...]が、[SPIFFS Image Uploaded]になったら完了。

[追記] ESP8266/ESP32+Arduino IDEでSPIFFSを使う方法参照。

スケッチ(SPIFFS版)

 スケッチは、せっかくなので、Ajaxにしてみるのindex.htmlとinoをそのまま使わせて頂きました。

ブラウザでアクセス

 アクセス方法については、前段と同じ。

 尚、同スケッチのピン番号だけ0/1/2/3と変えて4通り試してみたところ、0(GPIO0)と2(GPIO2)はいけたものの、1(TX)と3(RX)は、点灯させることができなかったのですが、一体、なぜだろう...謎。

ホーム前へ次へ