Why can I do not perform an OTA Update when I am in the AP mode it?

I am currently searching for a way, to perform an OTA update with my ESP32. I can do that when I connect to a local WiFi. But the project does not always have an existing WiFi network so I would like to have the option to create an WiFi network where I can connect too and upload the new Firmware.

The router mode works with out problem. But when I try to use the Acesspoint mode I only get the webside but as soon i try to upload it does not work and only redirection me to http://ipaddress/#
With the text: Not found: /
Did i miss some configuration on the server or does it have trouble with the filesystem?
Any help is appreciated

This is my code which I use:
main.cpp

#include "Utils/OTAWeb.h"

// For connection with router
// OTAWeb otaweb("SSD","Password",false);
OTAWeb otaweb("CreatedWIFI", "1234567890", true);
void setup()
{
  otaweb.setup();
}

void loop()
{
  otaweb.loop();
}

OTAWeb.h:

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <ArduinoOTA.h>
#pragma once
class OTAWeb
{
public:
    // Create network does not work at the moment
    OTAWeb(const char *ssid, const char *password, bool createNetwork);
    ~OTAWeb();
    void start();
    void stop();
    void setup();
    void loop();

private:
    const char *host = "esp32";
    const char *ssid;
    const char *password;
    WebServer *webserver;
    WiFiServer *wifiserver;
    bool createNetwork;
    // Stores request of WiFi Server
    String header;
};

OTAWeb.cpp:

#include "OTAWeb.h"
const char *webserverIndex =
    "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
    "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
    "<input type='file' name='update'>"
    "<input type='submit' value='Update'>"
    "</form>"
    "<div id='prg'>Fortschritt: 0%</div>"
    "<script>"
    "$('form').submit(function(e){"
    "e.preventDefault();"
    "var form = $('#upload_form')[0];"
    "var data = new FormData(form);"
    " $.ajax({"
    "url: '/update',"
    "type: 'POST',"
    "data: data,"
    "contentType: false,"
    "processData:false,"
    "xhr: function() {"
    "var xhr = new window.XMLHttpRequest();"
    "xhr.upload.addEventListener('progress', function(evt) {"
    "if (evt.lengthComputable) {"
    "var per = evt.loaded / evt.total;"
    "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
    "}"
    "}, false);"
    "return xhr;"
    "},"
    "success:function(d, s) {"
    "console.log('success!')"
    "},"
    "error: function (a, b, c) {"
    "}"
    "});"
    "});"
    "</script>";

OTAWeb::OTAWeb(const char *ssid, const char *password, bool createNetwork)
{
    this->ssid = ssid;
    this->password = password;
    this->createNetwork = createNetwork;
    this->webserver = new WebServer(80);
}
OTAWeb::~OTAWeb()
{

    delete webserver;
}

void OTAWeb::stop()
{

    webserver->stop();
}

void OTAWeb::start()
{
    setup();
}

void OTAWeb::setup()
{
    Serial.begin(115200);
    if (createNetwork)
    {
        Serial.println("Booting");
        WiFi.mode(WIFI_AP);
        // IPAddress apIP(192, 168, 4, 1);
        // WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
        WiFi.softAP(ssid, password);

        
        Serial.print("IP address: ");
        Serial.println(WiFi.softAPIP());



    }
    else
    {

        // Connect to WiFi network
        WiFi.begin(ssid, password);
        Serial.println("");
        // Wait for connection
        while (WiFi.status() != WL_CONNECTED)
        {
            delay(500);
            Serial.print(".");
        }
        Serial.println("");
        Serial.print("Connected to ");
        Serial.println(ssid);
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());
    }
        /*use mdns for host name resolution*/
        if (!MDNS.begin(host))
        { //http://esp32.local
            Serial.println("Error setting up MDNS responder!");
            while (1)
            {
                delay(1000);
            }
        }
        Serial.println("mDNS responder started");
    
        /*return index page which is stored in webserverIndex */
        webserver->on("/", HTTP_GET, [this]()
                      {
                          webserver->sendHeader("Connection", "close");
                          webserver->send(200, "text/html", webserverIndex);
                      });
        /*handling uploading firmware file */
        webserver->on(
            "/update", HTTP_POST, [this]()
            {
                webserver->sendHeader("Connection", "close");
                webserver->send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
                ESP.restart();
            },
            [this]()
            {
                HTTPUpload &upload = webserver->upload();
                if (upload.status == UPLOAD_FILE_START)
                {
                    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*/
                    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);
                    }
                    else
                    {
                        Update.printError(Serial);
                    }
                }
            });
        webserver->begin();
    
}

void OTAWeb::loop()
{

    webserver->handleClient();
    delay(1);
}

Because in your webpage you are using JQuery from a remote CDN.
With a similar requirement you can avoid at all, it's really not needed!

Off course you have to rewrite webpage source without the use of jquery syntax and functions (aka Vanilla-JS).

Oh thank you, I will try that. I was hoping that the browser caching would work for that so that i just can get that running before modify the whole class even more. (Just used the websitecode from the example)

I am still trying to get it running, and I am just not sure can the esp32 server run in trouble with specific Javascript code? (like encoding error? Since the rendering happens on the client and the server only sends text files with specific file ending, I am not sure what did got wrong here)
Thats what I am currently trying: What did I wrong be converting that jquery code to plain javascript (Upload binary file to embedded server) - Stack Overflow

You can send whatever you want from webserver to browser, even zipped if you like (in order to reduce size of js, css files).

It must be said that flashing the firmware at every slightest change is really stressful.

You should try to serve html pages directly from flash memory.
There should be an example included in the WebServer library.

P.S.
If you want to see complete examples (firmware / webserver) you can also try to take a look at this library of mine (it's a wrapper around core library WebServer in order to make easy work with files on flash). All examples use only "plain" Javascript.

There is also one example for OTA from remote host (Guthub), but you can use as inspiration in order to work from local because all you need to perform the firmware update, is the address of the raw binary file which can also reside on your LAN.

I think it's simpler than the way you are proceeding (uploading before and then flash)

Note that for ESP32 in the examples, the default SPIFFS file system is not used, but FFat (much better performing) so you have to select the right partition scheme.

Do you mean with flash memory that I should use the PROGMEM keyword or use a filesystem on the flash? But can that really change the outcome, since it is just a diffrend storage methode the code would still be the same.

Yes, both! Even at the same time.

In my library for example, there are 2 "system" zipped (gzip format) webpage embedded directly in the firmware using

  • this array const char edit_htm_gz[] PROGMEM = {..} for the ACE web editor
  • this array const char WEBPAGE_HTML[] PROGMEM = {..} for the WiFi manager

In addition, any resources such as HTML, text files, CSS, Javascript, images etc etc that you upload to the filesystem will be "served" to the clients like browser when requested.

I've added a sample code for anyone trying to make it work, and get the serverIndex or similar errors caused by the jquery.min.js. -> GitHub - italocjs/ESP32_OTA_APMODE: This is a sample code for using OTA on Acess point

Thank you for sharing your code I will try that too :smiley:

You can also use ESP2SOTA library included in Arduino Libraries

It has an AP_Mode sample ready to work!!

Dear Alberto,
your solution with the ESP2SOTA library would be great if it worked, but I can't do it in any way. I deactivated the Firewall, I identified the ESP32 network as Private, I connected to the ESP32 as an AP, but it does not let me reach the site 10.10.10.1 and there is no way to see the ESP32-AP port for upload to Arduino. Among other things, the OTA upload does not even work with the ESP32 configured as a Client. I really need it instead, hope you can help me. Giancarlo

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.