The ESP32 can provide the functionality of a Webserver. There are plenty of examples where this functionality is used to provide a GUI to control the Microcontroller or to provide information from it.

What all have in common: they is very basic and often plain ugly. Therefore the question is, how to run a modern Progressive JavaScript Frameworks together with an ESP32 in an easy way. Unfortunately it is out of the question to put the necessary files on the Microcontroller: there is just not enough space for this. So we need to define some alternative architecture:
– In a first thought one might be tempted to host the web application on a separate server and have a mechanism where the Microcontroller registers itself to it to make it’s address known. After that the web application will control the microcontroller with the help of REST calls.
– A alternative pattern that is also very easy to implement is to just pretend that the Microcontroller is hosting the application: The cool thing is, that the content does not really need to be located on the Microcontroller itself. It could be on Github or just anywhere on the Internet. We just need to have a web server running on the ESP32 where the requests to index.html are tunnelled to the physical location: This way all local REST calls automatically end up at the Microcontroller and we just need to handle them there. In order to improve the performance we can even avoid the tunnel for all other content and just use a 301 Moved Permanently return code. In the following chapters I will provide a small demo for this approach.

Vue.js

Vue.js is my favourite Progressive JavaScript Framework which is approachable, versatile and performant. A minimal Example Vue.js application is available on Github at https://pschatzmann.github.io/esp32_vue_example/vue-demo/dist/index.html

ESP32

I am using the Arduino IDE with the ESPAsyncWebServer ESP32 Library for the Server part. Here is the basic logic:

First we need to setup the Serial Interface and WIFI.

Serial.begin(115200);
//WiFi.mode(WIFI_STA);
WiFi.begin("your SSID", "your Password");

Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
}

We declare the server and path to the Vue.js index file (that has a size of 1458 bytes).

// Web Server & Services
AsyncWebServer server(80);
String indexPath = "/esp32_vue_example/vue-demo/dist/index.html";

Then we add some rewrites to make sure that the requests are ending at the right path

server.rewrite( "/", indexPath) ;
server.rewrite( "/index.html", indexPath);

We need to tunnel the request to the index. In order to reduce the complexity of the sketch, I have implemented the small helper class ClientRequestTunnel which just wrapps the HTTPClient.h.

// tunnel the index.html request
server.on(indexPath, HTTP_GET, [&](AsyncWebServerRequest *request){
ClientRequestTunnel tunnel;
if (tunnel.open("https://pschatzmann.github.io", request->url())) {
String result = tunnel.getString();
request->send(200, "text/html", result);
} else {
request->send(tunnel.getHttpCode());
}
});

All other requests can be redirected and do not need to go thru the Microcontroller. This consists of the Javascript files, the CSS and the icons:

server.on("/esp32_vue_example/*", HTTP_GET, [&](AsyncWebServerRequest *request){
String moved_url = "https://pschatzmann.github.io"+request->url();
request->redirect(moved_url);
});

We can add our application specific services that can be accessed in Vue with the help of Axios.

// Generic Services
server.on("/service/info", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncResponseStream *response = request->beginResponseStream("application/json");
StaticJsonDocument<200> doc;
doc["heap"] = ESP.getFreeHeap();
doc["ssid"] = WiFi.SSID();
serializeJson(doc, *response);
request->send(response);
});

Finally we can just start the server:

// start server
server.begin();

The complete Sketch can be found on Github.
When we deploy the sketch we will get the following output:

09:31:23.382 -> [D][ESPAsyncTunnel.cpp:8] ClientRequestTunnel(): Constructor!
09:31:23.483 -> [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 0 - WIFI_READY
09:31:23.483 -> [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 2 - STA_START
09:31:23.483 -> Connecting[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 4 - STA_CONNECTED
09:31:23.656 -> [D][WiFiGeneric.cpp:337] _eventCallback(): Event: 7 - STA_GOT_IP
09:31:23.656 -> [D][WiFiGeneric.cpp:381] _eventCallback(): STA IP: 192.168.1.43, MASK: 255.255.255.0, GW: 192.168.1.1
09:31:24.001 -> .
09:31:24.001 -> You can connect to 192.168.1.43

Now we can launch the application on the indicated address:

Summary

The described solution approach is very easy to implement, very memory efficient and provides the major advantage that changes to the web GUI can be deployed w/o any impact to the Microcontroller.

The complete source code for this demo project which contains the Vue and Arduino code can be found on Github.


3 Comments

Massimiliano · 9. November 2021 at 18:27

Never mind, got my answer by looking at the extra docs on your vue application.
https://pschatzmann.github.io/esp32_vue_example/vue-demo/dist/index.html#/

I will still try to fit it all into the ESP32. The main hurdle I see, is to properly set up the coding & building on my laptop then avoiding the huge amount of things that might go wrong when bringing it all to the ESP. Cheers!

    pschatzmann · 9. November 2021 at 18:35

    Another option is to use an SD drive to store the files.
    But I thought it is more convenient just to store the files on Github and serve them from there.

Massimiliano · 9. November 2021 at 18:14

Hi, I was looking for examples of ESP32 + Vue and found this post of yours. I am unclear as the whole of the tunneling – why do you need one? are you running some stuff on your non-esp32 server?

Myself, I am thinking how to make a nice web interface to my esp32 universal 433MHz Tx/Rx. About to choose whether to go for bootstrap + jquery or, say, Vuetify. Cheers!

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *