1

I've set up an esp32 board (wemos d1 r32) to send a request to a server hosted on my local network. If I set up a very basic go server, the board handles the response fine (200 with the appropriate body content). Once I switch to a Node JS Server, it only responds with a 400 status code. Weirdest of all none of the console.log calls in the route handlers get called, which indicates that it quits before it even gets to the express server. I've tested that the server returns the body & status code I want using postman, all looks good.

I've tried different frameworks (Koa) to see if this is related to Express, but it doesn't work with that either. I assume its something to do with NodeJS?

Express server

import express from "express";

const app = express();
app.use(express.json());
app.post("/", (req, res) => {
  console.log(req.body);
  req.statusCode = 200;
  res.send("200");
});

app.get("/hello", (req, res) => {
  res.setTimeout(50000);
  console.log(req.body);
  res.setHeader("Location", "123");
  res.setHeader("Server", "123");
  req.statusCode = 200;
  res.send("200");
});

app.listen(8090, () => {
  console.log(`Example app listening on port ${8090}`);
});

Go server

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
    for name, headers := range w.Header() {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {
    fmt.Println("?")

    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}

Arduino script

#include <HTTPClient.h>
#include <WiFi.h>

// #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"

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

String webserver = "http://192.168.0.19:8090/hello";

String qrCode;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("Connected to AP successfully!");
}

void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  delay(1000);
  Serial.println("Disconnected from WiFi access point");
  Serial.print("WiFi lost connection. Reason: ");
  Serial.println(info.disconnected.reason);
  Serial.println("Trying to Reconnect");
  WiFi.begin(ssid, password);
  delay(5000);
}

void setup() {
  esp_log_level_set("*", ESP_LOG_VERBOSE);
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial.println("Hello, ESP32!");
  Serial.println(WiFi.getMode());
  Serial.println(WiFi.macAddress());

  WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
  WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);

  WiFi.begin(ssid, password);
}

int cnt = 0;

void loop() {
  while (Serial2.available()) {
    int  ch = Serial2.read();
    char character = ch;
    Serial.print(ch);

    // end char
    if(character == 13){
      Serial.print("\n");
      Serial.println(qrCode);
      Serial.println(cnt);
      qrCode = "";
      cnt = 0;
      send(qrCode);
    } else {
      cnt = ch + cnt;
      qrCode = qrCode + character;
    }
  }
}

void tryWifi() {
  WiFi.begin(ssid, password);
  int status = WiFi.status();
  while (status == 6 || status == 4 || status == 5 || status == 1) {
    Serial.print("Failed to connect:"); 
    Serial.print(status);
    Serial.print("\n");
    delay(1500);
    WiFi.begin(ssid, password);
  }
}

void send(String json) {
  HTTPClient http; 

  if(http.begin(webserver)){    

      int httpCode = http.GET();
      http.addHeader("Content-Type", "application/json");
      http.addHeader("Host", "something");
      // client.print("Content-Length: ");
      delay(1500);  
      
      Serial.println(httpCode);
      Serial.println(http.getString());
      if (httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();                 
          if (payload != "false"){
            Serial.println(payload); 
            delay(1500);  
          } else {
          Serial.println("Bad Playload");
          }
        } else {
          
        }
      } else {
      Serial.println("Error sending to HTTP");
      }
      http.end(); 
    } else {
      Serial.println("?");
    } 
}

2 Answers 2

3

Your Arduino code reads like this:

  int httpCode = http.GET();
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Host", "something");
  // client.print("Content-Length: ");
  delay(1500);  
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

The call http.GET() actually performs the HTTP request. You're adding headers after you perform the request. They need to be added before it. So your code should read:

  // http.addHeader("Content-Type", "application/json");
  int httpCode = http.GET();

  // delay(1500);   -- there's no need to call delay() here
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

HTTP status code 400 means "bad request". Some web servers will return this code if headers that they expect to see are missing or have non-sensical values. The Go and Javascript web servers are different implementations and may behave differently with malformed requests.

I've removed "Host" header as HTTPClient does this for you. And there's no need for the delay() call after calling http.GET().

Edit: I also commented out the "Content-Type" header as it makes no sense on a GET request, thanks to @EdgarBonet for mentioning that. I'm leaving it there as a comment to make it clear that that any headers should be added before the request is performed.

3
  • 1
    Note also that "Content-Type" does not make sense on an HTTP request. The OP probably means "Accept". Feb 26, 2023 at 10:42
  • @EdgarBonet Thanks, good point. It does actually make sense on a PUT or POST request that has a body, but not on a GET, which should never have a body. I've edited the answer. I suspect OP was just throwing in random lines of code to see if they fixed their problem.
    – romkey
    Feb 26, 2023 at 23:55
  • 1
    Thanks for the comments. I had the Content-Type header there as I intend to use a POST request w/ a json body. I've moved the call to set the header before the request is fired, nice catch!
    – Batzz
    Feb 27, 2023 at 16:12
1

I've figured out after an unreasonable amount of time that the request fails with a 400 as it doesn't have a slash at the end of the URL (http://192.168.1.208:8090 -> http://192.168.1.208:8090/). This must be something specific to NodeJS, as it works ok on a go server.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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