0

4th Update: I bought an ESP32 (the successor to ESP8266) and am getting the same problem. I used the same code except used the Wifi.h library instead, also removed the ESP8266WiFiGratuitous.h code. I added an LED flash at the start of the loop and can confirm the LED stops flashing, so I can confirm the loop() stops running. This makes me think it must be something to do with my WIFI network.

3rd update: I've removed all the code and instead uploaded a simple onboard LED blink sketch. The LED has been blinking for over 24 hrs which leads me to think there is something in the wifi code that is making the software crash.

2nd update: I've even tried resetting the board at the start of the loop() body, every 10 mins, using the millisDelay.h library, calling this method to reset:

void(* resetFunc) (void) = 0;

but that doesn't help either. The loop() method seems to completey stop running.

Update: I've added experimental::ESP8266WiFiGratuitous::stationKeepAliveSetIntervalMs(); but am still failing to get a response after 1hr, with about 15 requests made in that hour.

I have this code which works on my ESP8266:

#include <ESP8266WiFi.h>
#include <ESP8266WiFiGratuitous.h>

const size_t input_buffer_length = 256;

const char* ssid = "iiNetC2DAD7";
const char* wifiPassword = "aM9PrxcbtkS";
const char* passwordToOpenDoor = "/87"; //password should begin with a slash

const int doorPin = 5;
const int buzzerPin = 4;
const int toneDuration = 700;

int numOfRequests = 0;

WiFiServer server(301); //Pick any port number you like
WiFiClient client;

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println(WiFi.localIP());

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, 1);

  pinMode(doorPin, OUTPUT);
  digitalWrite(doorPin, 0);

  pinMode(buzzerPin, OUTPUT);
  digitalWrite(buzzerPin, 0);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, wifiPassword); 

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  experimental::ESP8266WiFiGratuitous::stationKeepAliveSetIntervalMs();

  server.begin();
  Serial.println("Server started.");
  Serial.println(WiFi.localIP());
}

void loop() {
  client = server.available();
  if (!client) {    
    return;
  }

  while(!client.available()){
    delay(1);  
  }

  char request[input_buffer_length];
  client.readBytesUntil('\r', request, input_buffer_length);  
  Serial.println(request);

  if(strstr(request, passwordToOpenDoor)) {//Is password correct?
    GenerateResponse("Password is correct");
    OpenDoor();
    CorrectPasswordSound();
  }
  //Got a GET request and it wasn't the favicon.ico request, must have been a bad password:
  else if (!strstr(request, "favicon.ico")) {
    GenerateResponse("Password is incorrect.");
  }  
}

void OpenDoor() {
  digitalWrite(LED_BUILTIN, 0); //flash the onboard LED to help during testing.
  digitalWrite(doorPin, 1);
  delay(500);
  digitalWrite(LED_BUILTIN, 1);
  digitalWrite(doorPin, 0);
}

void GenerateResponse(const char *text) {
  Serial.println(text);
  client.print(
      "HTTP/1.1 200 OK\r\n"
      "Content-Type: text/html\r\n"
      "\r\n"
      "<!DOCTYPE HTML>\r\n"
      "<html>\r\n"
      "<br><h1><b>"
  );
  client.print(text);
  client.print("</b></h1><br><h1><b>Num of requests: ");
  client.print(++numOfRequests);  
  client.print("</b></h1></html>\r\n");
  client.flush();  
}

void CorrectPasswordSound() {
  //Play 1700, 1800, 1900Hz
  for(int i = 17; i < 20; i++){
    int frequency = i * 100;
    tone(buzzerPin, frequency);
    delay(toneDuration);
    noTone(buzzerPin);
  }
}

However, after a random amount of time, the software crashes and the device won't respond to HTTP requests anymore.

I've tested this by printing to the serial monitor every loop. It seems as though it crashes and the loop() function stops running since it stops printing to the monitor.

I noticed that it'll still reply to pings.

It won't reconnect after restarting the router, I need to press the reset button on the ESP8266 to get it to start working again.

I've tested it by sending an HTTP Get request every 5 mins for many hours (using a C# console app). Sometimes it failed after 45 mins, sometimes 7 hours.

I initially thought it was because I was using String objects, but after getting an answer to this question and testing it out with only C-strings, I still have the same problem.

I've been sending HTTP Get requests using Chrome on my phone and laptop, as well as the .NET Framework class System.Net.Http.HttpClient.

  • Board: AI-Thinker ESP8266MOD
  • IDE: Arduino 1.8.12
  • ESP8266 Boards version = 2.6.3
  • IDE Settings:

