Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce yieldUntil(untilFunction, timeoutMs) #8317

Closed
wants to merge 12 commits into from
18 changes: 12 additions & 6 deletions cores/esp8266/LwipIntfDev.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class LwipIntfDev: public LwipIntf, public RawDev
_mtu(DEFAULT_MTU),
_intrPin(intr),
_started(false),
_default(false)
_default(true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this intended?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Changing it to true allows to avoid calling manually eth.setDefault(), which is necessary for valid routing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense for one instance, true. ok if not a problem
(but, I wonder if default=false would be safer when there are more)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are more than one interface, only the last "defaulted" one becomes the default route.
I will investigate further on why this is necessary, but last time I checked packets are not routed without it.
I could have delegated the directive to "userland" but I think it is better to let the library deal with defaults so we don't need to change the API or the guidelines / examples when something changes or is fixed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see also WiFi

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the instance variable does not make sense and it should be a static (and shared between the wifi and the lwipintf class), if every interface wants to become the default on callback?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.
I have moved the default value back to false and updated examples to explicitly use the new interface as default route.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the lwip part also be made aware of default modifications? (with another issue / pr, I gather, not to pollute the changes too too much)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modification for "default" is reverted to what it was before the PR no ?
If the question is about "lwip2 - the driver adapter", it is very delicate to update the WiFi interface management code per possible hidden side effect. Maybe a new gh-issue describing the API question might be indeed opened for that purpose.

{
memset(&_netif, 0, sizeof(_netif));
}
Expand Down Expand Up @@ -64,7 +64,7 @@ class LwipIntfDev: public LwipIntf, public RawDev
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.gw)));
}

void setDefault();
void setDefault(bool deflt = true);

bool connected()
{
Expand Down Expand Up @@ -187,10 +187,10 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
return false;
}

_netif.flags |= NETIF_FLAG_UP;

if (localIP().v4() == 0)
{
// IP not set, starting DHCP
_netif.flags |= NETIF_FLAG_UP;
switch (dhcp_start(&_netif))
{
case ERR_OK:
Expand All @@ -204,6 +204,12 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
return false;
}
}
else
{
// IP is set, static config
netif_set_link_up(&_netif);
netif_set_up(&_netif);
}

_started = true;

Expand Down Expand Up @@ -386,9 +392,9 @@ err_t LwipIntfDev<RawDev>::handlePackets()
}

template <class RawDev>
void LwipIntfDev<RawDev>::setDefault()
void LwipIntfDev<RawDev>::setDefault(bool deflt)
{
_default = true;
_default = deflt;
if (connected())
{
netif_set_default(&_netif);
Expand Down
12 changes: 12 additions & 0 deletions cores/esp8266/Schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,15 @@ void run_scheduled_recurrent_functions()

fence = false;
}


bool yieldUntil (std::function<bool()> until, uint32_t maxMs) {
mcspr marked this conversation as resolved.
Show resolved Hide resolved
esp8266::polledTimeout::oneShotFastMs timeOut(maxMs);
while (true) {
if (until())
return true;
if (timeOut)
return false;
yield();
}
}
4 changes: 4 additions & 0 deletions cores/esp8266/Schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,

void run_scheduled_recurrent_functions();

// Let a chance for recurrent scheduled functions to run during a waiting delay

bool yieldUntil (std::function<bool()> until, uint32_t maxMs);

#endif // ESP_SCHEDULE_H
39 changes: 17 additions & 22 deletions libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <string.h>
#include <coredecls.h>
#include <PolledTimeout.h>
#include <Schedule.h>
#include "ESP8266WiFi.h"
#include "ESP8266WiFiGeneric.h"

Expand Down Expand Up @@ -621,22 +622,19 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
aResult = IPAddress(&addr);
} else if(err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
delay(timeout_ms);
// will resume on timeout or when wifi_dns_found_callback fires
_dns_lookup_pending = false;
// will return here when dns_found_callback fires
if(aResult.isSet()) {
if (yieldUntil([pIp = &aResult](){ return pIp->isSet(); }, timeout_ms))
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
err = ERR_OK;
}
_dns_lookup_pending = false;
// will return here when dns_found_callback fires (or timeout)
}

if(err != 0) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
} else {
if(err == ERR_OK) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
return 1;
} else {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
return 0;
}

return (err == ERR_OK) ? 1 : 0;
}

