Introduction
In this tutorial we are going to learn how to render inja templates stored in the file system of the ESP32. We will be using the Arduino core to program the device.
On this previous tutorial, we have learned the basics on how to work with the inja library, a template rendering engine for C++. On this tutorial and the next ones, we have mostly worked with templates stored in memory, as strings. This works well for simple and small programs, but if we want to handle many different templates (for example, HTML templates in a web application), it may be a good alternative to store them in the file system of the ESP32.
For that effect, we may resort to the SPIFFS file system, which we also already covered on previous tutorials. Although in those tutorials we have mostly used specific ESP32 APIs to interact with the files, the standard C++ constructs also work, meaning that inja can access the template files without having to know the details about SPIFFS.
For reference, here is a list of tutorials that are used as base for better understanding what we are covering here:
- Inja template engine: an introduction to the inja library on the ESP32. We cover how to install it as an Arduino library and its basic features, such as placeholders, which we are using below.
- Getting started with the nlohmann/json library: an introduction on how to use the nlohmann/json library on the ESP32, including detailed installation instructions. Inja uses json objects from the nlohmann/json lib to hold the data to be used to render the templates.
- Writing a file to the SPIFFS file system: an introduction on SPIFFS on the ESP32 and how to write a file.
- Arduino IDE SPIFFS file upload plugin: an explanation on how to use the Arduino IDE SPIFFS plugin to directly upload files to the ESP32 file system (without the need to write any code to get the file). template
In this tutorial we are going to focus on the procedure to obtain the template from the file system. Consequently, we will not give much emphasis to the template features but rather use a simple template with two placeholders. If you are interested in more advanced features such as loops or conditionals, please follow this tutorial.
So, we will create a file called “template.txt” (you can name it differently, as long as you then use the correct name in the code) and place there the following template:
Hello {{ firstName }} {{ lastName }}.
Going quickly through the template, we can easily see that there are two placeholders: “firstName” and “lastName“. Our objective is to replace them with some first name and last name defined in our Arduino program, using inja.
Now, we need to upload this file to the SPIFFS file system of the ESP32. The easiest way is using the already mentioned Arduino IDE plugin. To sum up, we basically need to open the folder of our Arduino sketch (you can already create an empty sketch where we will later write our code) and create a folder called “data“.
Inside this folder, we must place the “template.txt” file. Then, going back to the Arduino IDE, we can need to click on “Tools” (on the top menu) and select the “ESP32 Sketch Date Upload” option. Naturally, the ESP32 must be connected to the computer and the correct COM port must have been previously selected.
After a successful completion of this procedure, the file should be on the root of the SPIFFS file system, with the same “template.txt” name.
The code
We will start our code by the library includes.
- SPIFFS.h: used to initialize the SPIFFS file system. Only this way we can have access to the file with the template.
- json.hpp: used to provide the data needed for the template in the format of a json object.
- inja.hpp: the template engine library.
Moving on to the Arduino setup, we will start by opening a serial connection. This way, we will be able to print the result of the template rendering.
Serial.begin(115200);
After that, we will initialize the SPIFFS file system, so we can later access its files. We do this with a call to the begin method on the SPIFFS extern variable (this variable becomes available from the SPIFFS.h include). As input we will be passing the Boolean value “true“, which indicates that the file system should be automatically formatted in case the mounting procedure fails.
The method call returns a Boolean indicating if the mounting procedure was successful (true) or not (false). We will use it for error checking, printing a message in case of error and returning, since we wouldn’t be able to proceed if the mounting fails.
if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); return; }
Then we will create a json object, which will contain the data that will be used to populate the placeholders of our template. We will call this object “data“.
nlohmann::json data;
Then we will add two fields to it: “firstName” and “lastName“. Note that they should be named exactly how we named the placeholders in the template. We will assign some arbitrary values for testing purposes.
data["firstName"] = "John"; data["lastName"] = "Smith";
After this we will create an object of class Environment, which will allow us to obtain the template from the file system. Although for the purpose of this tutorial we will use it exclusively for reading the template file, an environment can be used to set some additional configurations, as can be seen in the library documentation.
inja::Environment env;
Then, we will call the parse_template method, passing as input the full path to our template file. Recall that we have called the file “template.txt“. Also, from this previous tutorial, we know that the default root path when we initialize the SPIFFS file system is “/spiffs“. As such, the whole path should be:
/spiffs/template.txt
As output, this method will return an object of class Template, which we will store in a variable.
inja::Template temp = env.parse_template("/spiffs/template.txt");
Now that we have the parsed template, we will call the render method on our Environment object. As first input we pass our previously obtained Template and as second input we pass the json object with the data.
The result of this method call will be a std::string with the rendered template (in our case, the placeholders will be replaced by the data).
std::string result = env.render(temp, data);
Finally, we will print the result to the serial port. Since we have obtained a std::string and the Serial print methods don’t support this data type, we need to call the c_str method to obtain the corresponding C string representation.
Serial.println(result.c_str());
The full code is shown below.
#include <SPIFFS.h> #include <json.hpp> #include <inja.hpp> void setup() { Serial.begin(115200); if (!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); return; } nlohmann::json data; data["firstName"] = "John"; data["lastName"] = "Smith"; inja::Environment env; inja::Template temp = env.parse_template("/spiffs/template.txt"); std::string result = env.render(temp, data); Serial.println(result.c_str()); } void loop() {}
Testing the code
To test the code, simply compile it and upload it to the ESP32 using a tool of your choice (Arduino IDE or platformio). When the procedure finishes, open a serial monitor tool (I’ve used platformio’s serial monitor).
You should get a result similar to figure 1. As can be seen, we have obtained the rendered template with the first name and last name we defined in our code, as expected.