A web server in nanoFramework is a small HTTP service that runs inside the microcontroller to respond to external requests.
So far, our device has been “browsing” the Internet looking for data. But what if we want it to show us an interface?
Imagine being able to type your device’s IP in Chrome (http://192.168.1.50) and having a control panel appear to turn on lights, view the temperature, or most importantly, configure Wi-Fi credentials without having to recompile the code.
In this post, we will learn how to use the HttpListener class. It is the component that allows us to “listen” on port 80 and respond with HTML code when someone visits us.
For this tutorial, you need to have the NuGet package installed: nanoFramework.System.Net.Http.Server
What is HttpListener?
In the “big” .NET world (ASP.NET Core), web servers are complex and powerful. In nanoFramework, we have a lightweight version called HttpListener.
Here’s how it works:
- Constructor: we specify
httporhttpsand the port we want to listen on. - Start: we start the service.
- GetContext: we wait until a request arrives.
- Process: we read the request and send a response.
Creating a basic web server
Let’s create a server that simply says “Hello World” in HTML.
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Diagnostics;
// ... Make sure you're connected to Wi-Fi before calling this
public static void StartWebServer()
{
// 1. Create the HTTP listener on port 80
HttpListener listener = new HttpListener("http", 80);
listener.Start();
Debug.WriteLine("Web Server started. Waiting for connections...");
while (true)
{
try
{
// 2. Wait for a request (The code stops here until you visit with the browser)
HttpListenerContext context = listener.GetContext();
// 3. Get Request and Response objects
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
Debug.WriteLine($"Request received: {request.HttpMethod} {request.RawUrl}");
// 4. Prepare the HTML response
string responseHtml = "<html><body><h1>Hello from nanoFramework</h1><p>Running on ESP32</p></body></html>";
byte[] buffer = Encoding.UTF8.GetBytes(responseHtml);
// 5. Set headers
response.ContentLength64 = buffer.Length;
response.ContentType = "text/html";
// 6. Send data
response.OutputStream.Write(buffer, 0, buffer.Length);
// 7. Close the connection (Very important!)
response.OutputStream.Close();
}
catch (Exception ex)
{
Debug.WriteLine($"Server error: {ex.Message}");
}
}
}If you run this and enter your ESP32’s IP from your phone or computer, you will see your web page served from the chip!
Controlling an LED from the browser
A static web page is boring. We want buttons. Let’s add two links in the HTML that point to:
/on/off
Then, in our C# code, we will check the request.RawUrl property to see what the user requested and act on the GPIO.
// ... (Inside the while loop)
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
// Simple routing logic
string url = request.RawUrl; // Eg: "/on"
string statusMessage = "The LED is ???";
if (url.Contains("on"))
{
// Here we would turn on the GPIO (using the GpioController class seen in previous lessons)
// led.Write(PinValue.High);
statusMessage = "The LED is ON";
}
else if (url.Contains("off"))
{
// led.Write(PinValue.Low);
statusMessage = "The LED is OFF";
}
// Generate dynamic HTML
string html = "<html><head><style>button { padding: 20px; font-size: 20px; }</style></head>";
html += "<body><h1>Control Panel</h1>";
html += $"<p>Status: <strong>{statusMessage}</strong></p>";
html += "<a href='/on'><button>ON</button></a> ";
html += "<a href='/off'><button>OFF</button></a>";
html += "</body></html>";
// ... Send response as beforeConfiguring the network in access point mode
A very typical use case is the Captive Portal. When you buy a device, it doesn’t know your Wi-Fi. What it does is:
- Create its own Wi-Fi network (Access Point Mode).
- You connect to that network with your phone.
- The web server serves you a page with a form.
- You send (POST) the name and password of your home network.
- The device saves the data, reboots, and connects to your router.
To do this, we would need to configure the Wi-Fi in AP mode (which nanoFramework supports) and handle a POST request.
Receiving a form via POST
When you send an HTML form (<form method='POST'>), the data is not in the URL; it is in the body.
if (request.HttpMethod == "POST")
{
// Read the request body
System.IO.Stream body = request.InputStream;
System.IO.StreamReader reader = new System.IO.StreamReader(body, request.ContentEncoding);
string formData = reader.ReadToEnd();
// formData will be something like: "ssid=MyHouse&pass=1234"
Debug.WriteLine($"Data received: {formData}");
// You would then parse that string, save the configuration, and reboot.
}Server Limitations
HttpListener in a microcontroller is not designed to serve large images, heavy CSS, or support 50 users at once. It is for configuration and simple control.
If you need a complex website, it is better to host it in the cloud and have the ESP32 only send/receive JSON data.
Blocking or asynchronous execution
The previous example uses listener.GetContext(), which is blocking. While waiting for a visit, the thread stops.
If your device has to do other things (read sensors, blink LEDs) while waiting for web visits, you have two options:
- Run the web server in a separate Thread.
- Reorganize the application with multiple threads and synchronization mechanisms.
In the current nanoFramework API, HttpListener exposes GetContext() synchronously. Therefore, running the listener in a dedicated thread is the straightforward option if the device must handle other tasks.