-->
Page 1 of 1

ESP8266 TCP Server gets 'weird' when Macbook TCP Client idle

PostPosted: Tue Oct 09, 2018 5:12 pm
by standarddeviant
Hello,

I have a relatively simple sketch, and relatively simple behavior. The sketch has a TCP server, and once connected, performs analogRead's every 100ms, and sends that data every 1second as a 28-byte packet to the remote TCP client.

For debug, I'm using Julia (programming language) on a Macbook to initiate a TCP connection with my ESP8266 server, and receive packets. For debug, I'm printing to the serial port every 2 seconds driven by the Ticker library.

Below is debug output and
* "Connected(x): 0" means "Connection 0 is active and millis() = x"
* "cix=0, afw=x" means "On connection 0, WifiClient.availableForWrite() = x"

Where things get 'weird' is when the Macbook goes to sleep. My theory is that it's related to the ESP8266 trying to send data on a TCP connection where the other side did not cleanly close the socket.

The 'Connected' message should print every 2 seconds, and it does until the macbook goes to sleep. When the macbook goes to sleep, 'availableForWrite' goes down by 28 bytes each packet, which makes sense. What doesn't make sense is that 'availableForWrite' gets 'stuck' at a number much greater than 0, and then the 2 second differential in millis becomes much greater than 2000 or 2 seconds. Here's where things start to go 'weird':
Code: Select allConnected(457377): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(469677): 0   


I haven't wiresharked exactly what's happening, but has this happened to others? I can side-step this problem, but I want to get to the bottom of it and practice 'defensive coding'. I can't submit my sketch as-is, but can make a version that replicates the problem if that's helpful.

Here's a longer section of the serial log:
Code: Select allConnected(439377): 0   
cix=0, afw=2892
cix=0, afw=2892
Connected(441377): 0   
cix=0, afw=2892
cix=0, afw=2892
Connected(443377): 0   
cix=0, afw=2892
cix=0, afw=2892
Connected(445377): 0   
cix=0, afw=2892
cix=0, afw=2892
Connected(447377): 0   
cix=0, afw=2892
cix=0, afw=2892
Connected(449377): 0   
cix=0, afw=2892
cix=0, afw=2864
Connected(451377): 0   
cix=0, afw=2836
cix=0, afw=2808
Connected(453377): 0   
cix=0, afw=2780
cix=0, afw=2752
Connected(455377): 0   
cix=0, afw=2724
cix=0, afw=2696
Connected(457377): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(469677): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(481877): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(494077): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(506077): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(518377): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(530577): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(542677): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(554877): 0   
cix=0, afw=2696
cix=0, afw=2696
Connected(566877): 0   
cix=0, afw=2696
cix=0, afw=2696

Re: ESP8266 TCP Server gets 'weird' when Macbook TCP Client

PostPosted: Thu Oct 11, 2018 12:25 pm
by standarddeviant
I've confirmed that if I force the Macbook to not go to sleep, then I don't see this behavior.

Here's my sketch that exhibits this behavior - keep in mind I'm also periodically sending a UDP broadcast that I think is unrelated, but could be related. Basically, the behavior of the Ticker library ceases to behave properly in the context of the sketch.
Code: Select all

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>         //https://github.com/tzapu/WiFiManager

// needed to send UDP packets
#include <WiFiUdp.h>

// needed to schedule analogRead's safely on ESP8266
#include <Ticker.h>

#define MY_LED_PIN (0)

// START OF UDP Broadcast Stuff
WiFiUDP Udp;
const uint16_t g_udpBroadcastPort = 42000; // port to broadcast 'my' IP and TCP port
const uint16_t g_udpBroadcastID = 0; // ID for broadcast packets
IPAddress g_udpBroadcastIP(255, 255, 255, 255);
IPAddress g_localIP; // used for broadcasting 'this' device's IP
uint8_t g_ipBroadcastPacket[8]; // buffer for outgoing packet, with
                                 // 2 byte 'ID' to discriminate between devices
                                 // 4 byte IP address (sorry IPv6) of 'this' device
                                 // 2 byte port for incoming TCP connections
                                 // this packet is 'built' in setup()
// END OF UDP Broadcast Stuff


// START OF TCP Server Stuff
//how many clients should be able to connect to this ESP8266
#define MAX_TCP_SRV_CLIENTS (4)
const uint16_t g_tcpListenPort = 42000; // local port to listen on
WiFiServer g_tcpServer(g_tcpListenPort);
WiFiClient g_tcpServerClients[MAX_TCP_SRV_CLIENTS];

uint8_t g_incomingPacket[255];  // buffer for incoming packets
uint8_t g_outgoingPacket[255];
uint16_t g_opix = 0;
uint16_t g_sampix = 0;
uint16_t g_pktix = 0;
uint16_t g_led_state = 0;
// END OF TCP Server Stuff

Ticker analogReadTicker;

volatile uint16_t g_do_analog_read = 0;
volatile uint32_t g_missed_analog_reads = 0;
void set_g_do_analog_read() {
    if(g_do_analog_read != 0) {
        g_missed_analog_reads++;
    }
    g_do_analog_read = 1;
}


