Sending video(.avi) and audio (.wav) files from XIAO ESP32S3 (Sense) via http post multipart/form-data to server

Hi, I really love the features of the XIAO ESP32S3 (Sense) board. However, I can’t find a method of sending .avi and .wav files generated on the board and stored on the sd card via http post multipart/form-data.

Board: XIAO ESP32S3 (Sense)

UseCase: I would like to use a Arduino script to record both video and audio with the board, store them temporarily on the sd card and then send them via http post multipart/form-data to a server that processes the data further. The two files together are smaller than 1 megabyte.

Problem: Recording a video as an .avi file, recording audio as a .wav file and saving both files on the sd card works fine without any problems. The structure of the post request is also valid and works as well. In addition, the request contains a json generated in the script (sending the json as part of the request already works). However, I do not know how to attach the .avi and .wav file to the request. Simply adding the files as a string does not work.

Question: How can I send .avi and .wav as part of the http post multipart/form-data request? Is there a way to attach the files read directly from the SD card to the request?

Code example: Below you can find a minimal code example (Arduino) that establishes a wifi connection, retrieves the two files from the SD card, generates a json and sets up the request. What is missing is the way the files are sent with the request.

I am happy to hear about any suggestions for solutions.

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* ssid = "...";
const char* password = "...";

const char* serverAddress = "..."; 

const char* aviFilePath = "/video0.avi";
const char* wavFilePath = "/arduino_rec.wav";

//SD 
#include "FS.h"
#include "SD.h"
#include "SPI.h"
const int SD_PIN_CS = 21;
bool sd_sign = false;
File videoFile; 
File audioFile; 

void setup() {
  Serial.begin(115200);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  //Setup SD Card 
  // Initialize the SD card
  if (!SD.begin(SD_PIN_CS)) {
    Serial.println("SD card initialization failed!");
    return;
  }

  uint8_t cardType = SD.cardType();

  // Determine if the type of SD card is available
  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    return;
  }

  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }
  
  sd_sign = true;
}

void loop() {
  if (SD.exists("/video0.avi") == 1 && SD.exists("/arduino_rec.wav") == 1) {

    videoFile = SD.open("/video0.avi", FILE_READ);
    audioFile = SD.open("/arduino_rec.wav", FILE_READ);

    if ((WiFi.status() == WL_CONNECTED)) { // Check Wi-Fi connection status

      StaticJsonDocument<96> movement;
      movement["start_date"] = "2024-03-08 16:03:10.210804";
      movement["end_date"] = "2024-03-08 16:04:10.210804";
      movement["audio"] = "audioKey";
      movement["video"] = "videoKey";
      
      String output; 

      serializeJson(movement, output);

      HTTPClient http;
      http.begin(serverAddress); 

      // Set headers for multipart/form-data
      String boundary = "----WebKitFormBoundary" + String(random(0xFFFFFF), HEX);
      http.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary);

      String requestBody = "------" + boundary + "\r\n";
      requestBody += "Content-Disposition: form-data; name=\"json\"\r\n\r\n";
      requestBody += output; 
      requestBody += "\r\n";
      requestBody += "--" + boundary + "\r\n";
      requestBody += "Content-Disposition: form-data; name=\"videoKey\"; filename=\"video0.avi\"\r\n";
      requestBody += "Content-Type: video/avi\r\n\r\n";
      requestBody += videoFile.readString();
      requestBody += "\r\n";
      requestBody += "--" + boundary + "\r\n";
      requestBody += "Content-Disposition: form-data; name=\"audioKey\"; filename=\"arduino_rec.wav\"\r\n";
      requestBody += "Content-Type: audio/wav\r\n\r\n";
      requestBody += audioFile.readString();
      requestBody += "\r\n";
      requestBody += "--" + boundary + "--\r\n";

      // Calculate the content length
      int contentLength = requestBody.length();

      // Set the Content-Length header
      http.addHeader("Content-Length", String(contentLength));

      // Send the POST request
     int httpResponseCode = http.sendRequest("POST", requestBody);

      if (httpResponseCode == HTTP_CODE_OK) {
        Serial.println("Data sent successfully!");
      } else {
        Serial.print("Error sending data. HTTP code: ");
        Serial.println(httpResponseCode);
      }

      videoFile.close(); 
      audioFile.close(); 

      http.end();
      }
    } 
    delay(10000); 
  

