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要求を発行

スケッチ:

POST 受信対応 スケッチ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// +==========================================================================+
// |  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 件のコメント:

コメントを投稿