#if LWIP_IPV4 && LWIP_IPV6
Expand Down Expand Up @@ -671,22 +669,20 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
aResult = IPAddress(&addr);
} else if(err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
delay(timeout_ms);
if (yieldUntil([pIp = &aResult](){ return pIp->isSet(); }, timeout_ms))
err = ERR_OK;
// will resume on timeout or when wifi_dns_found_callback fires
_dns_lookup_pending = false;
// will return here when dns_found_callback fires
if(aResult.isSet()) {
err = ERR_OK;
}
// will return here when dns_found_callback fires (or timeout)
}

if(err != 0) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
} else {
if(err == ERR_OK) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
return 1;
} else {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
return 0;
}

return (err == ERR_OK) ? 1 : 0;
}
#endif

Expand All @@ -705,7 +701,6 @@ void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *ca
if(ipaddr) {
(*reinterpret_cast<IPAddress*>(callback_arg)) = IPAddress(ipaddr);
}
esp_schedule(); // break delay in hostByName
}

uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState& state)
Expand Down
16 changes: 5 additions & 11 deletions libraries/ESP8266WiFi/src/include/ClientContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extern "C" void esp_schedule();

#include <assert.h>
#include <esp_priv.h>
#include <Schedule.h>

bool getDefaultPrivateGlobalSyncValue ();

Expand Down Expand Up @@ -144,12 +145,8 @@ class ClientContext
return 0;
}
_connect_pending = true;
_op_start_time = millis();
for (decltype(_timeout_ms) i = 0; _connect_pending && i < _timeout_ms; i++) {
// Give scheduled functions a chance to run (e.g. Ethernet uses recurrent)
delay(1);
// will resume on timeout or when _connected or _notify_error fires
}
// will resume on timeout or when _connected or _notify_error fires
yieldUntil([this]() { return !_connect_pending; }, _timeout_ms);
_connect_pending = false;
if (!_pcb) {
DEBUGV(":cabrt\r\n");
Expand Down Expand Up @@ -487,11 +484,8 @@ class ClientContext
}

_send_waiting = true;
for (decltype(_timeout_ms) i = 0; _send_waiting && i < _timeout_ms; i++) {
// Give scheduled functions a chance to run (e.g. Ethernet uses recurrent)
delay(1);
// will resume on timeout or when _write_some_from_cb or _notify_error fires
}
// will resume on timeout or when _write_some_from_cb or _notify_error fires
yieldUntil([this]() { return !_send_waiting; }, _timeout_ms);
_send_waiting = false;
} while(true);

Expand Down
80 changes: 80 additions & 0 deletions libraries/lwIP_Ethernet/examples/EthClient/EthClient.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.

This is Ethernet version of:
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino
*/

#include <LwipEthernet.h>

Wiznet5500lwIP eth(/*SS*/16); // <== adapt to your hardware

const char* host = "djxmmx.net";
const uint16_t port = 17;

void setup() {
Serial.begin(115200);

Serial.println("\nEthernet\n");

if (!ethInitDHCP(eth)) {
Serial.printf("no hardware found\n");
while (1) {
delay(1000);
}
}

while (!eth.connected()) {
Serial.printf(".");
delay(1000);
}

Serial.printf("Ethernet: IP Address: %s\n",
eth.localIP().toString().c_str());
}

void loop() {

Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);

// Use WiFiClient class to create TCP connections
// (this class could have been named TCPClient)
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}

// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from ESP8266");
}

// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}

// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
client.sendAll(Serial); // this peer closes once all data are sent

// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();

delay(600000); // execute once every 10 minutes, don't flood remote service
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.

This is Ethernet version of:
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino
*/

#include <LwipEthernet.h>

#define LOCAL_IP 192,168,0,233
#define LOCAL_GW 192,168,0,254 // <== adapt to your network
#define LOCAL_MASK 255,255,255,0
#define DNS 8,8,8,8

Wiznet5500lwIP eth(/*SS*/16); // <== adapt to your hardware

const char* host = "djxmmx.net";
const uint16_t port = 17;

void setup() {
Serial.begin(115200);

Serial.println("\nEthernet\n");

if (!ethInitStatic(eth, IPAddress(LOCAL_IP), IPAddress(LOCAL_GW), IPAddress(LOCAL_MASK), IPAddress(DNS))) {
Serial.printf("no hardware found\n");
while (1) {
delay(1000);
}
}

Serial.printf("Ethernet: IP Address: %s\n",
eth.localIP().toString().c_str());
}

void loop() {

Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);

// Use WiFiClient class to create TCP connections
// (this class could have been named TCPClient)
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}

// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from ESP8266");
}

// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}

// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
client.sendAll(Serial); // this peer closes once all data are sent

// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();

delay(600000); // execute once every 10 minutes, don't flood remote service
}
Loading