void setup() {
    pinMode(A0, INPUT);
    pinMode(MY_LED_PIN, OUTPUT);     // Initialize the MY_LED_PIN pin as an output
    digitalWrite(MY_LED_PIN, HIGH);   // Turn the LED on (Note that LOW is the voltage level

    // initialize serial
    Serial.begin(115200);

    //WiFiManager

    WiFiManager wifiManager;
    wifiManager.autoConnect("AutoConnectAP", "12345678");

    //if you get here you have connected to the WiFi
    Serial.println("connected...w00t! \\m/ O \\m/");

    // SETUP for UDP BROADCAST
    int tmpix=0;
    g_localIP = WiFi.localIP();
    g_ipBroadcastPacket[tmpix++] = (uint8_t)( (g_udpBroadcastID>>8)&0xFF );
    g_ipBroadcastPacket[tmpix++] = (uint8_t)( (g_udpBroadcastID>>0)&0xFF );
    g_ipBroadcastPacket[tmpix++] = g_localIP[0]; // IPv4 hack, 1 of 4
    g_ipBroadcastPacket[tmpix++] = g_localIP[1]; // IPv4 hack, 2 of 4
    g_ipBroadcastPacket[tmpix++] = g_localIP[2]; // IPv4 hack, 3 of 4
    g_ipBroadcastPacket[tmpix++] = g_localIP[3]; // IPv4 hack, 4 of 4
    g_ipBroadcastPacket[tmpix++] = (uint8_t)( (g_udpBroadcastPort>>8)&0xFF );
    g_ipBroadcastPacket[tmpix++] = (uint8_t)( (g_udpBroadcastPort>>0)&0xFF );

    // SETUP for TCP SERVER
    g_tcpServer.begin();
    g_tcpServer.setNoDelay(true);

    // SETUP for triggering analogRead's
    analogReadTicker.attach_ms(100, set_g_do_analog_read);
}
 
void loop() {
    uint32_t pkt_millis;
    int16_t analog_read;
    uint16_t cix, bix; // cix=client index, bix=byte index

    if( g_do_analog_read ) {
        g_do_analog_read = 0;
        analog_read = analogRead(A0);

        // add custom header to packet if this is the first sample in the packet
        if(0==g_opix) {
            g_led_state = (g_led_state + 1) & 1;
            uint32_t pkt_millis = millis();
            g_outgoingPacket[g_opix++] = 0xDA;
            g_outgoingPacket[g_opix++] = 0x7A;

            g_outgoingPacket[g_opix++] = (uint8_t)( (g_pktix>>8) & 0xFF );
            g_outgoingPacket[g_opix++] = (uint8_t)( (g_pktix>>0) & 0xFF );
            g_pktix++;

            g_outgoingPacket[g_opix++] = (uint8_t)( (pkt_millis>>24) & 0xFF );
            g_outgoingPacket[g_opix++] = (uint8_t)( (pkt_millis>>16) & 0xFF );
            g_outgoingPacket[g_opix++] = (uint8_t)( (pkt_millis>> 8) & 0xFF );
            g_outgoingPacket[g_opix++] = (uint8_t)( (pkt_millis>> 0) & 0xFF );
        }

        // every other packet, blink led every other sample
        // not to be confused with arduino blink examples
        if( g_led_state ) {
            digitalWrite(MY_LED_PIN, (g_sampix) & 1 ? LOW : HIGH);
        }

        // put analog_read sample in to g_outgoingPacket
        g_outgoingPacket[g_opix++] = (uint8_t)( (analog_read>>8) & 0xFF );
        g_outgoingPacket[g_opix++] = (uint8_t)( (analog_read>>0) & 0xFF );
        g_sampix++;

        // send packet if this is the last sample in the packet
        if( 10==g_sampix ) {
            // check for new clients every packet
            if (g_tcpServer.hasClient()) {
                for (cix = 0; cix < MAX_TCP_SRV_CLIENTS; cix++) {
                    //find free/disconnected spot
                    if (!g_tcpServerClients[cix] || !g_tcpServerClients[cix].connected()) {
                        if (g_tcpServerClients[cix]) {
                            g_tcpServerClients[cix].stop();
                        }
                        g_tcpServerClients[cix] = g_tcpServer.available();
                        g_tcpServerClients[cix].setNoDelay(true);
                        g_tcpServerClients[cix].setTimeout(10);
                        // Calling keepAlive seems to reboot the board... g_tcpServerClients[cix].keepAlive(7200, 10, 5);
                        continue;
                    }
                }
                //no free/disconnected spot so reject most recent connection
                WiFiClient serverClient = g_tcpServer.available();
                serverClient.stop();
            }

            // Broadcast IP on UDP every N TCP packets
            if( 0 == g_pktix % 2 ) {
                Udp.beginPacket(g_udpBroadcastIP, g_udpBroadcastPort);
                Udp.write((const uint8_t*) &(g_ipBroadcastPacket[0]), 8);
                Udp.endPacket();
                Serial.printf("Connected(%d): ", millis());
                for (cix = 0; cix < MAX_TCP_SRV_CLIENTS; cix++) {
                    if( g_tcpServerClients[cix] && g_tcpServerClients[cix].connected() ) {
                        Serial.printf("%d   ", cix);
                    }
                }
                Serial.printf("\n");
            }

            // Send TCP packets
            for( cix=0; cix<MAX_TCP_SRV_CLIENTS; cix++ ) {
                if( g_tcpServerClients[cix] && g_tcpServerClients[cix].connected() ) {
                    g_tcpServerClients[cix].write(g_outgoingPacket, g_opix);
                    g_tcpServerClients[cix].flush();
                    Serial.printf("g_tcpServerClients[%d].available()=%d\n", cix, g_tcpServerClients[cix].availableForWrite());
                }
            }

            g_sampix = 0; // reset sample index to build/send next packet
            g_opix = 0; // reset packet byte index to build/send next packet
        }
    }
}

Re: ESP8266 TCP Server gets 'weird' when Macbook TCP Client

PostPosted: Sat Oct 13, 2018 8:25 am
by rudy
I can't submit my sketch as-is, but can make a version that replicates the problem if that's helpful.


I would like to see what you are doing. I don't have a Mac but so no promise on being much help if that is part of the issue.