A status LCD for Opnsense using LCDproc and an Arduino

Following the deprecation of IPSec in favour of better protocols like OpenVPN from various vpn providers, I have recently had to move away from Mikrotik as the firewall solution of choice. A couple of years back I had used Opnsense for one of my clients, and was quite impressed with the capabilities and performance of the software, however, having another full blown pc, running 24 x 7 in the house was something that always held me back from using it at home.

Recently however, Deciso have started shipping their own hardware, and some manufacturers like Protectli offer hardware solutions with a very small footprint, but with enough grunt to handle almost anything you can throw at them.

Having migrated all the rules and vlans from the Mikrotik to Opnsense, after a few days without issues, it was time to change something or other … it was working as it should, so it was getting boring.

I decided to add a status lcd to the unit. Surprisingly, even though the documentation is not lacking, I could not find a one stop shop solution, and had to cobble together various pieces scattered around the web. Once I got it up and running, I decided to write this walkthrough which hopefully will save someone some time and headaches. You will need some basic soldering skills, (although technically you could build everything using jumper wires. The first prototype was indeed built this way as seen in the picture below using and arduino uno). You will also need some basic knowledge on how to compile and upload stuff to Arduino.

The Arduino and the LCD

After looking around in my junk box, I found a 20 x 4 HD 44780 LCD and an Arduino nano. The job of the Arduino here is to offer a bridge or rather an interface, between the LCDProc daemon and the LCD. In the past, these lcd’s used to be connected straight to the parallel port of the pc, however nowadays parallel ports are extremely rare to find, so the best way forward is to use a serial port. The Arduino has a built in usb to serial interface, which is used to upload firmware to the cpu. The same usb to serial interface can be used as a standard serial port. Using the short sketch below, we will instruct the cpu to display any characters it receives via the serial port on the LCD. The program will also cater for any LCD Commands received like clear screen, or display init, sent over from LCDProc.

You can change the LCD type (16 x 2 or 20 x 4) by modifying the constants lcdRows and lcdCols. The same applies to the LCD Pins, baud rate and startup message. If you decide to change the baud rate, you will also need to change the corresponding setting in the LCDd-sdeclcd.conf config file (Speed=57600) line 38.

/*************************************************/
/* USER VARS
/*************************************************/

// Size of LCD usually 16 X 2 or 20 X 4
const int lcdCols = 20;
const int lcdRows = 4;


//LCD Pins
const int RS = 2;
const int RW = 3;
const int E = 4;
const int D4 = 9;
const int D5 = 10;
const int D6 = 11;
const int D7 = 12;

//Serial Baud Rate
const long serialBaudRate = 57600;

String startupMsg=" Proc pass-through.   ***RANTS.TECH***";

/*************************************************/
/* END USER VARS
/* Nothing else should be touched below this line
/*************************************************/

// Load the Liquid Crystal Library
#include <LiquidCrystal.h>

//Init LCD based on the defined pins
LiquidCrystal lcd(RS, RW, E, D4, D5, D6, D7);

void setup() { 
  Serial.begin(serialBaudRate);
  lcd.begin(lcdRows, lcdCols);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(startupMsg);
  lcd.setCursor(0,1);
}

byte getSerial(){
  int rxByte;  
    while (Serial.available()==0){
  }
  rxByte = Serial.read();
  return (rxByte &0xff);
}

void loop(){
  byte rxByte;
  rxByte = getSerial(); // Fetch byte
  if(rxByte==0xFE) {// If byte is a command    
    lcd.command(getSerial()); // Pass through
  }
  else{
    lcd.write(rxByte); //Otherwise just dump it as text
  }
}

In order to simplify the design, we will use the LCD in 4 bit mode, so the only connections required are Vcc, GND, RS, RW, E and D4-D7. In my case I have soldered the Arduino directly to the back LCD, so D2 to D4 and D9 to D12 align perfectly with LCD pins 4 to 6 and 11 to 14. The remaining 5V and Gnd pins are routed using two short wires (orange and white orange in the picture).

Schematic and Pin connections

The LCD’s Vo (contrast) pin 3, is connected to the wiper of a 4.7k preset. The other ends of the preset are connected to Vdd pin 2 and Gnd pin 1 respectively. Full contrast is achieved by shorting Vo to Gnd. You can also short Vo to gnd directly, eliminating the preset if you like. If your Lcd has a backlight, this can be connected directly to Vdd and GND (Pins 2 and 1) respectively. In my case Pin 16 was the backlight ground pin, while pin 15 was the +ve pin.

A copy of the Arduino IDE used to edit the sketch and program the Arduino can be obtained from here

Arduino IDE

Once the sketch is compiled and uploaded, the LCD should display the message stored in the startupMsg string in line 22.

Adding the plugin

In order to use LCDProc with Opnsense, you need to install a plugin by navigating to System->Firmware->Plugins, or simply by typing “plugins“in the search box on the top right of the screen.

Look for os-lcdproc-sdelcd, and hit the “+”button the the right of the screen.

The plugin will install the necessary software to run LCDProc, however, there is no way of configuring this via the gui, or at least I haven’t yet found a way to do so. To configure the plugin you need to login to the firewall using ssh. You can enable ssh login from System->Settings->Administration. I strongly recommend not to allow password login, and to use ssh keys instead. You might also want to disable ssh after you finish what you need to do. Also make sure that you only allow ssh from the Lan interface in Listen Interfaces.

Configuring the plugin

Once you login to the firewall, select option 8. Shell on the menu and navigate to /usr/local/etc.

>cd /usr/local/etc
Terminal Session

Connect the Arduino nano usb cable to the opnsense host. After a couple of seconds you should see a new device added to your /dev directory called ttyUn. Where n can be any number but is usually 0 or 1. Go ahead and type

>ls -lrt /dev/ttyU*.

In my case the device is called /dev/ttyU0. The timestamp of the device should correspond to the time the usb cable was connected. Keep a mental note of this path, since you will be needing it in the next section.

The configuration file

When the plugin is installed, a sample configuration file located at /usr/local/etc/LCDd-sdeclcd.conf will be created for you. Create a copy of this file just in case using cp /usr/local/etc/LCDd-sdeclcd.conf /usr/local/etc/LCDd-sdeclcd.conf_backup.

Next open the file for editing with the only available editor on the machine, yes its vi. If you are used to using it all well and good, if not, please follow the next instructions to the letter and you will be fine. Vi can be very intimidating. Some love it, yes I belong to this group, others hate it with passion. I have been told that you need to spend half an hour on Google just to find a way to exit the editor … I will show you this is not true in the next few lines.

Lets go ahead and open the config file using vi. Type vi <code>/usr/local/etc/LCDd-sdeclcd.conf

You should be greeted with the screen below. If not, do not panic. just hit the Esc key and type :q (Esc colon q) followed by enter. An you will be back at the shell prompt. Check the paths again and make sure you typed everything correctly and try again.

The first thing we need to do is delete the current config, do this by repeatedly typing dd. Keep deleting lines until the last line (#No options) is deleted. You can also type 26dd which will literally do dd 26 times for you … you are well on your way to become a vi master. Once at it, it might help you to know that x deletes a single character in vi.

Once all the lines are deleted, we need to add our config. Select and copy the text below carefully, making sure nothing is omitted, paste it in a text editor and modify the line 35 if necessary to the path we found in the previous step.

Device=/dev/ttyU0  #This is the device we located in the previous step, can be /dev/ttyU0 or /dev/ttyU1...

Once the necessary modifications are done, select and copy the modified file once again.

[server]
DriverPath=/usr/local/lib/lcdproc/
Driver=hd44780
Bind=127.0.0.1
Port=13666
ReportToSyslog=yes
User=nobody
Foreground=no
Hello="  Welcome to"
Hello="   OPNsense!"
GoodBye="Thanks for using"
GoodBye="   OPNsense!"
WaitTime=15
TitleSpeed=6
ServerScreen=no
Backlight=on
PrevScreenKey=Down
NextScreenKey=Up

[menu]
MenuKey=Left
EnterKey=Right
UpKey=Up
DownKey=Down

[sdeclcd]
# No options

[hd44780]
# Change your ConnectionType to:
# Select what type of connection. See documentation for types.
ConnectionType=lcdserializer

# Device of the serial interface [default: /dev/lcd]
Device=/dev/ttyU0  #This is the device we located in the previous step, can be /dev/ttyU0 or /dev/ttyU1...

# Bitrate of the serial port (0 for interface default)
Speed=57600

# If your display is slow and cannot keep up with the flow of data from
# LCDd, garbage can appear on the LCDd. Set this delay factor to 2 or 4
# to increase the delays. Default: 1.
DelayMult=1

# If you experience occasional garbage on your display you can use this
# option as workaround. If set to a value bigger than null it forces a
# full screen refresh <RefreshDiplay> seconds. Default: 0.
RefreshDisplay=1

Go back to your terminal, and hit i (which is insert for vi), and then paste the contents you just copied from above. The display should look something like this

After you make sure noting was left out or added unintentionally, hit the esc key then type :wq which tells vi to (w)rite the buffer to file and (q)uit. There, that was not hard at all was it ?

Start the service manually.

With the lcd still connected, run the following command in your terminal :

pkill LCD || sleep 2 && /usr/local/sbin/LCDd -c /usr/local/etc/LCDd-sdeclcd.conf && /usr/local/bin/lcdproc -s 127.0.0.1 -p 13666 C M D L

This will kill any running processes called LCD something or other, and restart the lcdproc daemon. At this point you should see the display come to life, and display the relevant data.

If nothing shows on the display, most likely the cause is the wrong device in line 35 of the config file. Double check it and try again.

Boxing it

Finally if you have access to a 3D printer, you can use this stl file to make a box for the LCD unit.

The unit has been running without issues for over a week now, and am quite happy with it. If you can also play around with the settings and screens, this is a good place to start. I would definitely recommend you create some sort of staging environment though, for testing, and avoid testing directly on any production machines. In my case, I run a copy of Opnsense as a virtual machine on my pc. It works perfectly and without any issues.

Leave a reply:

Your email address will not be published.

Site Footer