while loop makes wifi unresponsive

stamasd
Posts: 7
Joined: Sun May 26, 2019 12:24 pm

while loop makes wifi unresponsive

Postby stamasd » Sun May 26, 2019 12:48 pm

Let me start by saying I am new to ESP32 but I do have some experience with Arduino. I'm probably somewhere between an advanced novice, or a low-level intermediate Arduino user. I have next to zero programming experience outside of Arduino. But I did make some nice custom Arduino projects.

I'm ready to take my hobby to the next level with the ESP32. I got a couple of dev boards off Amazon, they use the ESP-WROOM-32 module. I use Arduino 1.8.9 with the latest ESP32 Arduino core from github.

I started with some of the ESP32 examples to understand the board. Currently I'm working with the WiFiAccessPoint example, trying to have it make different things with the LED over WiFi.

For instance, what I am trying to do now is to add a third option to the web page served over WiFi that makes the LED blink. I made a function that makes the LED turn on and off, and called that from the web page. That makes the LED blink only once though, so I made a while loop inside the blink function that turns the LED on/off under the control of an external variable.

So whit that what happens is:
1. turning the LED on or off over WiFi works as intended
2. Starting the blinking over WiFi works, but when I call that function the WiFi server becomes unresponsive and will not serve the web page anymore.

So once the blinking starts, I cannot control the LED anymore. I think the while loop is saturating both cores and the WiFi server doesn't get any more CPU time at all.

Is there a better way of doing this?

FWIW here's the sketch as it is now:

Code: Select all

/*
  WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it.

  Steps:
  1. Connect to the access point "yourAp"
  2. Point your web browser to http://192.168.4.1/H to turn the LED on or http://192.168.4.1/L to turn it off
     OR
     Run raw TCP "GET /H" and "GET /L" on PuTTY terminal with 192.168.4.1 as IP address and 80 as port

  Created for arduino-esp32 on 04 July, 2018
  by Elochukwu Ifediora (fedy0)
*/

#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>

#define LED_BUILTIN 2   // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
int blinker = 0;

// Set these to your desired credentials.
const char *ssid = "ESPTEST";
const char *password = "password:)";

WiFiServer server(80);


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);
  Serial.println();
  Serial.println("Configuring access point...");

  // You can remove the password parameter if you want the AP to be open.
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  Serial.println(ssid);
  Serial.println(password);
  server.begin();

  Serial.println("Server started");
}

void loop() {
  WiFiClient client = server.available();   // listen for incoming clients

  if (client) {                             // if you get a client,
    Serial.println("New Client.");           // print a message out the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        if (c == '\n') {                    // if the byte is a newline character

          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();

            // the content of the HTTP response follows the header:
            client.print("<br><br>Click <a href=\"/H\">here</a> to turn ON the LED.<br><br><br>");
            client.print("Click <a href=\"/L\">here</a> to turn OFF the LED.<br><br><br>");
            client.print("Click <a href=\"/B\">here</a> to make LED blink<br>");

            // The HTTP response ends with another blank line:
            client.println();
            // break out of the while loop:
            break;
          } else {    // if you got a newline, then clear currentLine:
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }

        // Check to see if the client request was "GET /H" or "GET /L": or /B
        if (currentLine.endsWith("GET /H")) {
          digitalWrite(LED_BUILTIN, HIGH); // GET /H turns the LED on
          blinker = 0;
        }
        if (currentLine.endsWith("GET /L")) {
          digitalWrite(LED_BUILTIN, LOW);                // GET /L turns the LED off
          blinker = 0;
        }
       if (currentLine.endsWith("GET /B")) {
          blinker = 1;
          blink();                                       // GET /B blinks
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println("Client Disconnected.");
  }
}

void blink(){
  while(blinker > 0) {
  digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level
  // but actually the LED is on; this is because
  // it is active low on the ESP-01)
  delay(random(40,100));                      
  digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
  delay(random(50,200));
  }                     
}

ESP_Sprite
Posts: 9727
Joined: Thu Nov 26, 2015 4:08 am

Re: while loop makes wifi unresponsive

Postby ESP_Sprite » Mon May 27, 2019 1:53 am

