0

I should first note that I have made some minor changes to the base code that is used by that library, instead using it as a reference. Basically, turning off unwanted actions during the update and progress support. I'm not sure if it's this, some issue with how frequently/infrequently server.handleClient() is called, or my upload page.

The issue I'm having is that when I choose the firmware.bin file and click the button to upload, the ESP8266 runs the setup code for the update and then, the next time a chunk of data is sent, mysteriously restarts instead of accepting further data. The update is NOT completing, and no more than about 3% of the file (according to the JS side at the webpage, 16384 bytes) gets sent. I have the sketch/filesystem division set to 2M/2M (NodeMCU board, and I know for sure it has 4M), and the sketch is not larger than 0.5M.

I can't really seem to find any documentation on how the server.upload works or is structured/used, so I'm not sure what's wrong.

[EDIT: I apparently forgot to mention that I am seeking help with finding out what is causing this reset. (Although I doubt most people would WANT their device to crash-reboot in place of updating! :D )]I have not heard of this exception before, and I don't think I've ever run into this cause before. A quick Google search turned up nothing on this exception, and I don't know how to solve it. Can someone figure out what this issue actually means, so that I can try to figure out why it's happening, or solve this for me (if the issue is determinable from the code I've posted)?

My best guess is that the scheduler I'm using (which is set to run the server.handleClient() function every millisecond (as fast as it can run without needing redesigning or needing to have a lower priority to prevent starving other tasks) is not running the handleClient fast enough for some sort of asynchronous part of the upload code to avoid a buffer overflow, or perhaps that my javascript upload code is wrong somewhere...? One possible note is that the connection stays open even after the file is sent and the ESP8266 has rebooted (when it works, which was intermittent and is "not at all" now). This is the case even with the stock library and its minimal uploader page, so I don't think it's me. I've even tried using the unmodified HttpUpdateServer library (and even using its page with its backend), where it still fails instantly and reboots.

Original server-side code: https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp

HTML/Javascript:

  <h2 class="info">This page allows for updating the device firmware.</h2>
  <h2 class="warn">Only do this for new, valid firmware.</h2>
  <form id="upload_form" class="upload_form" enctype="multipart/form-data" method="post">
  <fieldset>
  <legend>Upload New Firmware</legend>
    <input type="file" id="file1" onchange="uploadable=1">
    <div id="myProgress">
    <div id="myBar"></div>
    </div>
    <button class="uploadFile" onClick="uploadFile()">Are you <i>sure</i>?</button>
    <h3 id="status"></h3>
    <p id="loaded_n_total"></p>
    </fieldset>
  </form>
<script type="text/javascript">
function uploadFile() {
  var fileBox = document.getElementById("file1");
  var file = fileBox.files[0];
    if (fileBox.files.length>0){
    var formdata = new FormData();
    formdata.append("file1", file);
    var ajax = new XMLHttpRequest();
    ajax.upload.addEventListener("progress", progressHandler, false);
    ajax.addEventListener("load", completeHandler, false);
    ajax.addEventListener("error", errorHandler, false);
    ajax.addEventListener("abort", abortHandler, false);
    ajax.open("POST", "/updatefw");
    ajax.send(formdata);
    }
  }
  function progressHandler(event) {
    document.getElementById("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
    var percent = (event.loaded / event.total) * 100;
    //document.getElementById("progressBar").value = Math.round(percent);
    document.getElementById("myBar").style.width=percent+'%';
    document.getElementById("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
  }
  function completeHandler(event) {
    document.getElementById("status").innerHTML = event.target.responseText;
  }
  function errorHandler(event) {
    document.getElementById("status").innerHTML = "Upload Failed";
  }
  function abortHandler(event) {
    document.getElementById("status").innerHTML = "Upload Aborted";
  }
</script>

Server-side ESP8266 code:

(It never gets past if (!Update.begin(contentLength))) In setup code for the server:

server.on("/updatefw", HTTP_POST, finishUploadFW, startUploadFW);
const char *headerkeys[] = {"Content-Length"};
server.collectHeaders(headerkeys, 1);

The actual upload handlers:

void finishUploadFW() {
  menu.log(F("HTTP Update Done"));
  menu.log(F("Rebooting!"));
  server.sendHeader("Connection", "close");
  String msg = F("<META http-equiv=\"refresh\" content=\"15;URL=/\">");
  bool success = Update.hasError();
  msg += (success ? F("FAILED") : F("OK"));
  server.send(200, F("text/plain"), msg);
  if (success) {
    menu.setIndicator(0, RgbwColor(0, 40, 0, 0), true);
  } else {
    menu.setIndicator(0, RgbwColor(40, 0, 0, 0), true);
  }
  delay(100);
  server.close();
  ESP.restart();
}

void startUploadFW() {
  HTTPUpload &upload = server.upload();
  if (upload.status == UPLOAD_FILE_START) {
    int contentLength = server.header(F("Content-Length")).toInt();//I have the listener for this header set up in the init section.
    menu.log(F("Web Update Started"));
    menu.log(String(F("Total Size: ")) + String(contentLength));
    controller.haltForUpdate();
    menu.log(String(F("Update: ")) + upload.filename);
    menu.setIndicator(0, RgbwColor(0, 0, 40, 0), true);
    // uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) &
    // 0xFFFFF000;
    if (!Update.begin(contentLength)) { // start with max available size
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    /* flashing firmware to ESP*/
    int p = 100 - Update.remaining() / (Update.size() / 100);
    menu.drawProgress(p, String(F("Update Progress: ")) + p);
    menu.setProg(1, p);
    if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_END) {
    menu.log(F("Update finalizing"));
    if (Update.end(true)) { // true to set the size to the current
                            // progress
      menu.log(String(F("Success: ")) + upload.totalSize);
    } else {
      Update.printError(Serial);
    }
  }
}

menu.log() prints a string to the OLED screen in scrolling mode. I'm using the Squix Esp8266-oled library. controller.haltForUpdate() turns off the RGB led strand and stops the non-server tasks in the scheduler. The header I am reading has the size of the form that is being uploaded, since the server.upload object has no support for this sort of thing at all (a known issue, and a rather weird one). It returns a value that is a small number of bytes larger than the file, but the Update() library is told to end the update when the file is done being sent. This allows for accurate progress estimation, and that part at least seems to be working properly.

I have found that allowing any of these functions to call yield() seems to cause a crash, but I think I removed all of them?

Stack Trace (pardon the question marks. The decoder doesn't seem to be getting all of the information for some reason):

This is interesting. Normally, it's a write in a protected section or a similar error when this fails (Exception 0, 1, or 28, I think). Here, it's an interrupt error of some kind. I can't figure out what it means though.

Exception: 4 (Level1Interrupt: Level-1 interrupt as indicated by set 
level-1 bits in the INTERRUPT register)

epc1:     0x400005cb
epc2:     0x00000000
epc3:     0x40100ca0: stopWaveform at ??:?
excvaddr: 0x40029801
depc:     0x00000000

ctx: cont

sp:       0x3ffffac0
end:      0x3fffffc0
offset:   0x000001a0

stack:
0x4021e801: twi_write_byte at core_esp8266_si2c.cpp:?
0x40105d3c: spi_flash_read at ??:?
0x40245ac0: pm_set_sleep_type_from_upper at ??:?
0x40241d90: umm_free_heap_size at ??:?
0x4026ace0: umm_block_size at ??:?
0x4020c2d0: xPortGetFreeHeapSize at ??:?
0x4024a7dd: system_get_free_heap_size at ??:?
0x40208a08: EspClass::getFreeHeap() at ??:?
0x402099e8: UpdaterClass::begin(unsigned int, int, int, unsigned char) at ??:?
0x4020a058: String::invalidate() at ??:?
0x402074b1: startUploadFW() at ??:?
0x4021f236: std::_Function_handler<void (), void (*)()>::_M_invoke(std::_Any_data const&) at ??:?
0x401000c5: std::function<void ()>::operator()() const at ??:?
0x4021be04: FunctionRequestHandler::canUpload(String) at ??:?
0x4021cdf1: FunctionRequestHandler::upload(ESP8266WebServer&, String,     HTTPUpload&) at ??:?
0x4020a3d4: String::String(String const&) at ??:?
0x4021daeb: ESP8266WebServer::_parseForm(WiFiClient&, String const&, unsigned int) at ??:?
0x4021d3ed: ESP8266WebServer::_parseArguments(String const&) at ??:?
0x4021d58c: std::_Function_handler<void (String&, String&, String const&, int, int, int, int), storeArgHandler>::_M_invoke(std::_Any_data const&, String&, String&, String const&, int, int, int, int) at ??:?
0x4021cdc0: FunctionRequestHandler::upload(ESP8266WebServer&, String, HTTPUpload&) at ??:?
0x4021be04: FunctionRequestHandler::canUpload(String) at ??:?
0x4021e47f: ESP8266WebServer::_parseRequest(WiFiClient&) at ??:?
0x4010622b: esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::DoNothing,  esp8266::polledTimeout::TimePolicy::TimeUnit<esp8266::polledTimeout::TimePolicy::TimeSourceMillis, 1000ull> >::expiredOneShot() const at ??:?
0x4021bf30: FunctionRequestHandler::canHandle(HTTPMethod, String) at ??:?
0x402147a8: ESP8266WiFiAPClass::softAPgetStationNum() at ??:?
0x4021d03f: ESP8266WebServer::handleClient() at ??:?
0x402165ae: esp8266::MDNSImplementation::MDNSResponder::update() at ??:?
0x401002fa: millis at ??:?
0x4021bdb6: ArduinoOTAClass::handle() at ??:?
0x402088c2: handleServers() at ??:?
0x402011cc: cortos_class::scheduler() at ??:?
0x40201024: loop at ??:?
0x4020b570: loop_wrapper() at core_esp8266_main.cpp:?
0x401008d1: cont_wrapper at ??:?

[EDIT:] I've done more testing. Does anyone know of a bug between the system call that gets the free memory (heap, I think, was the one) and some sort of hardware peripheral? I'm seeing this crash any time a function (and apparently not just the updater! -- my routine print-status function is halting on this!) checks to see the available heap, and the trace is listing a UART or I2S call at the top before the crash. I'm not sure how both are happening at once, but I assume some asynchronous part of the NeoPixelBus library is interfering. I don't know enough about this to report it (especially since I'm not sure it isn't just me yet), but something odd is going on.

This is an excellent example:

Exception: 4 (Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register)

epc1:     0x400005cb
epc2:     0x00000000
epc3:     0x4020d4c9: uart_write at ??:?
excvaddr: 0x40029811
depc:     0x00000000

ctx: cont

sp:       0x3ffffc70
end:      0x3fffffc0
offset:   0x000001a0

stack:
0x401001cc: NeoEsp8266DmaMethodBase<NeoEsp8266DmaSpeed800Kbps>::i2s_slc_isr() at ??:?
0x40208ef8: HardwareSerial::write(unsigned char) at ??:?
0x40206b78: in::draw() at ??:?
0x40205fe0: in::left(unsigned char) at ??:?
0x40241d48: umm_free_heap_size at ??:?
0x4025ad7c: sleep_reset_analog_rtcreg_8266 at ??:?
0x4020c288: xPortGetFreeHeapSize at ??:?
0x4024a795: system_get_free_heap_size at ??:?
0x402089e8: EspClass::getFreeHeap() at ??:?
0x4020a38c: String::String(String const&) at ??:?
0x40206d85: in::draw() at ??:?
0x40208f1c: HardwareSerial::write(unsigned char const*, unsigned int) at ??:?
0x4020162d: menu_class::ledPin(unsigned char, unsigned char) at ??:?
0x40205fe0: in::left(unsigned char) at ??:?
0x40201ff8: menu_class::update() at ??:?
0x40220730: WiFiServer::_s_discard(void*, ClientContext*) at ??:?
0x401002fa: millis at ??:?
0x402020d8: updateMenu() at ??:?
0x402011cc: cortos_class::scheduler() at ??:?
0x40201024: loop at ??:?
0x4020b528: loop_wrapper() at core_esp8266_main.cpp:?
0x401008d1: cont_wrapper at ??:?
  • you did not ask a question – jsotola Jun 1 at 20:33
  • 1
    your first paragraph describes a possible cause of the problem .... there is no reason to read any further .... revert to unmodified code to determine if your changes caused the issue – jsotola Jun 1 at 20:34
  • The issue is that it has worked before. I think some changes I made somewhere else must have caused this, and I'd revert THOSE, but I can't recall what (if actually anything) that I did. Perhaps it really just did break on its own. I have been testing what happens if I return the code to its original state, though, and haven't figured out anything conclusive yet. – RDragonrydr Jun 1 at 22:16
  • I can confirm that this happens with the stock HTTPUpdateServer. Both its page and its backend. – RDragonrydr Jun 1 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.