Pug is a language to simplify the writing of HTML pages (official page of the project). Web technologies are currently in fashion. Yesterday confined to the creation of websites, it is now possible to use technologies from the Web to develop projects on computer, mini-PC ARM but also for the ESP8266. Writing HTML pages is not always easy because you have to manage the tags (and respect the tree at the time of closing). It quickly becomes a real headache when you need to edit a page and even more if you have to do it after several weeks or months, especially if you are not a web professional. In this tutorial, I propose to discover (rather quickly) the language Pug. You will also find this project on the internet under the name Jade. Jade had to change his name because of a problem of rights (surprising to be able to protect the name of a stone, finally !!!). All projects requiring a Web interface offered on DIY Projects will be written using this language.
Installation of Pug on ARM (Raspberry Pi or Orange Pi)
Pug is a Nodejs plugin. It will therefore have to be installed on your machine before going further. It is therefore possible to run it on all compatible platforms. For this tutorial, I used an Orange Pi + 2e running under Armbian (Ubuntu 16.04 LTS) but it will install without difficulty on Raspbian Pixel.
To check if Node.js and npm are installed on your system, run this command in a Terminal.
To install Node.js, you can follow this tutorial.
Warning. There is no need to uninstall Node.js if it is already present on your distribution. Other programs may no longer work.
Once Node.js and npm are installed, all you have to do is install pug and the pug-cli command-line tool that will be used to generate the HTML files. Run the following npm command:
Sudo npm install -g pug pug
Preparation of the environment
Pug therefore requires a compilation before getting the HTML file. This is the function of the pug-cli tool that converts the pug file into HTML. This can be done directly from the Terminal with the pug command. To know all the options of the command, just run pug –help .
To get an output HTML file, you must pass the -O option in the form of a JSON object the desired document type {“doctype”: “html”} followed by the source file. We must respect the double quotation marks.
Rather than invoking the compile command each time, you can automate this task. It is possible to use a task manager such as Grunt or Gulp but to start or small interface projects is a bit too complicated. What I propose here is to use the construction commands of Geany presented in this article.
If you work with Sublime Text or Textmate 2, there is a plugin that allows you to do the same thing (for a next article).
Launch Geany then go to the Build menu and open Set Construction Commands. In the first field, add a command, such as a pug. In the command field type pug -O ‘{“doctype”: “html”}’ -P ‘% f’ .
Several important remarks:
The% f parameter will be replaced with the file name at the time of the command call.
It is important to put% f in quotation marks, otherwise the command will crash if the file name contains one (or some) spaces.
The file will be generated next to the source file.
You must save the file before you can run the command
The file must have the extension pug
To call the command, simply make F8 or select the shortcut created in the Build menu.
Discover the language Pug (Jade)
Pug therefore aims to simplify the writing of HTML pages, it does very well and much more. We will discover the basics of Pug in this article, if you have more specific problems, you should find everything you need on the project site.
An example to start
Here is a quick example to begin with. We create a div in which we place a title (tag h1) and just below a list (ul) composed of 2 tags (li). No more tags management
PUG (Jade) | HTML | Result |
div h1 Demo PUG – Jade p Texte ul li Puce 1 li Puce 2 |
<div> <h1>Demo PUG – Jade <p>Texte</p> <ul> <li>Puce 1 </li> <li>Puce 2</li> </ul> </h1> </div> |
HTML tags, identifiers, classes, and attributes
All HTML tags are supported (a complete list). To add an identifier (parameter id), it is enough to make follow the tag by #idelement, for example p # p1 which will give
p#p1 | <p id=”p1″>Texte</p> |
To add a class (for example, a CSS style that allows you to define the color and size of a text), we will use .nameoftheclass, which will give
p#p1.oneclass | <p class=”oneclass” id=”p1″>Texte</p> |
We can also put all that in parentheses. If the resulting HTML file does not meet your expectations, that’s what you need to do. On the other hand, we lose some of Pug’s interest.
p(id=”p1″ class=”oneclasse”)
or p.oneclasse(id=”p1″) or p#p1(class=”oneclasse”) |
<p class=”oneclasse” id=”p1″>Texte</p> |
In the same way, we can stack the id and several classes
p#p1.class1.class2.class3 Texte | <p class=”class1 class2 class3″ id=”p1″>Texte</p> |
The principle remains the same whatever the attribute one wishes to pass.
From code (script) Javascript, jquery …
Pug also handles very well the inclusion of Javascript code (or jQuery). To do this, just add the script tag (). like this
script(). // code area $(‘#p1’).click(function(){ console.log(“click”); }); |
<script> // code area $(‘#p1’).click(function(){ console.log(“click”); }); </script> |
But we can also write javascript directly in the HTML.
Note. It is necessary to “paste” the sign = to the tag li to recover the value of the variable i in the label.
– var x = 5; ul – for (var i=1; i<=x; i++) { li= i + “. Hello” – } |
<ul> <li>1. Hello</li> <li>2. Hello</li> <li>3. Hello</li> <li>4. Hello</li> <li>5. Hello</li> </ul> |
Similarly, you can fill a list (or a list of choices) from the contents of a variable.
– var list_gpio = [“GPIO1”, “GPIO2”, “GPIO3”]; div h1 GPIO List for gpio in liste_gpio div.board h3= gpio |
<div> <h1>GPIO List <div class=”board”> <h2>GPIO1</h2> </div> <div class=”board”> <h2>GPIO2</h2> </div> <div class=”board”> <h2>GPIO3</h2> </div> </h1> </div> |
Disable code
An entire block can be commented upon. Attention Pug respects the hierarchy and will comment all the code of the lower level. As you can see, the PUG code is not converted to HTML.
//ul li Puce 1 li Puce 2 |
<!–ul li Puce 1 li Puce 2 –> |
HTML Conversion to PUG or PUG to HTML Online
If you have an HTML page to resume, you can use a converter. There are several on the internet. The best known is http://html2jade.org/ works fine. One can choose the type of indentation as well as the width of it.
Same goes the other way, Beautify Converter offers an online PUG to HTML converter. You can enter code directly or load it from a URL or file. As you can see, it’s no problem to use resources from a CDN. Here I loaded the Bootstrap framework.
Example on a real case
To finish this article, I propose to you to see what it gives on a concrete case. In the previous tutorial we saw how to create a web interface for our ESP8266 projects, we will use this code as the basis for building an ESP8266 project from HTML, CSS and Javascript files directly embedded in the SPIFFS memory area of the ESP8266 .
As a reminder, here is what the current interface looks like. It is entirely generated from the Arduino code in the form of a string published by the web server of the ESP8266.
Here is the equivalent Pug code
html(charset='UTF-8') head meta(http-equiv='refresh', content='60', name='viewport') script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js') script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js') link(href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/superhero/bootstrap.min.css', rel='stylesheet') title ESP8266 Demo - www.projetsdiy.fr body .container-fluid .row .col-md-12 h1 Demo Webserver ESP8266 + Bootstrap h3 Mini station météo ul.nav.nav-pills li.active a(href='#') span.badge.pull-right 24.20 | Température li a(href='#') span.badge.pull-right 38.00 | Humidité li a(href='#') span.badge.pull-right 971.68 | Pression atmosphérique table.table thead tr th Capteur th Mesure th Valeur th Valeur précédente tbody tr td DHT22 td Température td 24.20°C td - tr.active td DHT22 td Humidité td 38.00% td - tr td BMP180 td Pression atmosphérique td 971.68mbar td - h3 GPIO .row .col-md-4 h4.text-left | D5 span.badge OFF .col-md-4 form(action='/', method='POST') button.btn.btn-success.btn-lg(type='button submit', name='D5', value='1') ON .col-md-4 form(action='/', method='POST') button.btn.btn-danger.btn-lg(type='button submit', name='D5', value='0') OFF .col-md-4 h4.text-left | D6 span.badge OFF .col-md-4 form(action='/', method='POST') button.btn.btn-success.btn-lg(type='button submit', name='D6', value='1') ON .col-md-4 form(action='/', method='POST') button.btn.btn-danger.btn-lg(type='button submit', name='D6', value='0') OFF .col-md-4 h4.text-left | D7 span.badge OFF .col-md-4 form(action='/', method='POST') button.btn.btn-success.btn-lg(type='button submit', name='D7', value='1') ON .col-md-4 form(action='/', method='POST') button.btn.btn-danger.btn-lg(type='button submit', name='D7', value='0') OFF .col-md-4 h4.text-left | D8 span.badge OFF .col-md-4 form(action='/', method='POST') button.btn.btn-success.btn-lg(type='button submit', name='D8', value='1') ON .col-md-4 form(action='/', method='POST') button.btn.btn-danger.btn-lg(type='button submit', name='D8', value='0') OFF .row .col-md-4 form#selecttheme(method='POST', name='selecttheme') input#choixtheme.span(name='theme', type='hidden') .btn-group button.btn.btn-default Theme : superhero button.btn.btn-default.dropdown-toggle(data-toggle='dropdown') span.caret ul.dropdown-menu li(onclick='$("#choixtheme").val("bootstrap"); $("#selecttheme").submit()') a(href='#') Boostrap li(onclick='$("#choixtheme").val("cerulean"); $("#selecttheme").submit()') a(href='#') Cerulean li(onclick='$("#choixtheme").val("cosmo"); $("#selecttheme").submit()') a(href='#') Cosmo li(onclick='$("#choixtheme").val("cyborg"); $("#selecttheme").submit()') a(href='#') Cyborg li(onclick='$("#choixtheme").val("darkly"); $("#selecttheme").submit()') a(href='#') Darkly li(onclick='$("#choixtheme").val("flatly"); $("#selecttheme").submit()') a(href='#') Flatly li(onclick='$("#choixtheme").val("journal"); $("#selecttheme").submit()') a(href='#') Journal li(onclick='$("#choixtheme").val("lumen"); $("#selecttheme").submit()') a(href='#') Lumen li(onclick='$("#choixtheme").val("paper"); $("#selecttheme").submit()') a(href='#') Paper li(onclick='$("#choixtheme").val("readable"); $("#selecttheme").submit()') a(href='#') Readable li(onclick='$("#choixtheme").val("sandstone"); $("#selecttheme").submit()') a(href='#') Sandstone li(onclick='$("#choixtheme").val("simplex"); $("#selecttheme").submit()') a(href='#') Simplex li(onclick='$("#choixtheme").val("slate"); $("#selecttheme").submit()') a(href='#') Slate li(onclick='$("#choixtheme").val("spacelab"); $("#selecttheme").submit()') a(href='#') Spacelab li(onclick='$("#choixtheme").val("superhero"); $("#selecttheme").submit()') a(href='#') Superhero li(onclick='$("#choixtheme").val("united"); $("#selecttheme").submit()') a(href='#') United li(onclick='$("#choixtheme").val("yeti"); $("#selecttheme").submit()') a(href='#') Yeti .col-md-8 p a(href='http://www.projetsdiy.fr') Version francaise : www.projetsdiy.fr p a(href='https://www.diyprojects.io') English version : www.diyprojects.io
Paste this code into a new document on Geany and save it. Then press F8 to generate the HTML file.
Here is the compiled HTML code. You can open it in a browser. All the resources are recovered from the Internet, you will get the same rendering as in the image above but no interaction is still possible!
<!DOCTYPE html> <html charset="UTF-8"> <head> <meta http-equiv="refresh" content="60" name="viewport"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/superhero/bootstrap.min.css" rel="stylesheet"> <title>ESP8266 Demo - www.projetsdiy.fr</title> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-12"> <h1>Demo Webserver ESP8266 + Bootstrap</h1> <h3>Mini station météo</h3> <ul class="nav nav-pills"> <li class="active"><a href="#"><span class="badge pull-right">24.20</span> Température</a></li> <li><a href="#"><span class="badge pull-right">38.00</span> Humidité</a></li> <li><a href="#"><span class="badge pull-right">971.68</span> Pression atmosphérique</a></li> </ul> <table class="table"> <thead> <tr> <th>Capteur</th> <th>Mesure</th> <th>Valeur</th> <th>Valeur précédente</th> </tr> </thead> <tbody> <tr> <td>DHT22</td> <td>Température</td> <td>24.20°C</td> <td>-</td> </tr> <tr class="active"> <td>DHT22</td> <td>Humidité</td> <td>38.00%</td> <td>-</td> </tr> <tr> <td>BMP180</td> <td>Pression atmosphérique</td> <td>971.68mbar</td> <td>-</td> </tr> </tbody> </table> <h3>GPIO</h3> <div class="row"> <div class="col-md-4"> <h4 class="text-left">D5 <span class="badge">OFF</span></h4> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-success btn-lg" type="button submit" name="D5" value="1">ON</button> </form> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-danger btn-lg" type="button submit" name="D5" value="0">OFF</button> </form> </div> <div class="col-md-4"> <h4 class="text-left">D6 <span class="badge">OFF</span></h4> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-success btn-lg" type="button submit" name="D6" value="1">ON</button> </form> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-danger btn-lg" type="button submit" name="D6" value="0">OFF</button> </form> </div> <div class="col-md-4"> <h4 class="text-left">D7 <span class="badge">OFF</span></h4> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-success btn-lg" type="button submit" name="D7" value="1">ON</button> </form> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-danger btn-lg" type="button submit" name="D7" value="0">OFF</button> </form> </div> <div class="col-md-4"> <h4 class="text-left">D8 <span class="badge">OFF</span></h4> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-success btn-lg" type="button submit" name="D8" value="1">ON</button> </form> </div> <div class="col-md-4"> <form action="/" method="POST"> <button class="btn btn-danger btn-lg" type="button submit" name="D8" value="0">OFF</button> </form> </div> </div> <div class="row"> <div class="col-md-4"> <form id="selecttheme" method="POST" name="selecttheme"> <input class="span" id="choixtheme" name="theme" type="hidden"> <div class="btn-group"> <button class="btn btn-default">Theme : superhero</button> <button class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> <ul class="dropdown-menu"> <li onclick="$("#choixtheme").val("bootstrap"); $("#selecttheme").submit()"><a href="#">Boostrap</a></li> <li onclick="$("#choixtheme").val("cerulean"); $("#selecttheme").submit()"><a href="#">Cerulean</a></li> <li onclick="$("#choixtheme").val("cosmo"); $("#selecttheme").submit()"><a href="#">Cosmo</a></li> <li onclick="$("#choixtheme").val("cyborg"); $("#selecttheme").submit()"><a href="#">Cyborg</a></li> <li onclick="$("#choixtheme").val("darkly"); $("#selecttheme").submit()"><a href="#">Darkly</a></li> <li onclick="$("#choixtheme").val("flatly"); $("#selecttheme").submit()"><a href="#">Flatly</a></li> <li onclick="$("#choixtheme").val("journal"); $("#selecttheme").submit()"><a href="#">Journal</a></li> <li onclick="$("#choixtheme").val("lumen"); $("#selecttheme").submit()"><a href="#">Lumen</a></li> <li onclick="$("#choixtheme").val("paper"); $("#selecttheme").submit()"><a href="#">Paper</a></li> <li onclick="$("#choixtheme").val("readable"); $("#selecttheme").submit()"><a href="#">Readable</a></li> <li onclick="$("#choixtheme").val("sandstone"); $("#selecttheme").submit()"><a href="#">Sandstone</a></li> <li onclick="$("#choixtheme").val("simplex"); $("#selecttheme").submit()"><a href="#">Simplex</a></li> <li onclick="$("#choixtheme").val("slate"); $("#selecttheme").submit()"><a href="#">Slate</a></li> <li onclick="$("#choixtheme").val("spacelab"); $("#selecttheme").submit()"><a href="#">Spacelab</a></li> <li onclick="$("#choixtheme").val("superhero"); $("#selecttheme").submit()"><a href="#">Superhero</a></li> <li onclick="$("#choixtheme").val("united"); $("#selecttheme").submit()"><a href="#">United</a></li> <li onclick="$("#choixtheme").val("yeti"); $("#selecttheme").submit()"><a href="#">Yeti</a></li> </ul> </div> </form> </div> <div class="col-md-8"> <p><a href="http://www.projetsdiy.fr">Version francaise : www.projetsdiy.fr</a></p> <p><a href="https://www.diyprojects.io">English version : www.diyprojects.io</a></p> </div> </div> </div> </div> </div> </body> </html>
So, what do you prefer? In the next tutorial, we’ll see how to use this code to create a Web interface and interact with Arduino code.
- Get Started with HC-SR04, measure distance by ultrasound. Arduino code example
- ESP32, GPIO pins and associated functions. I/O, PWM, RTC, I2C, SPI, ADC, DAC
- ESP32-CAM pinout and equipments. ESP-EYE, AI Thinker, TTGO T-Camera, M5Stack Timer Camera …
- ESP32-CAM. Which model to choose? ESP-EYE, AI Thinker, TTGO T-Camera, M5Stack Timer Camera …
- M5Stack Atomic GPS. ESP32 TinyGPS++ tracker, GPX export on SD card, visualization on Google Maps or VSCode
- Home Assistant. Install the snap on Synology NAS on an Ubuntu Virtual Machine