1

I am trying to replace a Unity app with a React Native app, that communicates with a remote with an ESP32 board inside it. The app needs to send a binary file to a Web Server created by the ESP32 and successfully update its firmware. The binary file is 1.73MB.

This is the current Unity app code that works properly:
Here is the Unity plugin used to create the HttpClient: https://assetstore.unity.com/packages/tools/network/http-client-79343

void UploadRemote()
    {
        byte[] remote = File.ReadAllBytes(remoteFirmwarePath);
        remoteUploadSize = remote.Length;

        if (firmwareUploadClient != null)
        {
            firmwareUploadClient.Abort();
            firmwareUploadClient = null;
        }
        firmwareUploadClient = new HttpClient();
        ByteArrayContent remoteContent = new ByteArrayContent(remote, "multipart/form-data");
        MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent();
        multipartFormDataContent.Add(remoteContent, remoteFileName, remoteFileName);
        firmwareUploadClient.Cache = null;
        firmwareUploadClient.Cookies = null;
        firmwareUploadClient.Post(new Uri("http://192.168.1.3/update"), multipartFormDataContent, HttpCompletionOption.AllResponseContent, CallBackRemote);
    }

And here is the code that is running on the ESP32:

It uses the standard WebServer and Update libraries from the arduino-esp32 repo, provided by espressif:
The Webserver code is pretty much directly lifted from the example with a few minor changes to send UDP signals:
https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer/examples/WebUpdate/WebUpdate.ino

server.on("/update", HTTP_POST, []()
  {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");

    if (SelfOTA)
    {
      sendUDP("OTA_Done,");
      delay(1000);
      ESP.restart();
    }

  },
  []()
  {
    //udp.stop();
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      showUpdating = true;
      Keep_Screen_On = true;
      webServerStartFlag = true;
      ////serial.println("web updated started now");
      ////serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        //Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      ////serial.println(String(upload.totalSize));
      ////serial.println(xPortGetCoreID());
      sendUDP("R" + String(upload.totalSize));
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        //Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        ////serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
        webServerStartFlag = false;
        BluetoothFlag = false;
      } else {
        //Update.printError(Serial);
        webServerStartFlag = false;
      }
    }
  });

After much research this is the code that I am running on the React Native app. For testing purposes, I have put the Binary file into the assets folder in the Android bundle and am copying it over to the Documents directory before sending it over POST:

const expoAssetsUri = FileSystem.bundleDirectory;
const expoDocumentUri = FileSystem.documentDirectory;
const initialUpdateFilePath =
  expoAssetsUri + "updates/Firmware_Beta.ino.pico32.bin";
const finalUpdateFilePath =
  expoDocumentUri + "Firmware_Beta.ino.pico32.bin";

try {
  await FileSystem.copyAsync({
    from: initialUpdateFilePath,
    to: finalUpdateFilePath,
  });

  setIsSendingData(true);

  const formData = new FormData();
  formData.append("Firmware_Beta.ino.pico32.bin", {
    uri: finalUpdateFilePath,
    name: "Firmware_Beta.ino.pico32.bin",
    type: "application/octet-stream",
  } as any);

  const response = await axios.post(ESP32_UPDATE_URL, {
    headers: {
      "Content-Type": "multipart/form-data",
    },
    body: formData,
  });

The issue is that I get the 200,OK response from the server but the firmware doesn't update, as the callback function handling the file upload on the ESP32 side is never called. (I do not get the corresponding upload.status UDP signals, only the one after hitting the endpoint ["OTA_Done,"], before the ESP32 restarts)

I'm not really sure how I can correctly send over the file. Do I need to manually chunk it? Convert the binary into a buffer array? From what I've read the FormData object and the POST method with Content-Type: multipart/form-data should do this automatically.

Does anyone have any experience with this? Any help would be appreciated.

4
  • 1
    To debug this you need to figure out what the ESP32 is seeing and what it's actually doing. There are debugging messages commented out in the ESP32 code you posted. Have you added them back in as well as adding any new ones you need to figure out what the ESP32 is seeing? Commented Dec 30, 2024 at 15:51
  • Hi romkey, the issue is that particular callback function is not being triggered, so I cannot see any debug logs. It has something to do with the way I'm sending the binary file. It must not be correct hence the callback not being triggered. Commented Dec 31, 2024 at 7:56
  • Method on() attaches two callbacks (in the form of lambdas) to the resource "/update". It's my understanding that first callback serves GET and the other POST/PUT methods. Maybe it's getting a GET method, invoking first callback and effectively throwing the request away. Add a short debug message to first callback to verify. Commented Dec 31, 2024 at 15:15
  • 1
    Note that the entire request handling method is quite complicated and can fail in many ways. It also logs what it's doing through log_x() functions - see if you can enable the logging in this library and understand what exactly is choking it up. Commented Dec 31, 2024 at 15:19

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.