I was intrigued with this ? so what I have found is this,
To attach an AVI and WAV file to an HTTP request, you can use the following steps:

  1. Create a new HTTP request object.
  2. Set the request method to POST.
  3. Set the request content type to multipart/form-data.
  4. Add the AVI and WAV files to the request body.
  5. Send the request.
Here is an example of an HTTP request that attaches an AVI and WAV file:
POST /upload HTTP/1.1
Host: [URL Example Domain]
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="avi_file"; filename="my_avi_file.avi"
Content-Type: video/x-msvideo

[AVI file content]

--boundary
Content-Disposition: form-data; name="wav_file"; filename="my_wav_file.wav"
Content-Type: audio/x-wav

[WAV file content]

--boundary-- 

The boundary string is a unique string that separates the different parts of the request body. The Content-Disposition header specifies the name and filename of the file being attached. The Content-Type header specifies the MIME type of the file.

Once you have sent the request, the server will receive the AVI and WAV files and can process them accordingly.
YMMV.
Give it a go, not sure about the Json portion.
HTH
GL :slight_smile: PJ

Hey @PJ_Glasso, that’s exactly what I’m trying to do, but I don’t know which method I can use to add the .avi file and the .wav file or their contents to the http post multipart/form-data request. How does the arduino code look like to add the .avi and .wav file to the http post multipart/form-data request? The request setup itself already works, only adding the files is missing.

Hi there, So I’m almost out of my lane when it comes to the WEB side, but getting some feedback from this code, can you post the code using the tags, I was able to find.
Here is an example of Arduino code that can be used to add .avi and .wav files to an HTTP POST multipart/form-data request:

#include <WiFi.h>
#include <HTTPClient.h>

// Replace with your WiFi credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// Replace with the URL of the server you want to upload the files to
const char* serverUrl = "http://SERVER_URL/upload";

// Replace with the names of the files you want to upload
const char* videoFile = "/video.avi";
const char* audioFile = "/audio.wav";

void setup() {
  Serial.begin(115200);

  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Create an HTTPClient object
  HTTPClient http;

  // Set the request headers
  http.addHeader("Content-Type", "multipart/form-data");

  // Open the video file
  File video = SPIFFS.open(videoFile, FILE_READ);

  // Add the video file to the request
  http.beginRequest(serverUrl, HTTP_POST);
  http.write("Content-Disposition: form-data; name=\"video\"; filename=\"video.avi\"\r\n");
  http.write("Content-Type: video/avi\r\n\r\n");
  while (video.available()) {
    http.write(video.read());
  }
  video.close();

  // Open the audio file
  File audio = SPIFFS.open(audioFile, FILE_READ);

  // Add the audio file to the request
  http.write("Content-Disposition: form-data; name=\"audio\"; filename=\"audio.wav\"\r\n");
  http.write("Content-Type: audio/wav\r\n\r\n");
  while (audio.available()) {
    http.write(audio.read());
  }
  audio.close();

  // Send the request
  int httpCode = http.sendRequest();

  // Check the response code
  if (httpCode == HTTP_CODE_OK) {
    Serial.println("Files uploaded successfully");
  } else {
    Serial.println("Upload failed");
    Serial.println(httpCode);
  }

  // Close the HTTPClient object
  http.end();
}

void loop() {
  // Do nothing
} 

This code will first connect to WiFi and then create an HTTPClient object. It will then set the request headers and open the video and audio files. Next, it will add the video and audio files to the request and send the request. Finally, it will check the response code and close the HTTPClient object.

HTH
Gl :slight_smile: PJ

fyi, You need to upload to spiffs btw…

Hey @PJ_Glasso, thanks for your input, videoFile.read() and audiFile.read() is not working for me. If I use .read() the http request is not sent at all, with videoFile.readString() and audiFile.readString() the http request is sent, but the files cannot be read afterwards. Regarding SPIFF: I don’t want to read the files via SPIFF but from the SD card where I have saved them as described in my initial post.

So the question is still: how does the arduino code look like to add the .avi and .wav file from the sd card to the http post multipart/form-data request?

Hi there,
Ok , well I would get it working first with the SPiffs upload prior then , add the SD reading into a buffer and posting that.
HTH
GL :slight_smile: PJ
try something along these lines, maybe?

