1

I am trying to implement OTA with ESP32 and thinking of using HTTP multipart/form-data as a protocol. Since I don't have a broad background in this protocol type, I wonder how I can receive HTTP multipart/form-data(file) from the server (to ESP32).

Could you give me any code examples, useful libraries, or general flow of it?

Also, I am currently using HTTPClient.h to post data to the server. Is this library enough to implement HTTP multipart form, or do I need the other one?

FYI, my OTA scenario is as below:

  1. (ESP32->server) check if there are any required updates
  2. (server->ESP32) send whether there exists any update
  3. (ESP32->server) request updates
  4. (server->ESP32) send firmware update file(s)
  5. (ESP32) receive the file(s) and reboot the device

Thank you in advance!

rnjsrkdmf
  • 11
  • 2
  • why multipart? just download a bin file – Juraj Sep 23 '22 at 09:09
  • Hi rnjsrkdmf, if you wish to understand the basics of any topic, it's more productive to google for it first. There are lots of tutorials and existing answers in SO e.g. https://stackoverflow.com/questions/4526273/what-does-enctype-multipart-form-data-mean. By the way, the OTA mechanism you're trying to add has already been implemented by Espressif ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_https_ota.html – Tarmo Sep 23 '22 at 10:10
  • What server would you use? I understand your concern. I have implemented this myself as well with Nodejs as a server, mongodb as a database and any esp32-wrover-e. If your server is nodeJS i would gladly write you an example. – Dr.Random Sep 26 '22 at 07:25

2 Answers2

0

Here's a function that will do one file upload and one field of data using POST

void UploadToDataverse() {
  //Check WiFi connection status
  if(WiFi.status()== WL_CONNECTED){
    WiFiClient client;

    // Length (with one extra character for the null terminator)
    int str_len = DATASET_REPOSITORY_URL.length() + 1; 
    // Prepare the character array (the buffer) 
    char char_url [str_len];
    // Copy it over 
    DATASET_REPOSITORY_URL.toCharArray(char_url, str_len);
    
    if (!client.connect(char_url, 8080)) {
      Serial.println("Cloud server URL connection FAILED!");
      Serial.println(char_url);
      int server_status = client.connected();
      Serial.println("Server status code: " + String(server_status));
      return;
    }
    Serial.println("Connected to the Dataverse.");

    String lineEnd = "\n";
    String twoHyphens = "--";
    String boundary = String("*****") + String("AaB03x") + String("*****");
    String charset="UTF-8";


    client.println("POST /Home/Index HTTP/1.1");
    client.println("Host: " + SERVER_URL);
    client.println("Connection: Keep-Alive");
    client.println("ENCTYPE: multipart/form-data");
    client.println("User-Agent: AeonLabs LDAD");
    client.println("Content-Type: multipart/form-data; boundary=" + String(boundary));
    client.println();
    client.println(String(lineEnd) + String(twoHyphens) + String(boundary) + String(twoHyphens) + String(lineEnd));
    client.println("Content-Disposition: form-data; name=\"file\"; filename=\""+ String(EXPERIMENTAL_DATA_FILENAME) +"\""+ String(lineEnd));
    client.println("Content-Transfer-Encoding: binary" + String(lineEnd) + String(lineEnd));

    // send 'jsonData' >>> send multipart form data after file data...
    String data2Send = "{\"description\":\"My description.\",\"categories\":[\"Data\"], \"restrict\":\"false\", \"tabIngest\":\"false\"}";      

    client.println(String(lineEnd) + String(twoHyphens) + String(boundary) + String(twoHyphens) + String(lineEnd));
    client.println("Content-Disposition: form-data; name=\"jsonData\"");
    client.println(lineEnd);
    client.println("Content-Type: text/plain; charset=" + String(charset));
    client.println(lineEnd);
    client.println(lineEnd);
    client.println(data2Send);
    client.println(lineEnd);
    //client.flush();

    // Start sending dataset file
    File file = FFat.open(EXPERIMENTAL_DATA_FILENAME, FILE_READ);
    client.println(String(lineEnd) + String(twoHyphens) + String(boundary) + String(twoHyphens) + String(lineEnd));
    client.println("Content-Disposition: form-data; name=\"file\";filename=\"" + String(EXPERIMENTAL_DATA_FILENAME) + "\"" + String(lineEnd));
    client.println(lineEnd);
    while (file.available()){
      client.write(file.read());
    }
    file.close();

    // finnish the wrapper 
    client.println(lineEnd);
    client.flush();
    client.println(String(lineEnd) + String(twoHyphens) + String(boundary) + String(twoHyphens) + String(lineEnd));
    
    Serial.println(">>><<<");

    Serial.println("Response:");
    while (client.connected()) {
      if (client.available()) {
        String response = client.readStringUntil('\n');
        Serial.println(response.c_str());
      }
    }

    client.stop();
  }
  else {
    Serial.println("WiFi Disconnected");
    if (connect2WIFInetowrk()){
      UploadToDataverse();
    }
  }
}
Miguel Tomás
  • 1,714
  • 1
  • 13
  • 23
-3

I think what you need is to use the Mongoose OS implementation for your ESP32. Its pretty straightforward, and all your remote OTA update requirement will be satisfied.

Thank You, Hope it helps

Naveen PS
  • 13
  • 5
  • Telling someone to change the entire underlying software or OS they're using because they're not sure how to use a library is not helpful. – romkey Sep 23 '22 at 17:28