enter image description here

I've tried resetting the connection using:

server.stop();
delay(1000);
server.begin();

Then this:

ESP.reset();

Then after those 2 didn't work, this:

ESP.restart();

Resetting it frequently did nothing.

Is there anything in my code that stands out? If there's nothing wrong withe code, then maybe my only option is to hard reset the board every 30 mins some how.

  • Your code looks okay. A couple of suggestions. 1) move the WiFiClient client; into the loop() and can combined with server.available(); to form a one liner like this WiFiClient client = server.available();. 2) the only time that the loop() seems to stop running will be the time where if (!client) { return;} which is actually when server is not available. I would suggest instead of simply return, you print something out before the return to see if that's what happening. – hcheung May 7 at 11:16
  • @hcheung but then client won't be recognised in the GenerateResponse() function. – David Klempfner May 7 at 11:39
  • 1
    Oh, you are right. I guess it has to be at Global then. – hcheung May 7 at 11:54
  • make the WiFiClient a local variable. pass it to function as reference. void GenerateResponse(WiFiClient& client, const char *text) { – Juraj May 8 at 5:43
  • @Juraj will that provide any benefits? – David Klempfner May 8 at 6:02
1

I don't know if this is your problem, but I'll leave it here because it is certainly a potential bug.

This code in loop:

  while(!client.available()){
    delay(1);  
  }

can cause a deadlock if you have a client that drops or doesn't talk. Turn that around and let the loop keep running.

boolean haveClient = false;

void loop() {
  client = server.available();
  if (client) {    
    haveClient = true;
  } else {
    haveClient = false;
  }

  if(haveClient && client.available()){

     // The rest of your loop where you read
     //  from the client goes inside here. 

That way if you get a client and then available keeps returning false you can at least keep checking for a client.

This may not be and is not intended to be a code solution. This is probably not the best way to code what I'm trying to say. I don't know if server.available will keep giving you back the same client or if it is going to cut you off to call that again before the client disconnects. There may be some method in there to check the connection. Either way, that's not the point. The point is to never capture your code into a locked up while loop. That is always asking for a deadlock. It's bad practice except for cases like fatal errors where you truly want to lock the thing up. Let the loop keep looping. Use if statements to decide which parts get run when.

If you want to check that this is where you are locking up, put some sort of output before and after that while loop. Print something or turn a light on before and off after. That way you can tell if this is the spot you're locking up.

| improve this answer | |
  • After implementing your change, it's worked the longest it ever has, over 7hrs! – David Klempfner May 15 at 0:04
1

Thanks to Delta_G for pointing me in the right direction. The trick was to not get stuck in the loop() function, ie. no while loops.

The problem was that after you run client = server.available();, the client needs some time before client.available() returns true, which is what the while loop is for, however, for some reason, the client would lose its connection every now and then and client.available() would never return true, in which case you get stuck in the while loop. You need to give it a second chance and let the process start again.

By delaying about 50ms, it seems to be enough time for the client to establish a connection, and if it doesn't, it loops and tries again so quickly that it appears as though the client always gets the connection and the code works fine.

Here's the working code:

void loop() {      
  WiFiClient client = server.available();

  if(client){
    delay(50);
    if(client.available()) {
        //rest of code here
    }
}
| improve this answer | |
0

This will be the "server unreachable after a while" problem, which is solved solved by sending gratuitous ARP requests. In the esp8266 Arduino core 2.7 they added functions for this. The main function is experimental::ESP8266WiFiGratuitous::stationKeepAliveSetIntervalMs();. Include <#include "ESP8266WiFiGratuitous.h>

Notes:

  • You don't know if your loop stops. It doesn't print anything if no client can reach it.

  • remove the flush() after readBytesUntil(). it doesn't do what you think it does.

| improve this answer | |
  • You means stationKeepAliveSetIntervalMs? or it is at somewhere else in the source code? – hcheung May 7 at 9:42
  • sorry it changed on the way from PR to release. yes stationKeepAliveSetIntervalMs – Juraj May 7 at 9:46
  • How do I read the password in the request URL without using readBytesUntil()? – David Klempfner May 7 at 11:14
  • Juraj is suggesting to delete the client.flush(), not the client.readBytesUntil(). – hcheung May 7 at 11:21
  • 1
    sorry I updated the answer. it is still an experimental function – Juraj May 7 at 12:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.