0

I've created a simple sketch for an esp32 that hosts a REST API that can be accessed via HTTP. Based on the instructions received from the client, the esp32 controls some neopixels with some various animations I've written.

Everything worked as expected until I got to an animation that runs in a while loop. The loop breaks when a new client is detected so that the light can switch functions. However, when the new client is detected, the client seems to be discarded or consumed, and i just end up back in the loop() function until I make a second HTTP request, then the changes to show up.

I'm sure it's something obvious, but what have I done wrong?

Code:

#include "WiFi.h"
#include "FastLED.h"
#include "aREST.h"
#include "ArduinoJson.h"

#define NUM_LEDS 64
#define DATA_PIN 18

CRGB leds[NUM_LEDS];
int LED_COUNT = 64;
int MAX_BRIGHTNESS = 200;

const char* ssid = "wifi";
const char* password = "password";
aREST rest = aREST();
WiFiServer server(80);
WiFiClient client;

void setup() {
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, LED_COUNT);
  flashAll(CRGB::Black);
  // Bind urls to the handling functions
  rest.function("random-strobe", randomStrobe);
  rest.function("solid-color", solidColor);
  rest.function("reset", reset);

  // Connect to WiFi and wait until we're connected to continue
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    flashAll(CRGB::Cyan);
    delay(500);
    flashAll(CRGB::Black);
    delay(500);
  }
  server.begin();
  flashAll(CRGB::Green);
  delay(2000);
  flashAll(CRGB::Black);
}

void loop() {
  if (checkForTask()) {
    rest.handle(client);
  }
}

boolean checkForTask() {
  // I have tried making this reassignment conditional
  // when the global 'client' is false, but it did not help
  //if (!client) { client = server.available(); }
  client = server.available(); 
  if (client) {
    return true;
  }
  return false;
}

int randomStrobe(String params){
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:application/json");
  client.println();
  client.stop();
  StaticJsonDocument<200> jsonDoc;
  Serial.println(params);
  Serial.println("params recieved. parsing...");
  auto error = deserializeJson(jsonDoc, params);
  if (!error) {
    // some setup stuff
    while(!checkForTask()){
      // do stuff in here
    }
    return 0;
  }
}
  • I have tried making this reassignment conditional how? – Jaromanda X Mar 20 at 3:26
  • @JaromandaX Something like: if (client) {...} else { client = server.available(); if (client) {...} -- i couldn't find any good documentation on checking for a client without that reassignment part, but i'm sure there is a better way.. – shadowkrazee Mar 20 at 22:49
  • @Juraj I fail to see how this is different than what i have, really. could you elaborate? – shadowkrazee Mar 21 at 2:08
1

Okay, I've managed to resolve the issue with some general direction from @juraj.

I now have TWO WifiClient instances:

WiFiClient client;
WiFiClient newClient;

This allows me to use the newClient instance to check for a new client:

boolean checkForTask() {
  newClient = server.available();
  if (newClient) {
    Serial.println("new client detected.");
    return true;
  }
  return false;
}

and only reassign the main client instance when a new client is actually detected:

void loop() {
  if (!newClient.connected()) {
    checkForTask();     
  } else {
    client = newClient;
    runTask();  
  }
}

This seems to work without any discernible side effects or additional resource usage, but if there is a more efficient way to accomplish this, please share. Thanks!

  • I show a simpler solution in my answer – Juraj Mar 25 at 5:51
1

server.available() doesn't return an already 'claimed' connection again. It returns a new connection or an empty WiFiClient object.

The esp8266 and esp32 Arduino core WiFi library close the underlying connection if the last referring WiFiClient instance is freed. The assignment of empty WiFiClient object frees the previous reference to underlying socket and if it was the last referrence, the library closes the socket.

You get a client with server.available() in checkForTask() and then you call server.available() again. It returns an empty WiFiClient object and you assign it to your WiFiClient object. The library closes the referred connection.

The solution is not to check server.available until WiFiClient is valid.

if (!client) {
  client = server.available();
}
  • Thank you for your help. How might I check for a new client to exit the loop, and then execute the client's instructions? What should me checkForTask() method look like? (I suspect the 'if' condition just needs to be revisited, though you should note that the client is declared globally, and should only be 'destroyed' by the reassignment, if i'm understanding correctly.) I have more experience with other languages, but my c++ is limited to arduino. sorry if i'm missing something obvious. – shadowkrazee Mar 21 at 23:08
  • @shadowkrazee, I edited the answer. I don't understand why I didn't see the client is global. I solved all kind of similar traps with networking libraries in my project with Telnet server and WebServer on WiFi and Ethernet libraries github.com/jandrassy/Regulator/blob/… – Juraj Mar 22 at 6:13

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.