Debugging WiFiClient with an ESP8266 on PlatformIO

I didn’t think the Arduino threaded so I’m not quite sure how to answer that. Here is an example of a failure:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Ticker.h>
#include <Arduino.h>

void connectHost(const char*, int);

void setup() {
    Serial.begin(74880);
    Serial.setDebugOutput(true);
    Serial.flush();
    WiFi.begin(F("Xxxxx"), F("xxxxxxxx"));
    Serial.println();
    Serial.print(F("Waiting for connection."));
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("."));
        delay(500);
    }
    Serial.println();
    Serial.println(F("Connected."));

    WiFi.mode(WIFI_STA);
    delay(200);

    MDNS.begin(WiFi.hostname());

    Serial.print(F("DNS #1: "));
    Serial.print(WiFi.dnsIP().toString().c_str());
    Serial.print(F(", DNS #2: "));
    Serial.println(WiFi.dnsIP(1).toString().c_str());
}

void loop() {
    Ticker lookup;
    lookup.attach(5, [lookup](){connectHost("google.com", 80);});

    while (true) {
        yield();
    }
}

void connectHost(const char* hostname, int port) {
    WiFiClient client;
    client.setTimeout(10000);
    int retval = client.connect(hostname, port);
    if (retval != 1) {
        Serial.println(F("Connection failed."));
        return;
    } else {
        Serial.println(F("Connected to endpoint."));
    }
}

This sketch results in the following consistent failure:

Waiting for connection.......scandone

Connected.
DNS #1: 1.1.1.1, DNS #2: 1.0.0.1
pm open,type:2 0
Connection failed.

While this sketch works as expected:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Ticker.h>
#include <Arduino.h>

void connectHost(const char*, int);
void _delay(unsigned long);

void setup() {
    Serial.begin(74880);
    Serial.setDebugOutput(true);
    Serial.flush();
    WiFi.begin(F("Xxxxx"), F("xxxxxxxx"));
    Serial.println();
    Serial.print(F("Waiting for connection."));
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("."));
        delay(500);
    }
    Serial.println();
    Serial.println(F("Connected."));

    WiFi.mode(WIFI_STA);
    delay(200);

    MDNS.begin(WiFi.hostname());

    Serial.print(F("DNS #1: "));
    Serial.print(WiFi.dnsIP().toString().c_str());
    Serial.print(F(", DNS #2: "));
    Serial.println(WiFi.dnsIP(1).toString().c_str());
}

void loop() {
    WiFiClient client;
    client.setTimeout(10000);

    while (true) {
        int retval = client.connect("google.com", 80);
        if (retval != 1) {
            Serial.println(F("Connection failed."));
            return;
        } else {
            Serial.println(F("Connected to endpoint."));
        }
    _delay(5000);
    }
}

void _delay(unsigned long ulDelay) {
    unsigned long ulNow = millis();
    unsigned long ulThen = ulNow + ulDelay;
    while (ulThen > millis()) {
        yield();
    }
}

Result:

Waiting for connection.......scandone

Connected.
DNS #1: 1.1.1.1, DNS #2: 1.0.0.1
pm open,type:2 0
Connected to endpoint.

Since I really wanted the flexibility of the Ticker() callback, I ended up using a static bool which was flipped by the Ticker() and picking that up in the loop and calling my connection like this:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Ticker.h>
#include <Arduino.h>

void connectHost(const char*, int);
static bool doConnect = false;

void setup() {
    Serial.begin(74880);
    Serial.setDebugOutput(true);
    Serial.flush();
    WiFi.begin(F("Xxxxx"), F("xxxxxxxx"));
    Serial.println();
    Serial.print(F("Waiting for connection."));
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("."));
        delay(500);
    }
    Serial.println();
    Serial.println(F("Connected."));

    WiFi.mode(WIFI_STA);
    delay(200);

    MDNS.begin(WiFi.hostname());

    Serial.print(F("DNS #1: "));
    Serial.print(WiFi.dnsIP().toString().c_str());
    Serial.print(F(", DNS #2: "));
    Serial.println(WiFi.dnsIP(1).toString().c_str());
}

void loop() {
    Ticker lookup;
    lookup.attach(5, [lookup](){doConnect = true;});

    while (true) {
        if (doConnect) {
            doConnect = false;
            connectHost("google.com", 80);
        }
        yield();
    }
}

void connectHost(const char* hostname, int port) {
    WiFiClient client;
    client.setTimeout(10000);
    int retval = client.connect(hostname, port);
    if (retval != 1) {
        Serial.println(F("Connection failed."));
        return;
    } else {
        Serial.println(F("Connected to endpoint."));
    }
}

A bit of a kludge, but it works now.

Using the Ticker.h library, it will do an attach on a software timer

So that should actually be okay because it’s not a hardware timer and your code is not called in an ISR context. Does it make a difference if you program it like this?

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Ticker.h>
#include <Arduino.h>

void connectHost(const char*, int);

static Ticker lookup;

void setup() {
    Serial.begin(74880);
    Serial.setDebugOutput(true);
    Serial.flush();
    WiFi.begin(F("Xxxxx"), F("xxxxxxxx"));
    Serial.println();
    Serial.print(F("Waiting for connection."));
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("."));
        delay(500);
    }
    Serial.println();
    Serial.println(F("Connected."));

    WiFi.mode(WIFI_STA);
    delay(200);

    MDNS.begin(WiFi.hostname());

    Serial.print(F("DNS #1: "));
    Serial.print(WiFi.dnsIP().toString().c_str());
    Serial.print(F(", DNS #2: "));
    Serial.println(WiFi.dnsIP(1).toString().c_str());

    lookup.attach(12, [lookup](){connectHost("google.com", 80);});
}

void loop() { }

void connectHost(const char* hostname, int port) {
    static WiFiClient client;
    client.setTimeout(10000);
    int retval = client.connect(hostname, port);
    if (retval != 1) {
        Serial.println(F("Connection failed."));
        return;
    } else {
        Serial.println(F("Connected to endpoint."));
    }
}

That fails as well although I see where you were going with it. It does seem like the Ticker() loses all context. Just FYI I did also try this:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Ticker.h>
#include <Arduino.h>

void setup() {
    Serial.begin(74880);
    Serial.setDebugOutput(true);
    Serial.flush();
    WiFi.begin(F("Xxxxx"), F("xxxxxxxx"));
    Serial.println();
    Serial.print(F("Waiting for connection."));
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(F("."));
        delay(500);
    }
    Serial.println();
    Serial.println(F("Connected."));

    WiFi.mode(WIFI_STA);
    delay(200);

    MDNS.begin(WiFi.hostname());

    Serial.print(F("DNS #1: "));
    Serial.print(WiFi.dnsIP().toString().c_str());
    Serial.print(F(", DNS #2: "));
    Serial.println(WiFi.dnsIP(1).toString().c_str());
}

void loop() {
    Ticker lookup;
    lookup.attach(5, [lookup](){
        Serial.println(F("In ticker."));
        WiFiClient client;
        client.setTimeout(10000);
        int retval = client.connect("google.com", 80);
        if (retval != 1) {
            Serial.println(F("Connection failed."));
            return;
        } else {
            Serial.println(F("Connected to endpoint."));
        }
    });
    while (true) {yield();}
}

If it was a context issue you’d think that would take care of it - the same issue, however.