2019年5月19日日曜日

ESP32/arduino : POST受信

目的: 

クライアントからの POST 要求で LED ON/OFF できるようにする。

POST 受信処理:

クライアントからのメッセージは、
 リクエスト行 (GET / POST 等 )
 リクエストヘッダ
 空行
 メッセージボディ (POST のデータ等)
の順に送られ、GET要求の場合は メッセージボディが無く、POST要求ではメッセージボディに送信データが入る。
POST受信処理では、空行の後のメッセージボディを受信して解析する必要がある。

リクエスト行から空行の間は、行単位(最後が '\r\n'で終了) の為、GET要求の受信処理では行単位で処理 ( readStringUntil('\n') で 行末('\n') までを種付く) していたが、メッセージボディの最後は ('\r\n') になるとは限らない為、readStringUntil('\n')で データを取得すると、('\n') が見つからずに タイムアウト (Default 1秒) まで待たされる。この為、受信バイト数分を取得 (メッセージボディ全てを一度に取得) して処理することとした。

例:

LED ON/OFF (固定 IPアドレス) の スケッチ に POST 受信処理を追加した例を以下に示す。

 WEB画面:

GET 用の ON, OFF ボタン と
POST 用 の ON, OFF ボタン を 設置

POST_ON, POST_OFF 押下時、POST要求を発行

スケッチ:

// +==========================================================================+
// |  WiFi LED ON/OFF TEST ( GET / POST )                                     |
// +==========================================================================+
                                                 //
#include <WiFi.h>                                // WiFi 用
                                                 //
// ----------------------------------------------------------------------------
// 各種設定                                                                  --
// ----------------------------------------------------------------------------
const char* ssid = "hogehoge" ;                  // WiFi AP  SSID
const char* password = "hogehogepassy";          // WiFi AP  パスワード
                                                 //
String html;                                     // HTML 格納用
                                                 //
IPAddress ip(192, 168, 1, 32);                   // ESP32 IPアドレス
IPAddress gateway(192,168, 1, 1);                //       ゲートウェイアドレス
IPAddress subnet(255, 255, 255, 0);              //       サブネットマスク
IPAddress DNS(192, 168, 1, 90);                  //       DNS アドレス
                                                 //
WiFiServer server(80);                           //
                                                 //
// ----------------------------------------------------------------------------
// 初期設定                                                                  --
// ----------------------------------------------------------------------------
void setup()                                     //
{                                                //
    Serial.begin(115200);                        // シリアル出力開始
    pinMode(2, OUTPUT);                          // LED 用 PIN 設定
                                                 //
    WiFi.config(ip, gateway, subnet, DNS);       // ESP32 WiFi 設定
    delay(10);                                   //
                                                 //
    Serial.println();                            // デバッグ用出力
    Serial.print("Connecting to ");              //
    Serial.println(ssid);                        //
                                                 //
    WiFi.begin(ssid, password);                  // WiFi 開始
                                                 //
    while (WiFi.status() != WL_CONNECTED) {      //
        delay(500);                              //
        Serial.print(".");                       //
    }                                            //
                                                 //
    Serial.println("");                          // デバッグ用出力
    Serial.println("WiFi connected.");           //
    Serial.println("IP address: ");              //
    Serial.println(WiFi.localIP());              //
                                                 //
    server.begin();                              // WiFi サーバ 開始
                                                 //
}                                                //
                                                 //
