-->
Page 1 of 2

Web Server - Can't connect more than five times

PostPosted: Mon Nov 01, 2021 9:12 am
by Inq720
I'm working on a native SDK web server (no lwIP) and have it working except... after the 5th connection, it will accept no more. I searched through the documentation, the available source code and examples. This sounds like I'm running into the espconn_tcp_get_max_con(). I am...

  1. receiving the request
  2. responding with a simple 404
  3. see the browser display the 404
  4. waiting for the espconn_sent_callback()
  5. doing a espconn_disconnect() outside of the callback,
  6. doing a espconn_delete()
This all works fine the first 5 requests... but none after. Doing an lwIP version web server, there are methods to tell the low level that to hold/send more connections... tcp_backlog_delayed() / tcp_backlog_accepted(). There doesn't seem to be an equivalent in the native methods. Here is a rudimentary, single request at a time, test web server that illustrates the problem. It's written for the Arduino IDE, but is easily ported the other way if needed.

Code: Select all#include <user_interface.h>
#include <espconn.h>

#define SSID "Your SSID"
#define PW "Your Password"
#define HOST "wp"

void setup()
{
   Serial.begin(115200);
   while(!Serial) { Serial.print("."); delay(100); }
   delay(2000);
   
    // This is just cook-book Station connection stuff.
    setupWiFi();
   
    // Setup and start web server listener
    static espconn listen;
    os_memset(&listen, 0, sizeof(espconn));
    listen.type = ESPCONN_TCP;
    listen.state = ESPCONN_NONE;   
    listen.proto.tcp = new esp_tcp();   
    listen.proto.tcp->local_port = 80;
   
    espconn_regist_connectcb(&listen, onConnection); 
    // This appears to be an error handler.  Haven't seen it hit yet.
    espconn_regist_reconcb(&listen, onError);
       
    printIfErr(espconn_accept(&listen), "listen");
   
   Serial.println("Ready");
}

espconn* dis = NULL;
u32 last;

void loop()
{
    // The only thing we do in the loop is do the final disconnect since
    // we can't do it in the onSent() callback.
    if (dis)
    {
        u32 now = micros();
        Serial.printf("disconnect = %u us\n", now - last);
        last = now;
       
        s8 rtn;
        if ((rtn = espconn_disconnect(dis)))
        {
            Serial.printf("ERR - disconnect = %d\n", rtn);
            if ((rtn = espconn_abort(dis)))
                Serial.printf("ERR - abort = %d\n", rtn);
        }
        if ((rtn = espconn_delete(dis)))
            Serial.printf("ERR - delete = %d\n", rtn);
        dis = NULL;
    }
}

void onConnection(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
       
    // Incoming connection
    last = micros();
    Serial.printf("\nonConnection\n");   
    espconn_regist_recvcb(conn, onReceive);
    espconn_regist_sentcb(conn, onSent);
    espconn_regist_disconcb(conn, onDisconnect);   

    espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
    // We'll use our own buffer instead of their 2920 buffer
    espconn_clear_opt(conn, ESPCONN_COPY);
}

void onError(void* conn, s8 err)
{
    Serial.printf("ERR=%d\n", err);
}

void onReceive(void* c, char* buf, u16 length)
{
    espconn* conn = reinterpret_cast<espconn*>(c);

    u32 now = micros();
    Serial.printf("onReceive = %u us\n", now - last);
    last = now;
   
    static char buffer[536];

    // Print out the request just to make sure its what we think it is.
    memcpy(buffer, buf, length);
    *(buffer + length) = 0;
    // Serial.print(buffer);
   
    // We don't care what's in there since we'll send back a 404 anyway.
    sprintf(buffer, "HTTP/1.1 404 Not Found\r\nServer: InqPortal/5.0\r\n",
        "Content-type: text/html\r\nPragma: no-cache\r\n\r\n");

    printIfErr(espconn_send(conn, (u8*)buffer, strlen(buffer)), "send");
   
    now = micros();
    Serial.printf("sent = %u us\n", now - last);
    last = now;
}

void onSent(void* c)
{
    u32 now = micros();
    Serial.printf("onSent = %u us\n", now - last);
    last = now;
    // Manual says we can't call disconnect in callback.  We'll use
    // cheap way of just letting the loop() do it.
    dis = reinterpret_cast<espconn*>(c);
;
}

void onDisconnect(void* c)
{
    Serial.printf("onDisconnect \n");
}

void printIfErr(s8 esp, const char* msg)
{
    if (esp)
        Serial.printf("ERR - %s = %d\n", msg, esp);
}

void setupWiFi()
{
    Serial.print("Connecting to your router");

    wifi_set_opmode(STATION_MODE);
   
    station_config sc;
    os_memset(&sc, 0, sizeof(station_config));
    sc.bssid_set = 0;
    os_memcpy(&sc.ssid, SSID, 32);
    os_memcpy(&sc.password, PW, 64);   
    wifi_station_set_config_current(&sc);

    wifi_station_set_hostname(HOST);

    wifi_station_connect();
    while (wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        Serial.print(".");
        delay(1000);
    }
    ip_info   info;
    wifi_get_ip_info(STATION_IF, &info);
    Serial.printf("\nBrowse to (http://%s/index.html) or (http://" IPSTR
        "/index.html)\n",
        HOST, IP2STR(&(info.ip)));
}

More information...

PostPosted: Thu Nov 04, 2021 9:24 am
by Inq720
I've made some modifications to the above test web server to serve out a web page with JavaScript that will cause a reload every 0.5 seconds. I've also tried it with older versions of the SDK.

  1. I notice that the espconn_regist_disconcb() callback NEVER gets called.
  2. I tried older development versions of the SDK
    1. Espressif SDK version 1.5.3(aec24ac9) Worked! - It continually re-serves the same page well into the thousands without issue and without memory leaks. This was downloaded in Arduino ESP8266 library 2.3.0.
    2. Subsequent versions do not work.
    3. Espressif SDK version 2.2.2-dev(38a443e) does not work after serving the file five times. - This was downloaded in the latest Arduino ESP8266 library 3.0.2.

Code: Select allextern "C"
{
  #include "user_interface.h"
  #include "espconn.h"
}

#define SSID "<Your SSID>"
#define PW "<Your Password>"
#define HOST "ws"

const char* PAGE = "<!DOCTYPE html><html><head><title>Native Server Test</title> <meta name='viewport' content='width=device-width, initial-scale=1'><script>window.addEventListener('load',()=>{ setTimeout(()=>{location.reload();},500);},false);</script></head><body>  <h1>Welcome to my Native Server Test</h1></body></html>";

void setup()
{
   Serial.begin(115200);
   while(!Serial) { Serial.print("."); delay(100); }
   delay(2000);
   
    // This is just cook-book Station connection stuff.
    Serial.printf("\n\nSDK version: %s\n", system_get_sdk_version());

    setupWiFi();
   
    // Setup and start web server listener
    static espconn listen;
    os_memset(&listen, 0, sizeof(espconn));
    listen.type = ESPCONN_TCP;
    listen.state = ESPCONN_NONE;   
    listen.proto.tcp = new esp_tcp();   
    listen.proto.tcp->local_port = 80;
   
    espconn_regist_connectcb(&listen, onConnection); 
    // This appears to be an error handler.  Haven't seen it hit yet.
    espconn_regist_reconcb(&listen, onError);
       
    printIfErr(espconn_accept(&listen), "listen");
   
   Serial.println("Ready");
}

espconn* dis = NULL;
u32 last;
u32 cnt = 0;

void loop()
{
    // The only thing we do in the loop is do the final disconnect since
    // we can't do it in the onSent() callback.
    if (dis)
    {
        u32 now = micros();
        Serial.printf("dis=%uus ", now - last);
        last = now;
       
        s8 rtn;
        if ((rtn = espconn_disconnect(dis)))
        {
            Serial.printf("ERR - disconnect = %d\n", rtn);
            if ((rtn = espconn_abort(dis)))
                Serial.printf("ERR - abort = %d\n", rtn);
        }
        if ((rtn = espconn_delete(dis)))
            Serial.printf("del=%d ", rtn);
        dis = NULL;
    }
}

void onConnection(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
       
    // Incoming connection
    last = micros();
    Serial.printf("\nconn(%u), ", cnt++);   
    espconn_regist_recvcb(conn, onReceive);
    espconn_regist_sentcb(conn, onSent);
    espconn_regist_disconcb(conn, onDisconnect);   

    espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
    // We'll use our own buffer instead of their 2920 buffer
    //espconn_clear_opt(conn, ESPCONN_COPY | ESPCONN_KEEPALIVE);
}

