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
16 changes: 11 additions & 5 deletions cores/esp8266/LwipIntfDev.h
Original file line number Diff line number Diff line change
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
46 changes: 46 additions & 0 deletions cores/esp8266/yieldUntil.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
yieldUntil.h
Copyright (c) 2021 esp8266/Arduino

This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#ifndef __YIELDUNTIL_H
#define __YIELDUNTIL_H

#include <Arduino.h> // yield()
#include <PolledTimeout.h>

namespace esp8266 {
namespace utils {

// Let a chance for recurrent scheduled functions to run during a waiting delay
// returns false if timeout is reached before user-provided callback returns true.

template <typename CB>
bool yieldUntil (CB&& until, uint32_t timeoutMs) {
esp8266::polledTimeout::oneShotFastMs timeOut(timeoutMs);
while (true) {
if (until())
return true;
if (timeOut)
return false;
yield();
}
}

}; // ns utils
}; // ns esp8266

#endif // __YIELDUNTIL_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 <yieldUntil.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 (esp8266::utils::yieldUntil([&](){ return aResult.isSet(); }, timeout_ms))
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 (esp8266::utils::yieldUntil([&](){ return aResult.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 <yieldUntil.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
esp8266::utils::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
esp8266::utils::yieldUntil([this]() { return !_send_waiting; }, _timeout_ms);
_send_waiting = false;
} while(true);

Expand Down
81 changes: 81 additions & 0 deletions libraries/lwIP_Ethernet/examples/EthClient/EthClient.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
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");

eth.setDefault(true); // default route set through this interface
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,82 @@
/*
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 IPAddress(192,168,0,233)
#define LOCAL_GW IPAddress(192,168,0,254) // <== adapt to your network
#define LOCAL_MASK IPAddress(255,255,255,0)
#define DNS IPAddress(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");

eth.setDefault(true); // default route set through this interface
if (!ethInitStatic(eth, LOCAL_IP, LOCAL_GW, LOCAL_MASK, DNS)) {
// enabling debug message will show the real cause
Serial.printf("no hardware found or bad network configuration\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