PlummersSoftwareLLC / NightDriverStrip

NightDriver client for ESP32

Home Page:https://plummerssoftwarellc.github.io/NightDriverStrip/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Large Embedded Webserver Files are not Being Sent

kriztioan opened this issue · comments

Bug report

Problem

What happens: The 'larger' embedded web resource files index.js, favicon.ico, and timezones.json are not sent/received, but index.html is, as is the JSON from the 'simple' web endpoints like effects, etc.

Expect to happen: All files being sent/received.

Steps

  1. Compile the demo target in VSCODE with webserver/wifi defines set to 1 in globals.h.
  2. Upload to ESP32.
  3. On an other device on network use curl to access web-resources.

Example

curl -vvv 192.168.20.47/index.js | gunzip

Notes

My device is an esp32doit-devkit-v1. The serial output does show 'GET for index.js', but subsequently no data is sent/received for the 'larger' files. I attempted some trouble shooting in the ServeEmbeddedFile-method of the CWebServer-class by using the beginChunkedResponse-method, but with no success. Similarly, I modified the code to read the files from an uploaded SPIFFS-image with the data, which had some success with the Chunked method, but the chunk-sizes were forced to only 256 bytes. I tried increasing RESERVE_MEMORY in globals.h with no success. When using a lambda for the AwsResponseFiller parameter in beginResponse that prints a message to the serial shows, again, the expected results for index.html, but not for the 'larger' files.

Result for effects end-point:

curl -vvv 192.168.20.47/effects      ✔  17:33:08 
*   Trying 192.168.20.47:80...
* Connected to 192.168.20.47 (192.168.20.47) port 80 (#0)
> GET /effects HTTP/1.1
> Host: 192.168.20.47
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 166
< Content-Type: application/json
< Server: NightDriverStrip
< Access-Control-Allow-Origin: *
< Connection: close
< Accept-Ranges: none
<
* Closing connection 0
{"currentEffect":0,"millisecondsRemaining":13931,"eternalInterval":false,"effectInterval":30000,"Effects":[{"name":"RainbowFill Rainbow","enabled":true,"core":true}]}%

Result for index.html:

curl -vvv 192.168.20.47/index.html | gunzip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 192.168.20.47:80...
* Connected to 192.168.20.47 (192.168.20.47) port 80 (#0)
> GET /index.html HTTP/1.1
> Host: 192.168.20.47
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 456
< Content-Type: text/html
< Content-Encoding: gzip
< Connection: close
< Accept-Ranges: none
<
{ [456 bytes data]
100   456  100   456    0     0   5514      0 --:--:-- --:--:-- --:--:--  6000
* Closing connection 0
<!doctype html><html lang="en"><head><title>NightDriverStrip</title><script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script><script crossorigin src="https://unpkg.com/@emotion/react@11.11.1/dist/emotion-react.umd.min.js"></script><script crossorigin src="https://unpkg.com/@emotion/styled@11.11.0/dist/emotion-styled.umd.min.js"></script><script crossorigin src="https://unpkg.com/@mui/material@5.14.9/umd/material-ui.development.js"></script><script crossorigin src="https://unpkg.com/html-react-parser@4.2.2/dist/html-react-parser.min.js"></script><script src="https://unpkg.com/prop-types/prop-types.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/recharts/2.3.2/Recharts.min.js" referrerpolicy="no-referrer"></script><link rel="shortcut icon" type="image/x-icon" href="favicon.ico"><link crossorigin rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"><script defer="defer" src="index.js"></script></head><body><div id="root"></div><script src="index.js.gz"></script></body></html>%

Result for index.js after 26 seconds:

curl -vvv 192.168.20.47/index.js | gunzip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 192.168.20.47:80...
* Connected to 192.168.20.47 (192.168.20.47) port 80 (#0)
> GET /index.js HTTP/1.1
> Host: 192.168.20.47
> User-Agent: curl/8.1.2
> Accept: */*
>
  0     0    0     0    0     0      0      0 --:--:--  0:00:26 --:--:--     0

Serial output during above curl calls.

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4
E (607) esp_core_dump_flash: No core du����ѥѥ���found!
E (608) esp_core_dump_flash: No core dump partition found!
Replacing Idle Tasks with TaskManager...
(I) (PrintOutputHeader)(C1) NightDriverStrip
(I) 
(I) (PrintOutputHeader)(C1) ------------------------------------------------------------------------------------------------------------
(I) (PrintOutputHeader)(C1) M5STICKC: 0, USE_M5DISPLAY: 0, USE_OLED: 0, USE_TFTSPI: 0, USE_LCD: 0, USE_AUDIO: 0, ENABLE_REMOTE: 0
(I) (PrintOutputHeader)(C1) Version 40: Wifi SSID: "C&C@centralpark" - ESP32 Free Memory: 215804, PSRAM:0, PSRAM Free: 0
(I) (PrintOutputHeader)(C1) ESP32 Clock Freq : 240 MHz
(I) (setup)(C1) Startup!
(I) (setup)(C1) Starting DebugLoopTaskEntry
>> Launching Debug Thread.  Mem: 215804, LargestBlk: 110580, PSRAM Free: 0/0, >> Launching JSON Writer Thread.  Mem: 207200, LargestBlk: 110580, PSRAM Free: 0/0, (W) (DeviceConfig)(C1) DeviceConfig could not be loaded from JSON, using defaults
(W) (NotifyJSONWriterThread)(C1) >> Notifying JSON Writer Thread
(W) (setup)(C1) Starting ImprovSerial for ESP32
[   831][E][vfs_api.cpp:182] remove(): /improv.log does not exists or is directory
(W) (ReadWiFiConfig)(C1) Retrieved SSID and Password from NVS: "C&C@centralpark", "********"
(W) (InitializeHardware)(C1) Allocating LEDStripGFX for channel 0
(I) (AddLEDsToFastLED)(C1) Adding LEDs to FastLED...
(I) (AddLEDsToFastLED)(C1) Adding 144 LEDs to pin 5 from channel 0 on FastLED.
(W) (SetupBufferManagers)(C1) Reserving 110 LED buffers for a total of 51040 bytes...
(W) (InitEffectsManager)(C1) InitEffectsManager...
(I) (LoadJSONFile)(C1) Attempting to read JSON file /effects.cfg
(I) (InitEffectsManager)(C1) Creating EffectManager from JSON config
(I) (ReadCurrentEffectIndex)(C1) Attempting to read file /current.cfg
>> Launching Drawing Thread.  Mem: 142968, LargestBlk: 102388, PSRAM Free: 0/0, (W) (DrawLoopTaskEntry)(C1) >> DrawLoopTaskEntry
(W) 
(W) (DrawLoopTaskEntry)(C1) Entering main draw loop!
>> Launching Network Thread.  Mem: 138296, LargestBlk: 98292, PSRAM Free: 0/0, [  1169][E][ESPmDNS.cpp:65] begin(): Failed starting MDNS
Error starting mDNS
>> Launching ColorData Thread.  Mem: 129116, LargestBlk: 86004, PSRAM Free: 0/0, (W) (NotifyJSONWriterThread)(C1) >> Notifying JSON Writer Thread
(I) (ConnectToWiFi)(C1) Setting host name to NightDriver...WL_NO_SHIELD
(W) (ConnectToWiFi)(C1) Pass 1 of 5: Connecting to Wifi SSID: "********" - ESP32 Free Memory: 80436, PSRAM:0, PSRAM Free: 0
(W) 
(I) (SaveToJSONFile)(C0) Number of bytes written to JSON file /effects.cfg: 124
(W) (ColorDataTaskEntry)(C1) Started color data server!
(I) (loop)(C1) WiFi: WL_CONNECTED, IP: 192.168.20.47, Mem: 75828, LargestBlk: 38900, PSRAM Free: 0/0, LED FPS: 30 LED Bright:  22%, LED Watts: 11, CPU: 003%, 001%, FreeDraw: 0.019
(W) (ConnectToWiFi)(C1) Connected to AP with BSSID: A0:04:60:31:7D:E2
(W) 
(W) (ConnectToWiFi)(C1) Received IP: 192.168.20.47
(I) (ConnectToWiFi)(C1) Setting Clock...
[  6717][E][WiFiUdp.cpp:221] parsePacket(): could not receive data: 9
(W) (UpdateClockFromWeb)(C1) NTP clock: Raw values sec=1995184889, usec=464540
(I) (UpdateClockFromWeb)(C1) Old Time: 6.924527, New Time: 1698108210.464540, Delta: 1698108203.540013
(I) (UpdateClockFromWeb)(C1) Adjusting time by 1698108203.540013 to 1698108210.464540
(I) (UpdateClockFromWeb)(C1) NTP clock: response received, updated time to: 1698108210.464540, DELTA: 1698108203.540013
(I) 
(I) (ConnectToWiFi)(C1) Starting Web Server...
(I) (begin)(C1) Connecting Web Endpoints
(I) (begin)(C1) Embedded html file size: 456
(I) (begin)(C1) Embedded jsx file size: 9645
(I) (begin)(C1) Embedded ico file size: 6434
(I) (begin)(C1) Embedded timezones file size: 17834
(I) (begin)(C1) HTTP server started
(I) (ConnectToWiFi)(C1) Web Server begin called!
GET for: /index.html
(I) (loop)(C1) WiFi: WL_CONNECTED, IP: 192.168.20.47, Mem: 50312, LargestBlk: 38900, PSRAM Free: 0/0, LED FPS: 30 LED Bright:  22%, LED Watts: 11, CPU: 004%, 001%, FreeDraw: 0.019
GET for: /index.js

NB The current html-file asks for index.js.gz, should this not be index.js?

NB2 My understanding is that the ESP webserver can only handle a single request at a time, however, modern browsers will do simultaneous request---can this cause an issue?

@kriztioan I think this issue's main point addresses something that isn't a thing, but your first NB does.

Bullet-point:

  • favicon.ico is retrieved by actual browsers, to get the icon to put in the page's tab. You can refer to https://en.wikipedia.org/wiki/Favicon for more information about how this works.
  • timezones.js is offered for retrieval in case a future iteration of the web UI wants to offer a drop down (or a similar control) to select the time zone for the respective device setting. The web UI doesn't use it at the moment, but as it is used within NightDriverStrip anyway, serving it via the webserver is basically free.
  • index.js is retrieved, but initially indeed using the wrong filename (index.js.gz instead of the index.js that the webserver uses to serve it, as per line 157 in webserver.cpp). @KeiranHines Maybe this is something you can take a look at, when you can find the time? I note that my browser does end up fetching the file using the correct URI after all, which it has to as the UI simply wouldn't work otherwise.

Concerning your second NB: I've been running the web UI on multiple types of devices for quite a while now, and never experienced this to be a problem.

I will try to get time on the weekend to look at it.

As eluded to, I am able to get index.js somewhat reliably served using beginChunkedResponse and setting max_bytes as low as 32 bytes(!) using a modified version of ServeEmbeddedFile (see below). However, it takes a while to render the website (Vivaldi on MacOS) and the bell-icon lists an increasing number of errors.

    void ServeEmbeddedFile(const char strUri[], EmbeddedWebFile &file) 
    {
        _server.on(strUri, HTTP_GET, [strUri, file](AsyncWebServerRequest *request) {
            Serial.printf("GET for: %s\n", strUri); 
            AsyncWebServerResponse *response = request->beginChunkedResponse(file.type,
            [file](uint8_t *buf, size_t max_bytes, size_t sent_bytes) -> size_t {
                    max_bytes = 32;
                    uint32_t send_bytes = file.length - sent_bytes;
                    if(send_bytes == 0) return 0;
                    if(send_bytes >= max_bytes) send_bytes = max_bytes;
                    memcpy_P(buf, file.contents + sent_bytes, send_bytes);
                    return send_bytes;
                });
            response->addHeader("Content-Encoding", file.encoding);
            request->send(response);
        });
    }

As for the NB's, yes I had to update index.js.gz to index.js in index.html-perhaps browser caching is preventing you from having any issue? In addition, using wireshark to follow the network traffic shows as lot of TCP_ACK_RETRYs, leading me to postulate that the ESP32 is struggling with handling the multitude of connections. Another thing I'm wondering is whether it could be my specific ESP32 version: DEVKIT V1 VROOM 32?

@rbergen Bedankt voor alle hulp!

For me the problem here is that I just can't relate any of the issues that @kriztioan describes to what I'm seeing, except for NB1. I've used Chrome (and Edge, but that's Chrome at its core, of course) on a number of computers (all Windows 11) to access the web UI on a collection of different (types of) ESP32 devices, and the current webserver code in this repo's main branch just works.

Caching is not a factor either. I often test the web UI with the Chrome dev tools running, and cache is disabled every time I do. I can literally see the browser fetch index.js after the GET for index.js.gz fails.

This being the case, I can't do anything with or about this issue other than addressing the NB1 thing, which @KeiranHines already indicated he'll look into.

Bedankt voor alle hulp!

Graag gedaan! :)

@robertlipe index.html does successfully load, it is the 'larger' index.js that is not being served. It is difficult to say whether my network configuration is 'special'; I do have dd-wrt flashed to my router, but am not doing anything 'weird' with it and I haven't had issues with it before. I tried the Web Installer with Edge under Windows 11 with selecting the ESP32 device and Demo project. The project is successfully flashed, but no option to 'CONNECT TO WI-FI' button appears, even after 'LOGS & CONSOLE' shows output. Doing a bisection on the codebase seems a bit excessive at this point as using a chunked response works adequately for me.

@rbergen Since my issue appears not to be reproducible and my modified ServeEmbeddedFile-method works sufficiently for my needs, I'm closing the issue. Thank you for your consideration and engagement.

@kriztioan As described in a comment on discussion #498, I have now also run into this. It seems to happen when there is just enough RAM available to start the web server, but not enough to serve the bigger files. I have prepared an update to the build configuration in question (Spectrum for the M5StickC Plus) to fix it there; your low-byte-count chunked response may be another way to (just) dodge the issue on whatever device/configuration combination you are/were building for.

@rbergen I was able to track the issue down to a lack of RAM as well. Following the entire stack down through AnyTCP and lwip shows an err_t of ERR_MEM being return by a call to_tcp_write_api, which is not being explicitly handled by the ESP Async WebServer and no such errors are propagated further up the calling chain.

Some research into the different flavors of ESP32s revealed my ESP32-WROOM-32 is not equipped with PSRAM. More consciousnessly reading through the different documentation/issues/discussions lead me to a statement by @davepl that having PSRAM is a must. To that end, I went and acquired myself three ESP32-S3-DevKitC-1-N16R8s. After adding its board configuration file to PlatformIO, using default_16MB.csv for the partition table, and configuring a specific Device section for it it in plattformio.ini has everything purring like a kitten.

Admittedly, my chunked approach did often require a number of page reloads for it to work and should likely not be considered for 'production'. My hypothesis there is that while the chunks themselves require a smaller memory footprint the number of pending requests balloons, reducing the available RAM there. One possible strategy is to require certain 'specs' to have certain features be enabled.