void onError(void* conn, s8 err)
{
    Serial.printf("ERR=%d\n", err);
}

void onReceive(void* c, char* buf, u16 length)
{
    espconn* conn = reinterpret_cast<espconn*>(c);

    u32 now = micros();
    Serial.printf("rcv=%uus ", now - last);
    last = now;
   
    static char buffer[1024];

    // Print out the request just to make sure its what we think it is.
    memcpy(buffer, buf, length);
    *(buffer + length) = 0;
    // Serial.print(buffer);
   
    // We'll send back a page that keeps reloading,
    // so we can check for errors and/or memory leaks.
    sprintf(buffer, "HTTP/1.1 200 OK\r\n"
        "Server: InqPortal/5.0\r\n"
        "Content-Length: %d\r\n"
        "Content-type: text/html\r\n"
        "Pragma: no-cache\r\n\r\n%s", strlen(PAGE), PAGE);
    printIfErr(espconn_sent(conn, (u8*)buffer, strlen(buffer)), "send");
   
    now = micros();
    Serial.printf("snd=%uus ", now - last);
    last = now;
}

void onSent(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
   
    u32 now = micros();
    Serial.printf("snt=%uus ", now - last);
    last = now;
    // Manual says we can't call disconnect in callback.  We'll use
    // cheap way of just letting the loop() do it.
    dis = conn;
}

void onDisconnect(void* c)
{
    u32 now = micros();
    Serial.printf("dis(%p)=%uus mem=%u",
        c, now - last, system_get_free_heap_size());
    last = now;   
}

void printIfErr(s8 esp, const char* msg)
{
    if (esp)
        Serial.printf("ERR - %s = %d\n", msg, esp);
}

void setupWiFi()
{
    Serial.print("\nConnecting to your router");

    wifi_set_opmode(STATION_MODE);
   
    station_config sc;
    os_memset(&sc, 0, sizeof(station_config));
    sc.bssid_set = 0;
    os_memcpy(&sc.ssid, SSID, 32);
    os_memcpy(&sc.password, PW, 64);   
    wifi_station_set_config_current(&sc);

    wifi_station_set_hostname(HOST);

    wifi_station_connect();
    while (wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        Serial.print(".");
        delay(1000);
    }
    ip_info   info;
    wifi_get_ip_info(STATION_IF, &info);
    Serial.printf("\nBrowse to (http://%s/index.html) or (http://" IPSTR
        "/index.html)\n",
        HOST, IP2STR(&(info.ip)));
}

Re: Web Server - Can't connect more than five times (SOLVED)

PostPosted: Sat Nov 06, 2021 2:37 pm
by Inq720
Well... kind of solved.

I was wondering why no one had any suggestions. Especially since I broached this problem two years ago in https://bbs.espressif.com/viewtopic.php?f=7&t=51435. I felt certain that if the espconn_regist_disconcb() callback wasn't getting fired there would be a lot more people besides me complaining. This was my first clue... that it had something to do with the Arduino IDE libraries.

Well... I took the last couple of days to research the Internet about doing native development on Windows. The last time I looked one had to use a Linux VM and there was just too much learning curve and too many ways for it to go sideways on me. This time I found PlatformIO and having used Visual Studio for twenty-five+ years, I felt at home and the tutorials got me up and running promptly. I converted the code above to a Native C version and it worked first time!!!

So there is something amiss when trying to do a Native SDK under the Arduino IDE.

Maybe - not a solution

PostPosted: Mon Nov 22, 2021 11:35 am
by Inq720
It turns out that VSCode/PlatformIO uses NonOS SDK 2.1.0!

But this is the code that runs under framework = esp8266-nonos-sdk
I haven't figured out how to upgrade the SDK to 3.0.5. Any help on that would be greatly appreciated.

Code: Select all//  Code    Global  RunHeap
//  279649  29984   46040   Using Arduino IDE
//  226545  28981   49336   Using C PlatformIO

#include "user_interface.h"
#include "espconn.h"
#include "osapi.h"
#include "driver/uart.h"
#include "mem.h"

#define SSID "<your-SSID>"
#define PW "<your-Password>"
#define HOST "ws"
#define dbg(f, ...) os_printf(f, ## __VA_ARGS__)

static os_timer_t ptimer;
struct espconn* conn;
u32 last;
u32 cnt = 0;

