ESP32(Arduino)で画像をPOST するサンプル

# 
ESP32 で SDカード内のファイルをPOSTするサンプルコード

# Github
[esp32-post-image](https://github.com/tfuru/esp32-post-image)

# 構成
[Adafruit ESP32 Feather](https://www.adafruit.com/product/3405)  
[マイクロＳＤカードスロットＤＩＰ化キット](http://akizukidenshi.com/catalog/g/gK-05488/)  

# SPI接続
Adafruit ESP32 Feather と マイクロＳＤカードスロットＤＩＰ化キット を SPI接続する  

|--|ESP32|SDカード|  
|:--:|:--:|:-:|  
|SPI_SCL|5|2|  
|SPI_MOSI|18|3|  
|3V3|--|4|
|SPI_CS_SD|16|5|
|GND|--|6|
|SPI_MISO|19|7|  

# 開発環境
[PlatformIO IDE for VSCode](https://docs.platformio.org/en/latest/ide/vscode.html#installation)   
環境でビルドとESP32へ書き込みができるのでリンクを参考にインストールする。

# コード
`multipart/form-data` で サーバにPOSTするコード

```c++:ImagePost.cpp
bool ImagePost::postImage(File *file, FUNC_POST_IMAGE_CALLBACK callback){
...
    //boundary 作成
    randomSeed(micros() + analogRead(A0));
    for (int i = 0; i < 3; i++) {
      ltoa(random(0x7FFFFFF), boundary + strlen(boundary), 16);
    }
    strcpy(contentType, "multipart/form-data; boundary=");
    strcat(contentType, boundary);

    String payloadHeaderFormat = ""
                     "--%s\r\n"
                     "Content-Disposition: form-data; name=\"image\"; filename=\"%s\"\r\n"
                     "Content-Type: application/octet-stream\r\n"
                     "Content-Transfer-Encoding: binary\r\n"
                     "\r\n"
                     ;
    //body 部分上部 boundary と下部 boundary データを作成
    char payloadHeader[200] = {0};
    sprintf(payloadHeader,
                payloadHeaderFormat.c_str(),
                boundary,
                file->name());

    char payloadFooter[50] = {0};
    sprintf(payloadFooter, "\r\n--%s--\r\n", boundary);
    
...

    if (client.connect(API_HOST,API_PORT)){
        client.printf("POST %s HTTP/1.1\n",API_PLANTER_IMAGE);
        client.print(F("Host: "));client.println(API_HOST);
        client.println(F("Accept: application/json"));
        client.println(F("Connection: close"));
        client.print(F("Content-Type: "));client.println(contentType);
        client.print(F("Content-Length: "));client.println(contentLength);        
        client.println();
        client.print(bodyHeader.c_str());
        client.flush();
              
        size_t w = 0;
        while (file->available()) {
            w += client.write(file->read());            
            if((w % API_POST_IMAGE_FLASH_SIZE) == 0){
                client.flush();
            }
        }
        client.flush();
        client.print(bodyFooter.c_str());
        client.flush();

        //レスポンス タイムアウト
        int timeout = millis() + (API_POST_IMAGE_RESPONSE_TIMEOUT_SEC * 1000);
        bool isTimeout = false;
        while (client.available() == 0) {
            if (timeout - millis() < 0) {
                Serial.println(">>> Client Timeout !");
                isTimeout = true;
                break;
            }
        }
        if(isTimeout == false){         
            String line = "";
            while(client.available()){
                line = client.readStringUntil('\r');
                response += line;
            }
        }
        client.stop();        
    }
    //コールバック
    callback(response.c_str());
    return true;
}

```

# 画像受け取りサーバ
server ディレクトリで `node app.js` を実行する
ESP32から POST されると `/uploads/` ディレクトリにファイルが作成される

```
$node app.js 
Example app listening at http://192.168.100.140:8080
```