Search code examples
arduinoesp32arduino-esp32

What does an HTTP response code of -5 or -2 mean?


i am writing a code that connects an ESP32 to an Arduino Nano 33 IoT. The ESP32 is acting as some sort of bridge between the client and an Arduino Nano 33 IoT as a server. Both devices are connected to a local network. The ESP32 is acting as both client and server. That means it spits out its static IP address and you can connect to it and then through an HTML web page on that IP address, you can control 4 LEDs connected to the Arduino Nano 33 IoT. The requests will be sent with the HTTP method to the Arduino Nano 33 and the Arduino will turn the LEDs on and off. The ESP32 will then wait for the response code and when it comes, it shows it in the serial monitor. Now I have two problems:

  1. When I make a request, especially at the beginning when the code gets uploaded, it will be executed without any problem, but after that when I want to turn an LED off, often I get a -5 response code and sometimes even -2 (which represents an error in the code) and the LEDs won't turn off.
  2. Even when an LED turns on successfully, I don't get 200 as the response code.

I would appreciate it if someone can help me understand, what these negative response codes are and how can i fix them. You can find the codes I have written in VSCode for both ESP32 and Arduino below.

Thanks, Ali

P.S.: I'm relatively new to the topic and might need some basic explanations as well :)

ESP32:

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>

IPAddress ip(192, 168, 0, 32);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
IPAddress nano(192, 168, 0, 33);
AsyncWebServer server(80);
WiFiClient client;
HTTPClient http;

String redState = "";
String greenState = "";
String blueState = "";
String whiteState = "";
String fadeState = "";

uint8_t ledStatus[5];

String processor(const String &var)
{
  if(var == "rState"){
    if(ledStatus[1] == 1){
      redState = "ON";
    }
    else
    {
      redState = "OFF";
    }
    return redState;
  }

  if(var == "gState"){
      if(ledStatus[2] == 1){
      greenState = "ON";
      }
      else
      {
        greenState = "OFF";
      }
      return greenState;
  }

  if(var == "bState"){
      if(ledStatus[3] == 1){
      blueState = "ON";
      }
      else
      {
        blueState = "OFF";
      }
      return blueState;
  }

  if(var == "wState"){
      if(ledStatus[4] == 1){
      whiteState = "ON";
      }
      else
      {
        whiteState = "OFF";
      }
      return whiteState;
  }

  if(var == "fadeState"){
      if(ledStatus[5] == 1){
      fadeState = "ON";
      }
      else
      {
        fadeState = "OFF";
      }
      return fadeState;
  }
  return String();
}

void httpGETRequest(const char* serverName) {
    
  // Your Domain name with URL path or IP address with path
  http.begin(client, serverName);
}

String httpGETResponse(int a, bool status) {
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  
  String payload = "--"; 
  
  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
    if (httpResponseCode == 200)
    {
      ledStatus[a] = status;
    }
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

const char* ssid = "opdeckenlicht";
const char* password = "opdeckenlicht";

const char* nano33 = "http://192.168.0.32/";
const char* reconnect = "http://192.168.0.32/reconnect";
const char* redLEDon = "http://192.168.0.32/r/On";
const char* redLEDoff = "http://192.168.0.32/r/Off";
const char* greenLEDon = "http://192.168.0.32/g/On";
const char* greenLEDoff = "http://192.168.0.32/g/Off";
const char* blueLEDon = "http://192.168.0.32/b/On";
const char* blueLEDoff = "http://192.168.0.32/b/Off";    
const char* whiteLEDon = "http://192.168.0.32/w/On";
const char* whiteLEDoff = "http://192.168.0.32/w/Off";
const char* fadeLEDon = "http://192.168.0.32/fade/on";
const char* fadeLEDoff = "http://192.168.0.32/fade/off";

void setup() {
  Serial.begin(9600);

  WiFi.config(ip, gateway, subnet);

  Serial.println("");
  Serial.print("Connecting to ");
  Serial.print(ssid);
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(" .");
  }
  
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  
  // Initialize SPIFFS
  if(!SPIFFS.begin(true)){
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
  }
}

void loop() {
  
  server.begin();
  client.connect(nano, 80);
  http.begin(nano33);

  // Route to load style.css file
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/r/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/g/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/b/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/w/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/fade/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/style.css", "text/css");
  });


  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  Serial.println("Client connected.");
  Serial.println("");
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/reconnect", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(reconnect);
  delay(1000);
  client.connect(nano, 80);
  http.begin(nano33);
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  // Red LED
  server.on("/r/on", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(redLEDon);
  delay(5);
  httpGETResponse(1, 1);
  // ledStatus[1] = 1;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/r/off", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(redLEDoff);
  delay(5);
  httpGETResponse(1, 0);
  // ledStatus[1] = 0;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });



  // Green LED
  server.on("/g/on", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(greenLEDon);
  delay(5);
  httpGETResponse(2, 1);
  // ledStatus[2] = 1;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/g/off", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(greenLEDoff);
  delay(5);
  httpGETResponse(2, 0);
  // ledStatus[2] = 0;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });



  // Blue LED
  server.on("/b/on", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(blueLEDon);
  delay(5);
  httpGETResponse(3, 1);
  // ledStatus[3] = 1;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/b/off", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(blueLEDoff);
  delay(5);
  httpGETResponse(3, 0);
  // ledStatus[3] = 0;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });



  // White LED
  server.on("/w/on", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(whiteLEDon);
  delay(5);
  httpGETResponse(4, 1);
  // ledStatus[4] = 1;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/w/off", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(whiteLEDoff);
  delay(5);
  httpGETResponse(4, 0);
  // ledStatus[4] = 0;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });



  // Fade Mode
  server.on("/fade/on", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(fadeLEDon);
  delay(3000);
  httpGETResponse(5, 1);
  // ledStatus[5] = 1;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/fade/off", HTTP_GET, [](AsyncWebServerRequest *request){
  httpGETRequest(fadeLEDoff);
  delay(3000);
  httpGETResponse(5, 0);
  // ledStatus[5] = 0;
  request->send(SPIFFS, "/index.html", String(), false, processor);
  });  
}

Arduino Nano 33:

#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include "WebServer.h"

IPAddress  ip(192, 168, 0, 33);
IPAddress  esp(192, 168, 0, 32);
WiFiServer server(80);
WiFiClient client;

Lightcontroller leds;
Luefter         x;
Tempsensor      core, driver;
VController     vcont;

WebServer::WebServer()
{
  ssid = "opdeckenlicht";
  password = "opdeckenlicht";
  status = WL_IDLE_STATUS;
}

void WebServer::setup()
{
  WiFi.config(ip);

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);                   // print the network name (SSID);

    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, password);
    // wait 5 seconds for connection:
    delay(5000);
  }

  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print where to go in a browser:
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);

  server.begin();                           // start the web server on port 80
}