// Reloads...
//const char* PAGE = "<!DOCTYPE html><html><head><title>Native Server Test</title> <meta name='viewport' content='width=device-width, initial-scale=1'><script>window.addEventListener('load',()=>{ setTimeout(()=>{location.reload();},500);},false);</script></head><body>  <h1>Welcome to my Native Server Test</h1></body></html>";
// One time!
const char* PAGE = "<!DOCTYPE html><html><head><title>Native Server Test</title> <meta name='viewport' content='width=device-width, initial-scale=1'></head><body>  <h1>Welcome to my Native Server Test</h1></body></html>";

void printIfErr(s8 esp, const char* msg)
{
    if (esp)
        os_printf("ERR - %s = %d\n", msg, esp);
}

void chkAP(void* arg)
{
    if (wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        os_printf(".");
        return;
    }
    struct ip_info info;
    wifi_get_ip_info(STATION_IF, &info);
    // This is just cook-book Station connection stuff.
    dbg("\n\nSDK version: %s\nboot version:  %u\nboot mode:  %u\n",
        system_get_sdk_version(), system_get_boot_version(),
        system_get_boot_mode());

    os_printf("\nBrowse to (http://%s/index.html) or (http://" IPSTR
        "/index.html)\n",
        HOST, IP2STR(&(info.ip)));
    os_timer_disarm(&ptimer);
}

void startHost()
{   
    // Start up the communications Host/Station and Client/Soft
    // ssid - Not NULL terminated in code - Can be 32 characters coming in! 
    // Password length must be blank OR >= 8!  Truncated if >= 64 characters.

    // Address
    wifi_softap_dhcps_stop();

    struct ip_info info;
    os_memset(&info, 0, sizeof(struct ip_info));
    // Don't set Gateway since we can't offer Internet / DNS
    // We're hardcoding our InqPortal server to always be 10.10.10.10.
    IP4_ADDR(&info.ip, 10, 10, 10, 10);   
    IP4_ADDR(&info.gw, 10, 10, 10, 10);   
    IP4_ADDR(&info.netmask, 255, 255, 255, 0);
   
    wifi_set_ip_info(SOFTAP_IF, &info);
   
    // Start up the DHCP server.
    wifi_softap_dhcps_start();
   
    struct softap_config cfg;
    os_memset((u8*)&cfg, 0, sizeof(struct softap_config));
   
    // Internal code does not use null termination, but
    // _HostSSID can be full 32 characters.  Use this filling.
    os_sprintf(cfg.ssid, "WSwswsWS");
    cfg.ssid_len = strlen((char*)cfg.ssid);   
    // Password is null terminated (no length) specified.  Use this filling.
    os_sprintf((char*)cfg.password, "");
    cfg.channel = 1;
    cfg.authmode = AUTH_OPEN;
    cfg.ssid_hidden = 0;
    cfg.max_connection = 4;    // Note: default 4, max 4
    cfg.beacon_interval = 100; // Note: support 100 ~ 60000 ms, default 100   
   
    dbg("set=%u\n", wifi_softap_set_config(&cfg));

    dbg("sleep=%u\n", wifi_set_sleep_type(NONE_SLEEP_T));
}

void setupWiFi()
{
    os_printf("\nConnecting to your router");

    wifi_set_opmode(STATIONAP_MODE);
   
    startHost();

    // CLIENT SETUP
    struct station_config sc;
    os_memset(&sc, 0, sizeof(struct station_config));
    sc.bssid_set = 0;
    os_memcpy(&sc.ssid, SSID, 32);
    os_memcpy(&sc.password, PW, 64);   
    wifi_station_set_config_current(&sc);

    wifi_station_set_hostname(HOST);

    wifi_station_connect();

    os_timer_disarm(&ptimer);
    os_timer_setfn(&ptimer, (os_timer_func_t *)chkAP, NULL);
    os_timer_arm(&ptimer, 1000, 1);
}

void onError(void* conn, s8 err)
{
    // Never seen this called!
    os_printf("ERR=%d\n", err);
}