It's not saturating anything; it's the way your code is written. Your main loop does the following: wait until a client connects, figure out what the client wants by inspecting the URL, handling what the client wants, closing the clients connection, waiting for the next client etc.

You put 'blink()' in one of the handlers. This is a function that never returns (because it has an infinite while loop), so your main loop will never proceed to closing the current clients connection and handling the next one.

One of the ways to fix this would be to start up another thread and blink the LED there, or have a timer callback or something do it. The crux is that you have to do it asynchrously from the webserver, or otherwise the one can block the other.

stamasd
Posts: 7
Joined: Sun May 26, 2019 12:24 pm

Re: while loop makes wifi unresponsive

Postby stamasd » Mon May 27, 2019 3:07 am

Hmm I see. That makes sense now that I think about it, thanks. I'll have to figure out how to handle multithreading on the ESP in Arduino.

TomWS1
Posts: 21
Joined: Wed May 01, 2019 2:50 pm

Re: while loop makes wifi unresponsive

Postby TomWS1 » Mon May 27, 2019 11:45 am

stamasd wrote:
Mon May 27, 2019 3:07 am
Hmm I see. That makes sense now that I think about it, thanks. I'll have to figure out how to handle multithreading on the ESP in Arduino.
You don't need to get that 'sophisticated'. Just realize that the blinking process is just one of many things you want to do. So you can reconstruct Blink to keep it's own on/off timers and it can be called in every loop cycle. For example:

Code: Select all

void blink(){
  static uint32_t 
    lastBlink = 0,  // when the last blink occurred (so you can do proper math on millis() time...)
    blinkTime=0;  // when the next state change should occur
  uint32_t now=millis();  // get current time
  if (blinker > 0) {   // if the blinker control is set, check the timer
    if (blinkTime < now-lastBlink) {  // this reduces the rollover problem by using unsigned math to get the time difference
      lastBlink = now;  // reset the timer
      if (digitalRead(LED_BUILTIN))  {  // is it currently off?
        // yes, turn it on and set the timer
       digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level
       // but actually the LED is on; this is because
       // it is active low on the ESP-01)
       blinkTime = random(40,100);  // how long to stay on
     } else {                     
       digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
       blinkTime = random(50,200);  // how long to stay off
     }
   } // end of timer check
  } else  // blinker is false
  if (lastBlink) {  // blinker should be off, reset the timers and make sure the LED is off (do this only once blinker goes false)
    lastBlink = 0;
    blinkTime=0;
    digitalWrite(LED_BUILTIN, HIGH);
  }                    
}
now you can call blink on every cycle through loop() and the blinking will only occur when 'blinker' is set to non zero value.

stamasd
Posts: 7
Joined: Sun May 26, 2019 12:24 pm

Re: while loop makes wifi unresponsive

Postby stamasd » Mon May 27, 2019 12:53 pm

Thanks again, my inexperience is showing. This works great to blink the LED while having the web server still active. My issue is now that if the LED is blinking and I try to turn it off, it stops blinking but remains on. I think it's a timing issue, and to solve it I probably need another variable to store the desired state of the LED when not blinking. Or even better, use just one variable with 3 possible states (0,1 or 2) that is checked in a switch..case

stamasd
Posts: 7
Joined: Sun May 26, 2019 12:24 pm

Re: while loop makes wifi unresponsive

Postby stamasd » Mon May 27, 2019 2:56 pm

Yes that worked. The LED on/off/blinking and responsive to commands though with a little delay which shouldn't really matter for my next purpose.

I'm pretty happy with this approach, should work with my intended project (this was a test of course). That is a remote kiln controller which would need to do a few things concurrently: read temperature sensor, control heating element depending on temperature by using software PWM-like "on x seconds out of 10, and off 10-x seconds" where X depends on the heating regimen/ramp etc; and displaying the above over wifi while waiting for commands to adjust the cycles. Timing wouldn't be critical, delays of even 1-2s are acceptable.

Timing would become important for my project following that, a motorized equatorial telescope mount with self-positioning for any latitude, longitude, altitude and time of the day; but one project at a time for now.

Who is online

Users browsing this forum: No registered users and 48 guests