esp32-net6-http

How to connect an ESP32 with NET6 via HTTP

  • 5 min

Continuing with our series of posts on how to connect an ESP32 device with a .NET application, in this post we will see how to perform communication using HTTP calls.

In our previous post, we saw how to connect an ESP32 with a NET6 application, using the serial port. In this post, we will leave the cable behind and go wireless via a Wi-Fi connection.

NET6 can be used on a variety of platforms, such as Windows, Android, Linux, Mac, x86/x64, and ARM7 or higher. This cross-platform capability means we can run the same program on different devices, such as Windows or Linux desktop computers, Android mobile phones, or Raspberry Pi, without needing to make changes to the code.

As in the previous post, we will use an M5 Stack, but any ESP32 or Arduino with Wi-Fi will work for you to follow the steps of this tutorial.

We will start with one of the simplest Wi-Fi communications: communication via an HTTP request. We have already seen HTTP communication in the ESP32 section, and as we have already mentioned, there is no one communication method that is better than another; all have their advantages and disadvantages.

In the case of HTTP communication, we are dealing with a client-server type of communication, where a server offers certain services to which one or more clients connect. The communication is not bidirectional, as the client initiates the communication; the server cannot initiate communication with the client.

Communication via HTTP requests is useful, for example, when we have a large number of clients connecting to a single server. However, the communication is not , and the server is easily locatable, meaning it is easy to obtain its IP (directly or indirectly).

We will differentiate between the case where the ESP32 acts as the server and the computer with .NET acts as the server.

ESP32 as Server, NET6 as Client

ESP32 Code

In this first case, the ESP32 acts as the server. So we need to start a server, something we already know how to do perfectly. As always, we split the code into files, grouping features. So we have the server.hpp file with all the related functionalities.


#pragma once

WebServer server(80);

void handleNotFound() 
{
   server.send(404, "text/plain", "Not found");
}

void InitServer()
{
   server.on("/A", []()
   {
      server.send(200, "text/plain", "A works");
      Serial.println("A");
   });

   server.on("/B", []()
   {
      server.send(200, "text/plain", "B works");
      Serial.println("B");
   });
 
   server.onNotFound(handleNotFound);
 
   server.begin();
   Serial.println("HTTP server started");
}
Copied!

On the other hand, in the setup function we simply initialize the WiFi and the server,

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

  ConnectWiFi_STA();
 
  InitServer();
}
Copied!

And finally in the loop we simply call the function that manages the server,

void loop()
{
  M5.update();

  server.handleClient();
  delay(100);
}
Copied!

NET6 Code

As for the .NET part, we need to make an Http request to the ESP32. There are several ways to make an Http request from C#, but one of the simplest is the following,

using System.Diagnostics;
using System.Reactive.Linq;

var isOn = false;
var timer = Observable.Interval(TimeSpan.FromSeconds(2))
.Subscribe(async _ =>
{
    isOn = !isOn;
    var url = @$"http://192.168.1.xxx/{(isOn ? "A" : "B")}";

    var response = await new HttpClient().GetAsync(url);
    var data = await response.Content.ReadAsStringAsync();
    Console.WriteLine(data);
});

Console.ReadLine();
Copied!

NET32 as Server, ESP32 as Client.

ESP32 Code

In this case, the ESP32 has to make a request to the .NET backend. So we simply have to use an http client. We have created an API.h file that groups these functions.

void processResponse(int httpCode, HTTPClient& http)
{
  if (httpCode > 0) {
    Serial.printf("Response code: %d\t", httpCode);

    if (httpCode == HTTP_CODE_OK) {
      String payload = http.getString();
      Serial.println(payload);
    }
  }
  else {
    Serial.printf("Request failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();
}

void SendEndpointA()
{
  HTTPClient http;
  http.begin(ApiHost + "/API/EndpointA");
  int httpCode = http.GET();
  processResponse(httpCode, http);
}

void SendEndpointB()
{
  HTTPClient http;
  http.begin(ApiHost + "/API/EndpointB");
  int httpCode = http.GET();
  processResponse(httpCode, http);
}
Copied!

In this case, in setup we only need to initialize the WiFi, as there is no server to initialize,

void setup() {
  Serial.begin(115200);
  WIFI_Connect();
}
Copied!
bool isOn = false;
void Update()
{
  if (M5.BtnA.wasPressed())
  {
    if (isOn == false)
    {
      isOn = true;
      Serial.println("A");
      SendEndpointB();
    }
    else
    {
      isOn = false;
      Serial.println("B");
      SendEndpointA();
    }
  }
}

void loop()
{
  M5.update();

  delay(200);

  Update();
}
Copied!

NET6 Code

On the .NET backend side, we will start a server that listens for Http requests. There are several ways to do it, but one of the simplest is that I’m going to give you a file where we have defined a simple WebServer. You can use this file in your programs whenever you need to listen for Http requests in a simple way.

public class WebServer
{
    private readonly HttpListener _listener = new HttpListener();
    private readonly Func<httplistenerrequest, string=""> _responderMethod;

    public WebServer(Func<httplistenerrequest, string=""> method, params string[] prefixes)          
    {
        foreach (string s in prefixes) _listener.Prefixes.Add(s);

        _responderMethod = method;
        _listener.Start();
    }

    public void Run()
    {
        ThreadPool.QueueUserWorkItem((o) =>
        {
            while (_listener.IsListening)
            {
                ThreadPool.QueueUserWorkItem((c) =>
                {
                    var ctx = c as HttpListenerContext;
                        string rstr = _responderMethod(ctx.Request);
                        SendResponse(ctx, rstr);
                        ctx.Response.OutputStream.Close();
                }, _listener.GetContext());
            }   
        });
    }

    private static void SendResponse(HttpListenerContext ctx, string rstr)
    {
        ctx.Response.ContentType = "text/html";
        ctx.Response.ContentEncoding = Encoding.Unicode;

        byte[] buf = Encoding.Unicode.GetBytes(rstr);
        ctx.Response.ContentLength64 = buf.Length;
        ctx.Response.OutputStream.Write(buf, 0, buf.Length);
    }

    public void Stop()
    {
        _listener.Stop();
        _listener.Close();
    }
}
Copied!

With this file, making our .NET backend to listen for the requests that the ESP32 will send is very simple. The code, for example, would be as follows,

InitWebServer();

Console.ReadLine();

int port;
string[] prefix;
WebServer webServer;

void InitWebServer()
{
    port = 80;
    prefix = new string[] { $"http://*/API/" };
    webServer = new WebServer(ProcessRequest, prefix);
    webServer.Run();
}

string ProcessRequest(HttpListenerRequest request)
{
    var endpoint = request.Url.LocalPath;

    if (endpoint == "/API/EndpointA")
    {
        Console.WriteLine("EndpointA");
        return "";
    }

    if (endpoint == "/API/EndpointB")
    {
        Console.WriteLine("EndpointA");
        return "";
    }

    return "Invalid address";
}
Copied!