void onReceive(void* c, char* buf, u16 length)
{
    if (c != conn)
        os_printf("ERROR onReceive %p != %p\n", c, conn);
    conn = (struct espconn*)c;

    u32 now = system_get_time() / 1000;
    os_printf("rcv/snd=%ums ", now - last);
    last = now;
   
    static char buffer[1024];

    // Print out the request just to make sure its what we think it is.
    os_memcpy(buffer, buf, length);
    *(buffer + length) = 0;
    // os_printf(buffer);
    // We'll send back a page that keeps reloading,
    // so we can check for errors and/or memory leaks.
    os_sprintf(buffer, "HTTP/1.1 200 OK\r\n"
        "Server: InqPortal/5.0\r\n"
        "Content-Length: %d\r\n"
        "Content-type: text/html\r\n"
        "Pragma: no-cache\r\n\r\n%s", strlen(PAGE), PAGE);
    printIfErr(espconn_send(conn, (u8*)buffer, strlen(buffer)), "send");   
}

void disconnect(void* c)
{
    if (c != conn)
        os_printf("ERROR disconnect %p != %p\n", c, conn);       
    conn = (struct espconn*)c;

    u32 now = system_get_time() / 1000;
    os_printf("dis=%ums ", now - last);
    last = now;
   
    s8 rtn;
    if ((rtn = espconn_disconnect(conn)))
    {
        os_printf("ERR - disconnect = %d\n", rtn);
        if ((rtn = espconn_abort(conn)))
            os_printf("ERR - abort = %d\n", rtn);
    }
    if ((rtn = espconn_delete(conn)))
        os_printf("del=%d ", rtn);
}

void onDisconnect(void* c)
{
    // This is always different
    //if (c != conn)
        //os_printf("ERROR onDisconnect %p != %p\n", c, conn);

    u32 now = system_get_time() / 1000;
    os_printf("dis(%p)=%ums mem=%u",
        c, now - last, system_get_free_heap_size());
    last = now;   
}

void onSent(void* c)
{
    if (c != conn)
        os_printf("ERROR onSent %p != %p\n", c, conn);
    conn = (struct espconn*)c;
   
    u32 now = system_get_time() / 1000;
    os_printf("snt=%ums ", now - last);
    last = now;

    os_timer_disarm(&ptimer);
    os_timer_setfn(&ptimer, (os_timer_func_t *)disconnect, conn);
    os_timer_arm(&ptimer, 5, 0);
}

void onConnection(void* c)
{
    conn = (struct espconn*)c;
       
    // Incoming connection
    last = system_get_time() / 1000;
    os_printf("\nconn(%u), ", cnt++);   
    espconn_regist_recvcb(conn, onReceive);
    espconn_regist_sentcb(conn, onSent);
    espconn_regist_disconcb(conn, onDisconnect);   

    //espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
    // We'll use our own buffer instead of their 2920 buffer
    //espconn_clear_opt(conn, ESPCONN_COPY | ESPCONN_KEEPALIVE);
}

void ICACHE_FLASH_ATTR user_init(void)
{
    uart_init(BIT_RATE_115200, BIT_RATE_115200);

    setupWiFi();
   
    // Setup and start web server listener
    LOCAL struct espconn listen;
    LOCAL esp_tcp tcp;
    os_memset(&listen, 0, sizeof(struct espconn));
    listen.type = ESPCONN_TCP;
    listen.state = ESPCONN_NONE;   
    listen.proto.tcp = &tcp;
    listen.proto.tcp->local_port = 80;
   
    espconn_regist_connectcb(&listen, onConnection); 
    // This appears to be an error handler.  Haven't seen it hit yet.
    espconn_regist_reconcb(&listen, onError);
       
    printIfErr(espconn_accept(&listen), "listen");
   
   os_printf("Ready\n");
}

uint32 ICACHE_FLASH_ATTR user_rf_cal_sector_set(void)
{
    enum flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;

    switch (size_map) {
        case FLASH_SIZE_4M_MAP_256_256:
            rf_cal_sec = 128 - 5;
            break;

        case FLASH_SIZE_8M_MAP_512_512:
            rf_cal_sec = 256 - 5;
            break;

        case FLASH_SIZE_16M_MAP_512_512:
        case FLASH_SIZE_16M_MAP_1024_1024:
            rf_cal_sec = 512 - 5;
            break;

        case FLASH_SIZE_32M_MAP_512_512:
        case FLASH_SIZE_32M_MAP_1024_1024:
            rf_cal_sec = 1024 - 5;
            break;

        case FLASH_SIZE_64M_MAP_1024_1024:
            rf_cal_sec = 2048 - 5;
            break;
        case FLASH_SIZE_128M_MAP_1024_1024:
            rf_cal_sec = 4096 - 5;
            break;
        default:
            rf_cal_sec = 0;
            break;
    }
    return rf_cal_sec;
}