// ----------------------------------------------------------------------------
// メインループ                                                              --
// ----------------------------------------------------------------------------
void loop(){                                     //
  String line = "" ;                             // データ格納用
  char buf[257] ;                                //
  int  n ;
  bool   post_req = false ;                      // ポスト要求フラグ
  WiFiClient client = server.available();        // listen for incoming clients
                                                 //
  if (client) {                                  // if you get a client,
    Serial.println("***** Client access start *****"); // デバッグ用出力
    while (client.connected()) {                 // client が接続されている間
      if (client.available()) {                  //  client からのデータがある間
        line = client.readStringUntil('\n');     //   データを1行分取得
        Serial.println(line);                    //   デバッグ用出力
        if (line.indexOf("POST /") != -1) {      //  "POST /" 受信の場合
          post_req = true ;                      //     LED on
        }                                        //
        if (line.indexOf("GET /?on") != -1) {    //  "GET /?on" 受信の場合
          digitalWrite(2, HIGH);                 //     LED on
        }                                        //
        if (line.indexOf("GET /?off") != -1) {   // "GET /?off" 受信の場合
          digitalWrite(2, LOW);                  //     LED off
        }                                        //
        // 空行を受信した場合 ---------------    //
        if (line.length() == 1 && line[0] == '\r'){ // 空行の場合
          if (post_req) {                        //  POST 要求の場合
            line = ""  ;                         //    データ格納用変数初期化
            while (n=client.available()) {       //    残りデータを全て受信
              if (n<256){                        //    
                client.readBytes(buf,n) ;        //
                buf[n] = 0 ;                     //
              } else {                           //
                client.readBytes(buf,256) ;      //
                buf[256] = 0 ;                   //
              }                                  //
              line += buf ;                      //
            }                                    //
            // POST データ処理 -------------     //
            Serial.println(line);                //   デバッグ用出力
            if (line.indexOf("post_on") != -1) { //  "on" の場合
              digitalWrite(2, HIGH);             //     LED on
            }                                    //
            if (line.indexOf("post_off") != -1) { // "off" の場合
              digitalWrite(2, LOW);              //     LED off
            }                                    //
          }                                      //
          // 終了処理 ---------------------------//
          send_response(client) ;                //
          Serial.println("Send HTML !!");        //
          break;                                 // break while loop
        }                                        //
      }                                          //
    }                                            //
    delay(1);                                    // give the web browser time to receive the data
    // close the connection: --------------------//
    client.stop();                               //
    Serial.println("Client Disconnected.");      //
    Serial.println("--------------------------------------------------");
  }                                              //
}                                                //
// ------------------------------------------------------------------
void send_response(WiFiClient client) {
    // 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();
    // the content of the HTTP response follows the header:
    client.print("<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>" ) ;
    client.print("<style>input {margin:8px;width:100px;}div {font-size:16pt;text-align:center;width:250px;border:solid 4px #93ff93;}</style>" ) ;
    client.print("<title>Color LED Controller</title></head>" ) ;
    client.print("<body><div><p>LED ON/OFF</p>" ) ;
    client.print("<form method='get'>" ) ;
    client.print("<input type='submit' name='on' value='GET_ON' /><input type='submit' name='off' value='GET_OFF' />" ) ;
    client.print("</form>" ) ;
    client.print("<form method='post'>" ) ;
    client.print("<input type='submit' name='post_on' value='POST_ON' /><input type='submit' name='post_off' value='POST_OFF' />" ) ;
    client.print("</form>" ) ;
    client.print("</div></body></html>" ) ;
    client.println();
    Serial.println( " --- send responce --- ");
}

 以下、メモ

・詳細は コメント参照。
(行#58~60)
 POST処理用 変数定義。
(行#69~71)
 リクエスト行が POST の場合に POST処理フラグを立てる。
(行#80~100)
 POST 処理フラグが 立っていた場合のPOST処理。
  (行#81~91) で メッセージボディのデータを取得。
  readStringUntil('\n') や readString() を使用すると、タイムアウト(Default 1000mS) まで待ってしまい、ボタンを押下してから LED の ON/OFF まで 1秒かかる。
 この為、残りデータ数 (client.available() の戻り値) 分を client.readBytes() で取得している。
  (行#92~98) で メッセージボディのデータを処理。
 処理内容は、GET と同様。

 

0 件のコメント:

コメントを投稿