void WebServer::begin()
{
  leds.setup();
  x.setup();
  WebServer::setup();
}

void WebServer::run()
{
  // put your main code here, to run repeatedly:
  client = server.available();   // listen for incoming clients
  
  if (client) {                             // if you get a client,
    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

                // Check to see what the client request was:
        if (currentLine.endsWith("GET /reconnect")) {
          status = WiFi.begin(ssid, password);
          server.begin();
          Serial.println("Reconnected to: ");
          Serial.print(ssid);
        }
        if (currentLine.endsWith("GET /r/On")) {
          leds.control(2,255);
          Serial.println("Red LED on.");
        }
        if (currentLine.endsWith("GET /r/Off")) {
          leds.control(2,0);
          Serial.println("Red LED off.");     
        }
        if (currentLine.endsWith("GET /g/On")) {
          leds.control(3,255);
          Serial.println("Green LED on.");
        }
        if (currentLine.endsWith("GET /g/Off")) {
          leds.control(3,0);
          Serial.println("Green LED off.");     
        }
        if (currentLine.endsWith("GET /b/On")) {
          leds.control(5,255);
          Serial.println("Blue LED on.");
        }
        if (currentLine.endsWith("GET /b/Off")) {
          leds.control(5,0);
          Serial.println("Blue LED off.");     
        }
        if (currentLine.endsWith("GET /w/On")) {
          leds.control(6,255);
          Serial.println("White LED on.");
        }
        if (currentLine.endsWith("GET /w/Off")) {
          leds.control(6,0);
          Serial.println("White LED off.");     
        }
        if (currentLine.endsWith("GET /fade/on")) {
          leds.fade(1);
          Serial.println("Fade mode on.");
        }
        if (currentLine.endsWith("GET /fade/off")) {
          leds.fade(0);
          Serial.println("Fade LED off.");     
        }

        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:
            Serial.println("Executing task...");

            
            // 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
        }
      }
    }
    // close the connection:
    // client.stop();
    // Serial.println("client disconnected");
  }
}

Solution

  • Negative returns from the ESP32 HTTPClient library indicate an error while attempting to communicate with the server, rather than an HTTP response code.

    The library is open source, so you can find its source code online and see what the return values indicate. -2 means HTTPC_ERROR_SEND_HEADER_FAILED, -5 means HTTPC_ERROR_CONNECTION_LOST. The gist of it is that the network connection to the web server is unstable and unexpectedly goes away.

    In this case, you're using the ESPAsyncWebServer. Its README is long but contains critical information, particularly in the "Important Things To Remember" section:

    You can not use yield or delay or any function that uses them inside the callbacks

    You directly call delay() in callback handlers, which is disallowed.

    This section should spell it out more clearly, but this means it is not safe to re-enter the network stack - it is not safe to call TCP-related functions (including HTTPClient) from a callback. Unfortunately you call HTTPClient functions extensively from web server callbacks, which is why they're failing.

    In general for web server callbacks (not just the async library ones), requests should be handled in a short, determinate amount of time. If you need to run a function that can take a long time, you should start that outside the callback. If you need to report its results to the browser accessing the web server you should do that asynchronously.

    It might be sufficient to eliminate the delay() calls, but it's still unsafe to call HTTPClient from the async web server callbacks, and it's still a problem to call potentially long running functions while responding to a web server request.

    The best approach is to rewrite your code to perform the HTTPClient calls from outside the web server callbacks and to not call delay() in them. One way to do this is to provide state variables which the callbacks set and which the loop() code inspects to decide whether to do the HTTPClient work.

    For instance, something like this would move the code out of the callback:

    boolean turn_red_len_on = false;
    
    // Red LED
    server.on("/r/on", HTTP_GET, [](AsyncWebServerRequest *request) {
      turn_red_led_on = true;
      request->send(SPIFFS, "/index.html", String(), false, processor);
    });
    
    void loop() {
      if(turn_red_len_on) {
        httpGETRequest(redLEDon);
        delay(5);
        httpGETResponse(1, 1);
        turn_red_led_on = false;
      }
    }
    

    You'd need to restructure your code similarly to the example I provided, and do this for each callback that needs to use an HTTPClient.

    As an alternative, you could switch to the WebServer library that's part of the Arduino framework for the ESP32. This loses many of the advanced features of the async web server but will be more resilient with code that misbehaves by running long running functions in the callbacks.

    Perhaps it would be simpler just to put the LEDs on the ESP32?