edit

Build Your Own UI Using Electron

pir

Introduction

This wiki explains how to build your own user interface using Electron. Electron is an open-source framework for creating native desktop applications (Windows, Mac, Linux) with web technologies like HTML, CSS and JavaScript. This means if you can build a website, you can build a desktop app!

By following the guide below, you will be able to create an application to control the GPIO pins on the reTerminal just by clicking buttons on the LCD. So let's get started!

Prepare Development Environment

On reTerminal

First, we will install Node.js along with npm on the reTerminal. npm is a package manager for Node.js packages.

  • Step 1. Access reTerminal using the onboard LCD, external display or SSH as explained here

  • Step 2. Upgrade the packages

sudo apt update
sudo apt full-upgrade 
  • Step 3. Download the script to install Node.js
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
  • Step 4. Install Node.js
sudo apt install -y nodejs

Node.js is now installed on the reTerminal. To check whether the installation is sucessful, run the following commands to check the version of Node.js and npm

node -v
npm -v

pir

On Host PC

Now, we will set up Microsoft Visual Studio Code for our development.

Note: Download the installer which is suitable for your operating system

  • Step 2. Click on the Extensions tab on the left navigation menu and type remote development in the search box

pir

  • Step 3. Select Remote Development and click Install

  • Step 4. Press F1 on the keyboard, type ssh and select Remote-SSH:Connect to Host...

pir

  • Step 5. Enter the following
pi@192.xxx.xx.xx

Note: pi is the username and 192.xxx.xx.xx is the IP address of the reTerminal

  • Step 6. Enter the password for the user

Now you have sucessfully logged in to reTerminal using SSH and have successfully finished preparing the development environment

Smart Light Electron Application

Now we will build a Smart Light Electron Appication that can be used to control the GPIO on the reTerminal by pressing buttons on the LCD. You can then connect relays to the GPIO and control home appliances as well!

Hardware Connections

We will connect an LED to the GPIO 24 of the reTerminal for testing purposes. Later you can add a relay and control home appliances using the GPIO!

pir

Note: A resistor is needed between the GPIO pin and the LED or otherwise the LED will burn out.

Create and Initialize the Application

  • Step 1. Open VSCode on the Host PC and login to reTerminal via SSH as explained before

  • Step 2. Navigate to File > Open Folder... and select a folder of your choice on the reTerminal

pir

  • Step 3. Create a new folder and name it

pir

pir

  • Step 4. Navigate to Terminal > New Terminal and navigate to the newly created folder

pir

Note: Here we use cd to change directory

  • Step 5. Type the following inside this terminal window to create a package.json file with the needed configurations for our Node.js app
npm init

Note: Keep pressing ENTER to use the defaults for the answers, but set the entry point: (index.js) as main.js and test command: as electron .(Use a space and a dot after electron)

pir

If you want to change the configurations later, you can visit the package.json file inside our main app folder

pir

  • Step 6. On the terminal inside VSCode, type the following to install Electron
npm install --save-dev electron

You will see the following output if Electron is successfully installed

pir

You will also see the node_modules folder generated with the required packages to run Electron

pir

Install onoff npm Module

The onoff npm module allows you to access and control the GPIO on the reTerminal using the Electron app

  • Step 1. Open a terminal window inside VSCode as explained before and navigate to our main app directory

  • Step 2. Type the following to install the onoff npm module

npm install onoff

Install and Run electron-rebuild npm Module

The electron-rebuild npm module rebuilds native Node.js modules against the version of Node.js that your Electron project is using. This allows you to use native Node.js modules in Electron apps without your system version of Node.js matching exactly (which is often not the case, and sometimes not even possible)

  • Step 1. Install electron-rebuild npm Module
npm install --save-dev electron-rebuild
  • Step 2. Run electron-rebuild
./node_modules/.bin/electron-rebuild

Note: Whenever you install a new npm package, rerun electron-rebuild

Create the HTML (Basic UI)

We will use the HTML file to create the basic user interface without any styling. This HTML file is responsible to display the UI elements on the screen.

Inside our main app directory, create a new file called index.html and copy the following codes

<!doctype html>
<html>
    <head>
        <!-- Specify the character encoding for the HTML document -->
        <meta charset="UTF-8">
        <!-- App title bar -->
        <title>Test Application</title>
        <!-- Load the material icons api -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <!-- Load the google fonts api -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
        <!-- Load style.css with UI styling -->
        <link rel="stylesheet" href="style.css">
        <!-- Embed  JavaScript code -->
        <script>
            // Load ipcRenderer module
            const { ipcRenderer } = require('electron');
            // Function to turn ON the GPIO on button press
            function buttonclick1()
            {
                // This will send 1 as a message to the main process with "msg" as the channel name
                ipcRenderer.send("msg1",1)
            }

            // Function to turn OFF the GPIO on button press
            function buttonclick2()
            {
                ipcRenderer.send("msg2",0)
            }

            // Function to close the app on button press
            function buttonclick3()
            {
                ipcRenderer.send("close-me")
            }
        </script>
    </head>
    <body>
        <!-- Application close button -->
        <button class="button3" onclick="buttonclick3()">X</button>
        <br>
        <!-- Application heading -->
        <h1>SMART LAMP</h1>
        <!-- Lightbulb icon from the material icons api -->
        <i class="material-icons">lightbulb</i>
        <br>
        <!-- Empty lines -->
        <br>
        <!-- ON button with class attribute for styling 
        and onclick attribute for button click event -->
        <button class="button1" onclick="buttonclick1()">ON</button>
        <br>
        <button class="button2" onclick="buttonclick2()">OFF</button>
    </body>
</html>

Note: The ipcRenderer module is an EventEmitter. It provides a few methods so you can send messages from the render process (web page - html/css) to the main process (main.js). You can also receive replies from the main process.

Now we have finished creating the HTML file.

Run npm test to see the following output

pir

Create the CSS (Styling)

We will use the CSS file to add different styling to the user interface created using the HTML file

Inside our main app directory, create a new file called style.css and copy the following codes

/* Styling for the body of the app */
body {
    background-color: rgb(141, 141, 141);
}

/* Styling for heading of the app */
h1 {
    font-size: 60px;
    text-align: center;
    font-family: "Roboto", "Courier New", monospace;
    color: rgb(255, 255, 255);
}

/* Styling for the light bulb */
.material-icons{
    font-size: 250px;
    color: rgb(204, 202, 71);
    display: flex;
    justify-content: center;
}

/* Styling for the ON button */
.button1 {
    display: inline-block;
    padding: 15px 25px;
    font-size: 35px;
    text-align: center;
    outline: none;
    color: rgb(255, 255, 255);
    background-color:rgb(76, 175, 80);
    border: none;
    border-radius: 15px;
    width: 20%;
    margin:auto;
    display:grid;
  }

/* Button press effect for ON button */
.button1:active {
  box-shadow: 0 5px rgb(104, 99, 99);
  transform: translateY(4px);
}

/* Hover effect for ON button */
.button1:hover {background-color: rgb(62, 142, 65)}

/* Styling for OFF button */
.button2 {
    display: inline-block;
    padding: 15px 25px;
    font-size: 35px;
    text-align: center;
    outline: none;
    color: rgb(255, 255, 255);
    background-color:rgb(207, 85, 85);
    border: none;
    border-radius: 15px;
    width: 20%;
    margin:auto;
    display:grid;
}

/* Button press effect for OFF button */
.button2:active {
  box-shadow: 0 5px rgb(104, 99, 99);
  transform: translateY(4px);
}

/* Hover effect for OFF button */  
.button2:hover {background-color: rgb(179, 44, 44)}

/* Styling for Close button */
.button3 {
  padding: 8px 25px;
  font-size: 20px;
  text-align: center;
  outline: none;
  color: rgb(255, 255, 255);
  background-color:rgb(179, 44, 44);
  border: none;
  width: 6%;
  margin:auto;
  display:grid;
  float: right;
}

The final output of the app will be as follows

pir

Create the Javascript (Load and Execute the App)

We will use the Javascript file to create the application window, display the load the HTML file, and add GPIO functionality.

Inside our main app directory, create a new file called main.js and copy the following codes

var Gpio = require('onoff').Gpio; //include onoff module to interact with the GPIO
var LED = new Gpio(24, 'out'); //initialize GPIO 24 as an output

// Include app, BrowserWindow and ipcMain module
const { app, BrowserWindow, ipcMain } = require('electron')
/* Use ipcMain module to receive the messages from the ipcRenderer module and 
turn ON the GPIO*/
ipcMain.on("msg1",(event,data)=>{
  LED.writeSync(data);
})

/* Use ipcMain module to receive the messages from the ipcRenderer module and 
turn OFF the GPIO */
ipcMain.on("msg2",(event,data)=>{
  LED.writeSync(data);
})

/* Use ipcMain module to receive the messages from the ipcRenderer module and 
close the app */
ipcMain.on("close-me", (event, arg) => {
    app.quit()
})

// Create application window
app.on('ready', function() {
    var mainWindow = new BrowserWindow({
        // Make the app fullscreen
        "fullscreen": true,
        webPreferences: {
            // enable the communication between the main and rendered process
            nodeIntegration: true,
            contextIsolation: false
          }
    });
    // Load the HTML page with CSS styling
    mainWindow.loadFile('index.html');
});

Note: The ipcMain provides a few methods so you can receive the messages sent fromt the renderer process (web page).

Test the App

You will see the output as soon as you save the previous file because we have turned on electron-rebuild. However if you have closed the app, you can open it again using npm test and see the following output

pir

Prepare a Script to Run the App

  • Step 1. Open the Root folder that we created before and create a new .sh file under that folder

pir

  • Step 2. Open the created file and enter the following
#!/bin/bash
cd $HOME/Desktop/testapp
DISPLAY=:0 npm test

Note: Here $HOME/Desktop/testapp is the location of the electron project

  • Step 3. Open a terminal window inside VSCode and navigate to the root directory of the app
example:
cd ~/Desktop/testapp
  • Step 4. Make the ledstart.sh an executable file
sudo chmod +x ledstart.sh

Prepare a Desktop File to Launch the App

  • Step 1. Open the Desktop folder and create a new .desktop file under that folder

pir

  • Step 2. Open the created file and enter the following
[Desktop Entry]
Encoding=UTF-8
Name=LED Test
Comment=IconTest Link
Exec=/home/pi/Desktop/testapp/ledStart.sh
Type=Application
Categories=Application;Development;

Note: Exec is the location of the script that we created before

  • Step 3. Open a terminal window inside VSCode and navigate to the Desktop
example:
cd ~/Desktop
  • Step 4. Make the ledStart.desktop an executable file
sudo chmod +x ledStart.desktop

Launch the App

Double click on the LED Test file on the Desktop of reTerminal LCD

You will see the output as follows

pir

Now you can click on the buttons and you will see the LED light up!

Debug the App

Let's go through the process of debugging your app while developing

  • Step 1. Log in to reTerminal via SSH using Microsoft Visual Studio Code as mentioned before

  • Step 2. Log in to reTerminal via SSH using a SSH application such as MobaXterm with X11 server feature

Note: X11 is needed to forward the reTerminal display and pop up on the PC

  • Step 3. After writing all the codes on Microsoft Visual Studio Code to the reTerminal, navigate to the main app directory and run the following
npm test

Finally you will see the output displayed on a new window. If there are any errors in the code, they will be dislpayed in the MobaXterm terminal window.

FAQ

Q: How to set up automatic updates to the app as we save it?

For this you can set up the Hot Reload feature using electron-reloader npm module

  • Step 1. Install electron-reloader
npm install --save-dev electron-reloader
  • Step 2. Add the following line at the end of main.js file
try {
  require('electron-reloader')(module)
} catch (_) {}

Now run the file once using npm test and the application will update as you save the files. You will not need to run npm test everytime you change the contents inside your project.

Bonus Demo

If you want to experience a more interesting demo with Electron, you can checkout this GitHub repo

pir

Resources

Tech Support

Please submit any technical issue into our forum.