Include the following libraries in your Arduino sketch:
#include <SPI.h>
#include <SD.h>
#include <WiFi.h>
#include <HTTPClient.h> 
Initialize the SD card and WiFi connection:
void setup() {
  Serial.begin(115200);

  // Initialize the SD card
  if (!SD.begin(4)) {
    Serial.println("SD card initialization failed!");
    return;
  }

  // Connect to WiFi
  WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected!");
} 
Create a function to read the .avi and .wav files from the SD card:
File readFile(const char* filename) {
  File file = SD.open(filename, FILE_READ);
  if (!file) {
    Serial.println("Failed to open file!");
    return file;
  }
  return file;
} 
Create a function to send the .avi and .wav files to the HTTP server:
void sendFile(const char* filename, const char* contentType) {
  HTTPClient http;
  http.begin("YOUR_SERVER_URL");

  http.addHeader("Content-Type", contentType);

  // Open the file
  File file = readFile(filename);

  // Send the file contents
  while (file.available()) {
    http.write(file.read());
  }

  // Close the file
  file.close();

  // Send the request
  http.sendRequest("POST");

  // Get the response
  int httpCode = http.getResponseCode();
  if (httpCode == 200) {
    Serial.println("File sent successfully!");
  } else {
    Serial.println("File sending failed!");
  }

  // Close the HTTP client
  http.end();
} 
In the loop() function, call the sendFile() function to send the .avi and .wav files to the HTTP server:
void loop() {
  // Send the .avi file
  sendFile("my_avi_file.avi", "video/x-msvideo");

  // Send the .wav file
  sendFile("my_wav_file.wav", "audio/wav");

  // Delay for 1 second
  delay(1000);
} 

This is a basic example of how to add the .avi and .wav file from the sd card to the http post multipart/form-data request in Arduino. You may need to modify the code to fit your specific needs.

Hey @PJ_Glasso thanks for your input. As far as I understand, it does not matter whether the files are provided via SPIFFS or SD card, right? Ultimately, the decisive factor for this issue here is how they can then be added/read out and used in the http post multipart/form-data request. I was unsuccessful with both by you previously proposed solutions ( videoFile.read() as well as sendFile()). In general, the solutions from ChatGPT are not really helpful for this problem, I have tried several of them. For example, the sendFile() option does not exist as part of the <HTTPClient.h> package, so the proposed solutions are not feasible.

So the question is still: how does the arduino code has to look like to add the .avi and .wav file from the sd card to the http post multipart/form-data request to send the files to another server for further processing? If there are any further suggestions for solutions, I would be very pleased to hear about them.

Hi there,
As far as quick suggestions , it works fine. The original code looks exactly like the first post, Also You may want to look at Stack exchange a fellow does have it working there, AN issue with the too many CR, LF b4 the post response , Which you never say if your getting or not?
Also the length parm, where is that in the post, pretty sure that’s needed.
HTH
GL :slight_smile: PJ

I’m skeptical you can read the whole file and send it too, so What are the file sizes? I missed it?

Hey @PJ_Glasso

So you tried it out and it worked for you sending a .avi and .wav file from the sd card via a http post multipart/form-data request to a server and the files were on the server still playable/usable?

Can you share the link? Despite an intensive search, I have not found a solution on stack overflow/stack exchange for sending a .avi and .wav file from the sd card via a http post multipart/form-data request.

As already described, the http post multipart/form-data request generally works with the characters used. I get 200 code for the post, see the structure of the post below.


----------WebKitFormBoundary7bd6fd

Content-Disposition: form-data; name="json"

{"start_date":"2024-03-08 16:03:10.210804","end_date":"2024-03-08 16:04:10.210804","audio":"audioKey","video":"videoKey"}

------WebKitFormBoundary7bd6fd

Content-Disposition: form-data; name="videoKey"; filename="video0.avi"

Content-Type: video/avi

------WebKitFormBoundary7bd6fd

Content-Disposition: form-data; name="audioKey"; filename="arduino_rec.wav"

Content-Type: audio/wav

------WebKitFormBoundary7bd6fd--

However, the corresponding files are missing because I don’t know how to add the .avi and .wav files read from the sd card to the post. Are there any further suggestions for this step?

I tried to include the contentLength of the whole request this way.

At the moment both files together are smaller than 1 megabyte. Potentially, these can also become larger, but this size would be sufficient for the basic functionality.

Hi there,
Well you are on the way to the solution, Some subtleties are all that is lacking.
Here this Post has the correct tech in the last post. If you do it this way it should work, However File sizes will maybe dictate the Buffering and send in multiple "post"s or streaming the file from the SD card with one post and additional support code.
HTH
GL :slight_smile: PJ

Also You never indicate what the server side is?MQTT, Apache2, Node Red? Home brew? :v:

Solution was found in an Arduino forum post.