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

Number of Max Concurrrent Connection in Station mode? #4460

Closed
dannybres opened this issue Mar 4, 2018 · 6 comments
Closed

Number of Max Concurrrent Connection in Station mode? #4460

dannybres opened this issue Mar 4, 2018 · 6 comments

Comments

@dannybres
Copy link

dannybres commented Mar 4, 2018

Hello,

I am using ESP8266WiFi in station mode to connect to my AP at home. It is controlling 3 RGB leds sets via the GPIO (9 channels for 3 sets). There is an situation where the server that controls them all polls the ESP8266WiFi server for the status of the lights with many concurrent http requests, e.g.

  • /rgb1/status
  • /rgb1/colour
  • /rgb1/brightness
  • /rgb2/status
  • /rgb2/colour
  • /rgb2/brightness
  • /rgb3/status
  • /rgb3/colour
  • /rgb3/brightness

My code is below and as new connections are created on the server, I add them to the array clients, once I have received a whole request and attempt to write back to that client, the line clients[i]->println("HTTP/1.1 200 OK"); takes a long time to execute and return 0, as I do not believe it still has that connection open and therefore cannot respond to the client? Is this because I am hitting a concurrent connection limit?

Is there a maximum number of connections in Station mode?
Can I increase this?

Thanks,

Daniel

#include <ESP8266WiFi.h>

const char* location = "tv room LED controller";
const int locationID = 2;

int whiteLedPin[3] = {5,14,3}; // Pin 8
int redLedPin[3] = {4,12,1}; // Pin 6
int greenLedPin[3] = {0,13,9}; // Pin 7
int blueLedPin[3] = {2,15,10}; // Pin 8

int rgbLedStatus[3] = {0,0,0};
String rgbColour[3] = {"FFFFFF","FFFFFF","FFFFFF"};

int WWLedStatus[3] = {0,0,0};
int ledWWBrightness[3] = {100,100,100};
#define MAX_CLIENTS 10
#define MAX_LINE_LENGTH 50
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
WiFiClient *clients[MAX_CLIENTS] = { NULL };
String st[MAX_CLIENTS];

void printJsonForTemperatureAndHumidityToClient(WiFiClient client,int locationID) {
  client.print("{\"room\": \"");
  client.print(location);
  client.print("\",\"id\": ");
  client.print(locationID);
  client.print(", \"led status\": ");
  client.print(rgbLedStatus[0]);
  client.print(", \"rgb colour\": \"");
  client.print(rgbColour[0]);
  client.print("\"}");
}

unsigned int hexToDec(String hexString) {
  unsigned int decValue = 0;
  int nextInt;
  
  for (int i = 0; i < hexString.length(); i++) {
    
    nextInt = int(hexString.charAt(i));
    if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9);
    if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15);
    if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15);
    nextInt = constrain(nextInt, 0, 15);
    
    decValue = (decValue * 16) + nextInt;
  }
  
  return decValue;
}

void setRGBLightState(int lightIndex){
  if (rgbLedStatus[lightIndex] == 0) {
    analogWrite(redLedPin[lightIndex], 0);
    analogWrite(greenLedPin[lightIndex], 0);
    analogWrite(blueLedPin[lightIndex], 0);
  } else {
    int redBrightness = hexToDec(rgbColour[lightIndex].substring(0,2));
    int greenBrightness = hexToDec(rgbColour[lightIndex].substring(2,4));
    int blueBrightness = hexToDec(rgbColour[lightIndex].substring(4,6));
  
    redBrightness = map(redBrightness, 0, 255, 0, 1023);
    greenBrightness = map(greenBrightness, 0, 255, 0, 1023);
    blueBrightness = map(blueBrightness, 0, 255, 0, 1023);

    analogWrite(redLedPin[lightIndex], redBrightness);
    analogWrite(greenLedPin[lightIndex], greenBrightness);
    analogWrite(blueLedPin[lightIndex], blueBrightness);
  }
}

void setWWLightState(int lightIndex){
  if (WWLedStatus[lightIndex] == 0) {
    analogWrite(whiteLedPin[lightIndex], 0);
  } else {
    analogWrite(whiteLedPin[lightIndex], map(ledWWBrightness[lightIndex], 0,100,0,1023));
  }
}

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

  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println(WiFi.localIP());

  for (int lightIdx = 1; lightIdx < 2; lightIdx++) {
    Serial.print("initialising pins for light: ");
    Serial.println(lightIdx);

    pinMode(redLedPin[lightIdx], OUTPUT);
    pinMode(greenLedPin[lightIdx], OUTPUT);
    pinMode(blueLedPin[lightIdx], OUTPUT);
    pinMode(whiteLedPin[lightIdx], OUTPUT);
  }
}

void loop() {

  // Check if a new client has connected
  WiFiClient newClient = server.available();
  if (newClient) {
    Serial.println("");
    Serial.println("new client");
    // Find the first unused space
    for (int i=0 ; i<MAX_CLIENTS ; ++i) {
        if (NULL == clients[i]) {
            clients[i] = new WiFiClient(newClient);
            Serial.print("new client created with index: ");
            Serial.println(i);

            break;
        }
     }
  }

  // Check whether each client has some data
  for (int i=0 ; i<MAX_CLIENTS ; ++i) {
    // If the client is in use, and has some data...
    if (NULL != clients[i] && clients[i]->available() ) {
      // Read the data 
      char newChar = clients[i]->read();

      // If we have the end of a string
      // (Using the test your code uses)
      if ('\r' == newChar) {
        // Blah blah, do whatever you want with inputs[i]
        Serial.print("request complete for client:");
        Serial.print(i);
        Serial.print(":");
        Serial.println(st[i]);

        Serial.print("clients[");
        Serial.print(i);
        Serial.print("]->available()");
        Serial.println(clients[i]->available());
        
  delay(200);

    Serial.println(clients[i]->println("HTTP/1.1 200 OK"));
    Serial.println(clients[i]->println("Content-Type: text/html"));
    Serial.println(clients[i]->println("")); //  do not forget this one  

    Serial.println("header done");

    // Read the first line of the request
    String request = st[i];
    if (request.indexOf("/api/rgb") != -1)  {
      // contains /api in request
      int lightIndex = request.substring(request.indexOf("/api/rgb")+8,request.indexOf("/api/rgb")+9).toInt();
      if (request.indexOf("/status") != -1)  {
        clients[i]->println(rgbLedStatus[lightIndex]);
      } else if (request.indexOf("/brightness") != -1)  {
        int r = hexToDec(rgbColour[lightIndex].substring(0,2));
        int g = hexToDec(rgbColour[lightIndex].substring(2,4));
        int b = hexToDec(rgbColour[lightIndex].substring(4,6));
        int maxrg = _max(r,g);
        int maxrgb = _max (maxrg,b);
        float ratio = (float)maxrgb / 255.0;
        float brightness = ratio * 100;
        clients[i]->println(brightness);
      } else if (request.indexOf("/colour") != -1)  {
        clients[i]->println(rgbColour[lightIndex]);
      } else if (request.indexOf("/setcolour") != -1)  {
        rgbColour[lightIndex] = request.substring(request.indexOf("/setcolour")+11,request.indexOf("/setcolour")+17);
        setRGBLightState(lightIndex);
      } else if (request.indexOf("/on") != -1)  {
        rgbLedStatus[lightIndex] = 1;
        setRGBLightState(lightIndex);
      } else if (request.indexOf("/off") != -1)  {
        rgbLedStatus[lightIndex] = 0;
        setRGBLightState(lightIndex);
      }
    } else if (request.indexOf("/api/ww") != -1)  {
      // contains /api in request
      Serial.println("ww");

      int lightIndex = request.substring(request.indexOf("/api/ww")+7,request.indexOf("/api/ww")+8).toInt();
      if (request.indexOf("/status") != -1)  {
        Serial.println("ww status");
        clients[i]->println(WWLedStatus[lightIndex]);
      } else if (request.indexOf("/brightness") != -1)  {
        Serial.println("ww brightness");         
        if(request.length() > 33){
          ledWWBrightness[lightIndex] = request.substring(24,24+request.length()-33).toInt();
        }
        clients[i]->println(ledWWBrightness[lightIndex]);
      } else if (request.indexOf("/on") != -1)  {
        Serial.println("ww on");         
        WWLedStatus[lightIndex] = 1;
        setWWLightState(lightIndex);
      } else if (request.indexOf("/off") != -1)  { 
        Serial.println("ww off");         
        WWLedStatus[lightIndex] = 0;
        setWWLightState(lightIndex);
      } 
    } else {    
      // no command     
//      printJsonForTemperatureAndHumidityToClient(clients[i],locationID);
    }

        
        
        // Empty the string for next time
        st[i] = "";

        // The flush that you had in your code - I'm not sure
        // why you want this, but here it is
//        clients[i]->flush();

        // If you want to disconnect the client here, then do this:
        clients[i]->stop();
        delete clients[i];
        clients[i] = NULL;
        delay(1);
        Serial.println("Client disconnected");
        Serial.println("");

      } else {
        Serial.print("Building request for client:");
        Serial.print(i);
        Serial.print(":");
        
        Serial.println(st[i]);
        // Add it to the string
        st[i].concat(newChar);
                // IMPORTANT: Nothing stops this from overrunning the string and
        //            trashing your memory. You SHOULD guard against this.
        //            But I'm not going to do all your work for you :-)
      }
    }
  }

}
@d-a-v
Copy link
Collaborator

d-a-v commented Mar 5, 2018

You can try to use the OOM debug option, call WiFiClient::flush() before ::stop() and for a sanity check, print a notice when your loop does not find a free client to hold the newcomer.
We currently have a max of 4 concurrent connections (lwip's MEMP_NUM_RAW_PCB). It means that the 5th will be waiting for one of the 4 to be closed before beeing accepted, but it won't be lost.
You may need to use tcpCleanup() too (check the faq).

@dannybres
Copy link
Author

What is meant by OOM debug?

your loop does not find a free client to hold the newcomer.

How do I know that there is a newcomer? If I have 4 connections open with atleast one more waiting and call
WiFiClient newClient = server.available();

will the newClient close one of the open connections and open a new one? Should I make sure I only call WiFiClient newClient = server.available(); if I have less than 4 connections open?

Thanks.

@d-a-v
Copy link
Collaborator

d-a-v commented Mar 5, 2018

I was wrong, I am currently running 12 simultaneous continuous tcp connections. I guess I need to understand the meaning of lwip's MEMP_NUM_*_PCB macros.

You get a newcomer if server.available() returns a valid client. It will not close your current active clients. I guess if lwip can't handle a new incoming client, then this client will wait until an already-opened one is closed.
I currently see nothing very bad with your sketch. Check your free heap, use OOM debug option, use tcpCleanup since you have frequent short connections. Your storing loop does nothing if there is no free index for newcomers.

@dannybres
Copy link
Author

dannybres commented Mar 5, 2018 via email

@d-a-v
Copy link
Collaborator

d-a-v commented Mar 5, 2018

OOM = Out Of Memory, it's an option in tools>debug level that you should use with tools>debug port>serial.
It will yell if malloc() returns null.

@devyte
Copy link
Collaborator

devyte commented Mar 6, 2018

This is not an issue in the core, but rather a "How do I..." type question.
Closing per issue POLICY doc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants