This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Alternative Method LCD Display

Hi all,

I have put together a method for using an LCD screen like a HD44780 based 16x2 or 20x4. I had tried to use LCD4Linux, but couldn't get it working properly, and the more I stuffed around with the system the more problamatic it became.

It goes without saying that the below is NOT supported in any way shape or form and may void any support contracts, however I am using this in an Home UTM environment, so that suits me fine. You do not need to install any 3rd party utilities or programs on the system, only a small bash script and a crontab entry.

The concept:
Instead of a USB connected device, I have gone for a network based LCD running off an Arduino with an ethernet shield. I send a small HTTP request to the arduino, which converts the data into something that can be displayed on an LCD. This gives me a LOT of flexibility in customizing both the data sets and the LCD display itself, as well as possible expansion to multiple screens (not to mention it can be mounted away from the server itself).

I don't claim to be an expert in any of this, but it works, and works well, so I'm open to other ideas for improvement!

What you need:
Sophos UTM Box (Duh)
Arduino with an Ethernet shield (few options out that there but I'm using an Uno with Ethernet Shield)
HD44870 LCD (I'm using a 20x4 via 2IC, and you can actually use any display you want, you just need to change the libraries used with the arduino)

Arduino Configuration:
Nice and easy and I won't go into detail, but plug in the ethernet shield to the arduino, then, if you are using a 2IC model LCD, plug in VCC, GND, and SDA/SCL and that's it.

Aduino Code, which is heavily commented:

///////////////////////////////////////////////////////////////////////
//                          Include Libaries                         //
///////////////////////////////////////////////////////////////////////
#include  
#include 
#include 
#include 

////////////////////////////////////////////////////////////////////////
//                     CONFIGURE ETHERNET SHIELD                      //
////////////////////////////////////////////////////////////////////////


// Set Mac Address (comment out if not needed)
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

EthernetServer server = EthernetServer(80); //port 80


////////////////////////////////////////////////////////////////////////
//                      Define custom chracters                       //
////////////////////////////////////////////////////////////////////////

#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args)  write(args);
#else
#define printByte(args)  print(args,BYTE);
#endif


byte DownArrow[8] = {
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
  B11111,
  B01110,
  B00100
};

byte UpArrow[8] = {
  B00100,
  B01110,
  B11111,
  B00100,
  B00100,
  B00100,
  B00100,
  B00100
};

////////////////////////////////////////////////////////////////////////
//                         Define LCD Display                         //
////////////////////////////////////////////////////////////////////////


LiquidCrystal_I2C lcd(0x27,20,4);  //0x27 is standard address, 20,4 is screen size


////////////////////////////////////////////////////////////////////////
//                         Declare Variables                          //
////////////////////////////////////////////////////////////////////////

char charnew;
String down = "";
long downnum = 0L;
long upnum = 0L;
String up = "";
String uptime = "";
String cpuload = "";
int switcher = 0;
String dlspeed = "";
String upspeed = "";
int firstrun = 1;
float cpupercent = 0;
String uptimeformat = "";
boolean connecteddhcp = 0;
int finished = 0;
boolean reading = false;


////////////////////////////////////////////////////////////////////////
//                             Setup Program                          //
////////////////////////////////////////////////////////////////////////

void setup()
{

  //Uncomment to enabled serial feedback
  //Serial.begin(115200);


  //Initialize the LCD
  lcd.init();

  //Initialize Custom LCD Characters
  lcd.createChar(0, DownArrow);
  lcd.createChar(1, UpArrow);


  // Print a loading message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("     LCD Display");
  lcd.setCursor(0,1);
  lcd.print("     Sophos UTM");
  lcd.setCursor(0,3);
  lcd.print("Waiting for network");

  //Try to get DHCP IP address and loop until there is one
  connecteddhcp = Ethernet.begin(mac);
  while(connecteddhcp == 0){
    connecteddhcp = Ethernet.begin(mac);
  }

  //Start Ethernet server once IP address is assigned and received
  server.begin();
  
  //Print IP ADdress to LCD screen
  lcd.setCursor(0,3);
  lcd.print("                   ");
  lcd.setCursor(5,3);
  lcd.print(Ethernet.localIP());



}

////////////////////////////////////////////////////////////////////////
//                           Main Program Loop                        //
////////////////////////////////////////////////////////////////////////

void loop()
{

  //Listen for incoming clients, and run main function when received
  checkForClient();

}



////////////////////////////////////////////////////////////////////////
//                            Main Function                           //
////////////////////////////////////////////////////////////////////////

void checkForClient(){

  //Reset Variables for start of function
  reading = false;
  switcher = 0;
  down = "";
  downnum = 0L;
  upnum = 0L;
  cpupercent = 0L;
  uptimeformat = "";
  up = "";
  dlspeed = "";
  upspeed = "";
  cpuload = "";
  uptime = "";

  //Mark server as available
  EthernetClient client = server.available();

  //Check if Client has data for us
  if (client) {

    Serial.println("Data Received and available");
    //Confirm request - an http request ends with a blank line
    boolean currentLineIsBlank = true;
    boolean sentHeader = false;


    //Start read loop
    while (client.connected()) {
      //Debugging Level 1
      Serial.println("Pass Level 1");
      if (client.available()) {
        //Debugging Level 2
        Serial.println("Pass Level 2");

        //Pause for the entire message to arrive
        delay(100);


        if(!sentHeader){
          //Send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          sentHeader = true;
        }

        //Start read in of characters from HTTP Request
        char charnew = client.read();

        if(reading && charnew == ' ') reading = false;
        if(charnew == '?') reading = true; //Found the ?, begin reading the valid info


        if (reading){
          //Debugging level 3
          Serial.println("Pass Level 3");
          
          //Starting main data split, reads in the characters and splits them into strings based on the / found
          if (charnew == '?') {
            //Do nothing - skipping ? character
          }          
          else if (charnew != '/' && switcher == 0 ){
            down = down + charnew;
            switcher = 0;
            Serial.print("Download Speed: ");
            Serial.println(down);
          }
          else if (charnew == '/' && switcher == 0){
            switcher = 1;
            Serial.println("Detected Switch 1");
          }
          else if (charnew != '/' && switcher == 1){
            up = up + charnew;
            Serial.print("Upload Speed: ");
            Serial.println(up);
          }
          else if (charnew == '/' && switcher == 1){
            switcher = 2;
            Serial.println("Detected Switch 2");
          }
          else if (charnew != '/' && switcher == 2){
            cpuload = cpuload + charnew;
            Serial.print("CPULoad: ");
            Serial.println(cpuload);
          }
          else if (charnew == '/' && switcher == 2){
            switcher = 3;
            Serial.println("Detected Switch 3");
            cpuload = cpuload + "  ";
          }
          else if (charnew != '!' && switcher == 3){
            uptime = uptime + charnew;

            Serial.print("Uptime: ");
            Serial.println(uptime);
          }
          else if (charnew == '!' && switcher == 3){
            //Mark as finished, all data received
            finished = 1;
            break;
          }
        }
      }

      //Clean up HTTP Request
      if (charnew == '\n' && currentLineIsBlank)  break;

      if (charnew == '\n') {
        currentLineIsBlank = true;
      }
      else if (charnew != '\r') {
        currentLineIsBlank = false;
      }
    }

    Serial.println("Read in complete");

    //Confirm that data was received and needs to be printed
    if (finished == 1){
      
      //Convert string to intergers and float values
      downnum = down.toInt();
      upnum = up.toInt();
      cpupercent = cpuload.toFloat();
      long seconds = uptime.toInt();

      //Work out uptime
      long s = seconds % 60;
      long m = (seconds / 60) % 60;
      long h = (seconds / (60 * 60)) % 24;
      long d = (seconds / 60 / 60 / 24);

      cpupercent = cpupercent/2;
      cpupercent = cpupercent*100;

      //Format Download Speed
      if (downnum >= 1024) {
        downnum = (downnum/1024);
        dlspeed = " KB/s";
      }
      else {
        dlspeed = " B/s";
      } 

      //Format Upload Speed
      if (upnum >= 1024) {
        upnum = (upnum/1024);

        upspeed = " KB/s";

      }
      else {
        upspeed = " B/s";
      } 

      //If first time run, load labels
      if (firstrun == 1) {
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("WAN");
        lcd.printByte(0);
        lcd.setCursor(0,1);
        lcd.print("LNK");
        lcd.printByte(1);
        lcd.setCursor(0,2);
        lcd.print("CPU Load:");
        lcd.setCursor(0,3);
        lcd.print("Uptime:");
        firstrun = 0;
      }

      //Update LCD Screen with data
      lcd.setCursor(5,0);
      lcd.print("               ");
      lcd.setCursor(5,0);
      lcd.print(downnum);
      lcd.print(dlspeed);
      lcd.setCursor(5,1);
      lcd.print("               ");
      lcd.setCursor(5,1);
      lcd.print(upnum);
      lcd.print(upspeed);
      lcd.setCursor(10,2);
      lcd.print(cpupercent,0);
      lcd.print("%  ");
      lcd.setCursor(8,3);
      lcd.print(d);
      lcd.print("D");
      lcd.print(" ");
      lcd.print(h);
      lcd.print(":");
      lcd.print(m);
      lcd.print(":");
      lcd.print(s);
      lcd.print(" ");
      finished = 0;

    }
    
    //Give the web browser time to receive the data
    delay(1);
    //Close the connection
    client.stop();
    Serial.println("Data Read complete");

  } 

}






Sophos configuration:

You will need to update my below code based on the location of your scripts, but it is easy enough to follow. For the purpose of the below, the scripts are simply in /home/login/

create a file called statupdate in the /home/login directory

Open the file and copy/paste the following script, update the IP address with the IP address of the Arduino (I suggest a DHCP reservation (static)).



#!/bin/bash -x
IP='10.0.0.185'
while :
do
#echo "Start Loop"
DN=$(S=1; F=/sys/class/net/ppp0/statistics/rx_bytes; X=`cat $F`; /bin/sleep $S; Y=`cat $F`; BPS="$(((Y-X)/S))"; /bin/echo $BPS)
UP=$(S=1; F=/sys/class/net/ppp0/statistics/tx_bytes; X=`cat $F`; /bin/sleep $S; Y=`cat $F`; BPS="$(((Y-X)/S))"; /bin/echo $BPS)
CPU=$(/usr/bin/uptime | /bin/grep -oh "load average: [0-9].[0-9][0-9]")
CPU=$(/bin/echo $CPU | /bin/sed -r 's/^.{14}//')
UPTIME=$(/bin/cat /proc/uptime)
UPTIME=$(/bin/echo $UPTIME | cut -f1 -d" ")
#echo $DN
#echo $UP
#echo $CPU
#echo $UPTIME
#echo /usr/bin/curl $IP/?$DN/$UP/$CPU/$UPTIME!
/usr/bin/curl $IP/?$DN/$UP/$CPU/$UPTIME!
/bin/sleep 1
#echo "Finished Loop"
done



You can uncomment the Echos for debugging if needed, and change the interface you want to monitor (I'm using ppp)

Save the file and then set the execution permissions:

 sudo chmod u+x /home/login/statupdate  


Lastly, you need to add the script to crontab so that it starts on reboot.

Login to your server via SSH and run the following:

Sudo crontab -e


Hit I for insert when VI loads, then copy/paste (or type) the following:

@reboot /home/login/statupdate


Hit ESC, then type :wq and hit enter.

Hit reboot and you should be done, your LCD should now start updating every 5-6 seconds.


Hopefully someone finds this useful!



This thread was automatically locked due to age.
  • New version of the arduino code is here, I have added in 3 RGB LED's that change colour depending on what it is monitoring (Red for saturated link for example, then yellow/orange/green depending on load). You will need an Arduino Mega 2560 for this code to run due to the requirement for extra PWM outputs (9 needed total).

    Wiring is simple, pins 2-10 for the colour lines and then +5V line (using 270 ohm resistors) to pins 30 - 32. You WILL need an Arduino Mega or PWM expansion module as you need 9 PWM outputs.

    I have also added in a switch for serial output at the top of the code, just comment out the respective lines to turn it on/off.

    Note I have also updated the bash script above as I was having some issues with it running under root for the crontab due to different environment variables, this new version seems to sort the issue.


    ///////////////////////////////////////////////////////////////////////
    //                          Include Libaries                         //
    ///////////////////////////////////////////////////////////////////////
    #include  
    #include 
    #include 
    #include 

    ////////////////////////////////////////////////////////////////////////
    //                     CONFIGURE ETHERNET SHIELD                      //
    ////////////////////////////////////////////////////////////////////////


    // Set Mac Address (comment out if not needed)
    byte mac[] = { 
      0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

    EthernetServer server = EthernetServer(80); //port 80


    ////////////////////////////////////////////////////////////////////////
    //                      Define custom chracters                       //
    ////////////////////////////////////////////////////////////////////////

    #if defined(ARDUINO) && ARDUINO >= 100
    #define printByte(args)  write(args);
    #else
    #define printByte(args)  print(args,BYTE);
    #endif


    byte DownArrow[8] = {
      B00100,
      B00100,
      B00100,
      B00100,
      B00100,
      B11111,
      B01110,
      B00100
    };

    byte UpArrow[8] = {
      B00100,
      B01110,
      B11111,
      B00100,
      B00100,
      B00100,
      B00100,
      B00100
    };

    ////////////////////////////////////////////////////////////////////////
    //                  Enable/Disable Serial Debugging                   //
    ////////////////////////////////////////////////////////////////////////

    // Enable debug printing...
    #define debugprint Serial.print
    #define debugprintln Serial.println
    // ...OR disable debug printing
    //#define debugprint //
    //#define debugprintln //


    ////////////////////////////////////////////////////////////////////////
    //                         Define LCD Display                         //
    ////////////////////////////////////////////////////////////////////////


    LiquidCrystal_I2C lcd(0x27,20,4);  //0x27 is standard address, 20,4 is screen size


    ////////////////////////////////////////////////////////////////////////
    //                         Declare Variables                          //
    ////////////////////////////////////////////////////////////////////////

    char charnew;
    String down = "";
    long downnum = 0L;
    long upnum = 0L;
    String up = "";
    String uptime = "";
    String cpuload = "";
    int switcher = 0;
    String dlspeed = "";
    String upspeed = "";
    int firstrun = 1;
    float cpupercent = 0;
    String uptimeformat = "";
    boolean connecteddhcp = 0;
    int finished = 0;
    boolean reading = false;


    ////////////////////////////////////////////////////////////////////////
    //                     Declare RGB LED Variables                      //
    ////////////////////////////////////////////////////////////////////////

    int UpRpin = 44;
    int UpGpin = 45;
    int UpBpin = 46;

    int DnRpin = 5;
    int DnGpin = 6;
    int DnBpin = 7;

    int LdRpin = 2;
    int LdGpin = 3;
    int LdBpin = 4;

    #define COMMON_ANODE

    //Colour Reference
    //setColor(255, 0, 0);  // red
    //setColor(83, 4, 0);  // orange
    //setColor(255, 255, 0);  // yellow
    //setColor(0, 255, 0);  // green
    //setColor(0, 255, 255);  // aqua

    ////////////////////////////////////////////////////////////////////////
    //                             Setup Program                          //
    ////////////////////////////////////////////////////////////////////////

    void setup()
    {

      //Uncomment to enabled serial feedback
      Serial.begin(115200);


      //Initialize the LCD
      lcd.init();

      //Initialize Custom LCD Characters
      lcd.createChar(0, DownArrow);
      lcd.createChar(1, UpArrow);


      // Print a loading message to the LCD.
      lcd.backlight();
      lcd.setCursor(0,0);
      lcd.print("     MASHD.net");
      lcd.setCursor(0,1);
      lcd.print("     Sophos UTM");
      lcd.setCursor(0,3);
      lcd.print("Waiting for network");

      //Set RGB LED Pin Mode

      //Set 5V Power for LEDs
      pinMode(30,OUTPUT);
      pinMode(31,OUTPUT);  
      pinMode(32,OUTPUT);

      digitalWrite(30, HIGH);
      digitalWrite(31, HIGH);
      digitalWrite(32, HIGH);

      //Set RGB Pins for LEDS
      pinMode(UpRpin, OUTPUT);
      pinMode(UpGpin, OUTPUT);
      pinMode(UpBpin, OUTPUT);

      pinMode(DnRpin, OUTPUT);
      pinMode(DnGpin, OUTPUT);
      pinMode(DnBpin, OUTPUT);

      pinMode(LdRpin, OUTPUT);
      pinMode(LdGpin, OUTPUT);
      pinMode(LdBpin, OUTPUT);

      //set initial RGB LED colour
      DNsetColor(0, 0, 255);  //blue
      UPsetColor(0, 0, 255);  //blue
      LDsetColor(0, 0, 255);  //blue
      debugprintln("Setting RGB LEDs to BLUE");

      //Try to get DHCP IP address and loop until there is one
       while(connecteddhcp == 0){

        connecteddhcp = Ethernet.begin(mac);

        for (int i = 255; i > 0; i--){ //decrease i with 1
          analogWrite(UpBpin, i);
          analogWrite(DnBpin, i);
          analogWrite(LdBpin, i);
          delay(5);
        }
        for (int i = 0; i  0; i--){ //decrease i with 1
        analogWrite(UpBpin, i);
        analogWrite(DnBpin, i);
        analogWrite(LdBpin, i);
        delay(5);
      }

      //Start Ethernet server once IP address is assigned and received
      server.begin();

      //Print IP ADdress to LCD screen
      lcd.setCursor(0,3);
      lcd.print("                   ");
      lcd.setCursor(5,3);
      lcd.print(Ethernet.localIP());

    }

    ////////////////////////////////////////////////////////////////////////
    //                           Main Program Loop                        //
    ////////////////////////////////////////////////////////////////////////

    void loop()
    {

      //Listen for incoming clients, and run main function when received
      checkForClient();

    }



    ////////////////////////////////////////////////////////////////////////
    //                            Main Function                           //
    ////////////////////////////////////////////////////////////////////////

    void checkForClient(){

      //Reset Variables for start of function
      reading = false;
      switcher = 0;
      down = "";
      downnum = 0L;
      upnum = 0L;
      cpupercent = 0L;
      uptimeformat = "";
      up = "";
      dlspeed = "";
      upspeed = "";
      cpuload = "";
      uptime = "";

      //Mark server as available
      EthernetClient client = server.available();

      //Check if Client has data for us
      if (client) {

        debugprintln("Data Received and available");
        //Confirm request - an http request ends with a blank line
        boolean currentLineIsBlank = true;
        boolean sentHeader = false;


        //Start read loop
        while (client.connected()) {
          //Debugging Level 1
          debugprintln("Pass Level 1");
          if (client.available()) {
            //Debugging Level 2
            debugprintln("Pass Level 2");

            //Pause for the entire message to arrive
            delay(100);


            if(!sentHeader){
              //Send a standard http response header
              client.println("HTTP/1.1 200 OK");
              client.println("Content-Type: text/html");
              client.println();
              sentHeader = true;
            }

            //Start read in of characters from HTTP Request
            char charnew = client.read();

            if(reading && charnew == ' ') reading = false;
            if(charnew == '?') reading = true; //Found the ?, begin reading the valid info


            if (reading){
              //Debugging level 3
              debugprintln("Pass Level 3");

              //Starting main data split, reads in the characters and splits them into strings based on the / found
              if (charnew == '?') {
                //Do nothing - skipping ? character
              }          
              else if (charnew != '/' && switcher == 0 ){
                down = down + charnew;
                switcher = 0;
                debugprint("Download Speed: ");
                debugprintln(down);
              }
              else if (charnew == '/' && switcher == 0){
                switcher = 1;
                debugprintln("Detected Switch 1");
              }
              else if (charnew != '/' && switcher == 1){
                up = up + charnew;
                debugprint("Upload Speed: ");
                debugprintln(up);
              }
              else if (charnew == '/' && switcher == 1){
                switcher = 2;
                debugprintln("Detected Switch 2");
              }
              else if (charnew != '/' && switcher == 2){
                cpuload = cpuload + charnew;
                debugprint("CPULoad: ");
                debugprintln(cpuload);
              }
              else if (charnew == '/' && switcher == 2){
                switcher = 3;
                debugprintln("Detected Switch 3");
                cpuload = cpuload + "  ";
              }
              else if (charnew != '!' && switcher == 3){
                uptime = uptime + charnew;

                debugprint("Uptime: ");
                debugprintln(uptime);
              }
              else if (charnew == '!' && switcher == 3){
                //Mark as finished, all data received
                finished = 1;
                break;
              }
            }
          }

          //Clean up HTTP Request
          if (charnew == '\n' && currentLineIsBlank)  break;

          if (charnew == '\n') {
            currentLineIsBlank = true;
          }
          else if (charnew != '\r') {
            currentLineIsBlank = false;
          }
        }

        debugprintln("Read in complete");

        //Confirm that data was received and needs to be printed
        if (finished == 1){

          //Convert string to intergers and float values
          downnum = down.toInt();
          upnum = up.toInt();
          cpupercent = cpuload.toFloat();
          long seconds = uptime.toInt();

          //Work out uptime
          long s = seconds % 60;
          long m = (seconds / 60) % 60;
          long h = (seconds / (60 * 60)) % 24;
          long d = (seconds / 60 / 60 / 24);

          //Convery CPU load to a percent
          cpupercent = cpupercent/2; //Divide by two for 2 CPU cores
          cpupercent = cpupercent*100;

          //Set CPULOAD RGB LED Colour
          if (cpupercent = 26) && (cpupercent = 51) && (cpupercent = 76){
            LDsetColor(255, 0, 0);  // red
            debugprintln("Setting CPU LOAD RGB LED to RED");
          }

          //Format Download Speed
          if (downnum >= 1024) {
            downnum = (downnum/1024);
            dlspeed = " KB/s";


            //Set DOWNLOAD SPEED RGB LED Colour
            if (downnum = 251) && (downnum = 501) && (downnum = 701){
              DNsetColor(255, 0, 0);  // red
              debugprintln("Setting DOWNLOAD RGB LED to RED");
            }

          }
          else {
            dlspeed = " B/s";

            //Set DOWNLOAD SPEED RGB LED Colour
            DNsetColor(0, 255, 0);  // green
            debugprintln("Setting DOWNLOAD RGB LED to GREEN");

          } 

          //Format Upload Speed
          if (upnum >= 1024) {
            upnum = (upnum/1024);
            upspeed = " KB/s";

            //Set UPLOAD SPEED RGB LED Colour
            if (upnum = 26) && (upnum = 51) && (upnum = 71){
              UPsetColor(255, 0, 0);  // red
              debugprintln("Setting UPLOAD RGB LED to RED");
            }

          }
          else {
            upspeed = " B/s";

            //Set UPLOAD SPEED RGB LED Colour
            UPsetColor(0, 255, 0);  // green
            debugprintln("Setting UPLOAD RGB LED to GREEN");
          } 

          //If first time run, load labels
          if (firstrun == 1) {
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print("WAN");
            lcd.printByte(0);
            lcd.setCursor(0,1);
            lcd.print("LNK");
            lcd.printByte(1);
            lcd.setCursor(0,2);
            lcd.print("CPU Load:");
            lcd.setCursor(0,3);
            lcd.print("Uptime:");
            firstrun = 0;
          }

          //Update LCD Screen with data
          lcd.setCursor(5,0);
          lcd.print("               ");
          lcd.setCursor(5,0);
          lcd.print(downnum);
          lcd.print(dlspeed);
          lcd.setCursor(5,1);
          lcd.print("               ");
          lcd.setCursor(5,1);
          lcd.print(upnum);
          lcd.print(upspeed);
          lcd.setCursor(10,2);
          lcd.print(cpupercent,0);
          lcd.print("%  ");
          lcd.setCursor(8,3);
          lcd.print(d);
          lcd.print("D");
          lcd.print(" ");
          //Add 0 if number is less than 10
          if (h 
  • Hi...with that said I would not recommend it, at all. With the S3 the AMOLED display was a bit thicker than it is on the S4, making it a slightly easier lens repair. The S4 AMOLED display is extremely thin and fragile. Trying to separate the lens from the display, especially for a first timer, will cause you to, probably, break the display.

    pcb printing and assembly