Arduino as MatrixOrbital LCD (Serial LCD).

Well... who doesn't love Serial lcds?! I mean, just 1 wire?!

Well, I purchased a Nuelectronics shield a while back.. the buttoned one, and at the bottom of the page they have an example sketch that uses Matrix Orbital type commands to run a HD47780 LCD. Also, I seen that there is a library posted on google code... but, there's no files to download!

So I guess what I'm kind of asking, does anyone know of any libraries/full sketches to Emulate a Matrix Orbital? I have a sketch that's got a good bit of the Matrix commands down, but it's mixed in with another huge sketch and just rather not spend the time to pick at it if there's already a sketch around! But if not.. I found my next project! (I have 3 LCD's, and 5 Atmega8's, want to put the 8's to use!:D)

Well first, here's the Google page I found... that's empty! But this brings hope that there's a sketch out there!:D http://code.google.com/p/arduino-matrixorbital/

The sketch on their page is for LCD Smartie program, set on Matrix.dll. This sketch requires the 4Bit_LCD modded library(next link) http://www.nuelectronics.com/download/projects/lcd_smartie_v1.pde

Here's the library: http://www.nuelectronics.com/download/projects/LCD4Bit_mod.zip

Here's an example of some of the main code from their site:

void loop()
      {
            byte rxbyte;
            byte temp;
            rxbyte = serial_getch();

              if (rxbyte == 254) //Matrix Orbital uses 254 prefix for commands 
              {
                    switch (serial_getch())
                    {
                        ... 
                        case 71:  //set cursor position 
                              temp = (serial_getch() - 1);  //get column byte 
                              switch (serial_getch())  //get row byte 
                              {

                              //line 1 is already set up 
                              case 2:
                                    temp += 0x40;
                                    break;
                                    ...

Well, it was MUCH easier than I expected!

I have two versions… one for just using hardware serial pins, 0 and 1. (required for Atmega8) And one that uses NewSoftSerial.

I’ve also wrote a small library for writing to the LCD, all though, there is another library available that will work for MOST commands. I’ll post that after I get a few commands… but I’ll show how easy it easy to use it!
(some of the better commands aren’t implemented yet, such as auto-wrapper, etc, but I added commands to easily READ/WRITE to the EEPROM on the LCD-arduino)

Also, I used pin 13 for the backlight… My back light draws too much current for just the pin, so I had to use a transistor(don’t forget your resistor!) I did this just for the sake of having the ability to turn on and off the backlight.

Well here’s the Hardware Only version: 4378 bytes

#include "LCD4Bit_mod.h"
#include <EEPROM.h>
const byte backLight = 13;

//create object to control an LCD.  
//number of lines in display=1
LCD4Bit_mod lcd = LCD4Bit_mod(2); 


void setup() { 
  pinMode(backLight, OUTPUT);  //we'll use 13 as our backLight
  digitalWrite(backLight,HIGH);  //backlight on at startup
  Serial.begin(19200);

  lcd.init();
  lcd.clear();
  lcd.printIn("   LCD Ready: ");
}


byte serial_getch(){

  int incoming;  
  while (Serial.available()==0){
  }
  // read the incoming byte:
  incoming = Serial.read();

  return (byte) (incoming &0xff);
}



void loop(){

  byte rxbyte;
  byte temp;
  byte addr;
  int val;

  rxbyte = serial_getch();

  if (rxbyte == 254) //Matrix Orbital uses 254 prefix for commands
  {
    switch (serial_getch())
    {

    case 64: // EEPROM Write (address, value) 
      addr = serial_getch();
      delay(10);
      val = serial_getch();
      EEPROM.write(addr, val);
      break;
    case 65: // EEPROM Read  (address)
      addr = serial_getch(); // EEPROM address
      val = EEPROM.read(addr); //
      Serial.print(val, BYTE);
      break;
    case 69: //backlight on (at previously set brightness)
      // not implemented      
      digitalWrite(backLight, HIGH);                  

      break;
    case 70: //backlight off
      // not implemented      
      digitalWrite(backLight, LOW);                  
      break;
    case 71:  //set cursor position
      temp = (serial_getch() - 1);  //get column byte
      switch (serial_getch())  //get row byte
      {
        //line 1 is already set up
      case 2:
        temp += 0x40;
        break;
      case 3:
        temp += 0x14;
        break;
      case 4:
        temp += 0x54;
        break;
      default:
        break;
      }
      lcd.commandWrite(0b10000000 + temp);
      break;
    case 72:  //cursor home (reset display position)
      lcd.commandWrite(2);
      break;
    case 74:  //show underline cursor
      lcd.commandWrite(0b00001110);
      break;
    case 75:  //underline cursor off
    case 84:  //block cursor off
      lcd.commandWrite(0b00001100);
      break;
    case 76:  //move cursor left
      lcd.commandWrite(16);
      break;
    case 77:  //move cursor right
      lcd.commandWrite(20);
      break;
    case 78:  //define custom char
      lcd.commandWrite(64 + (serial_getch() * 8));  //get+set char address
      for (temp = 7; temp != 0; temp--)
      {
        lcd.print(serial_getch()); //get each pattern byte
      }
      break;
    case 83:  //show blinking block cursor
      lcd.commandWrite(0b00001111);
      break;
    case 86:  //GPO OFF
      //implement later
      break;
    case 87:  //GPO ON
      /*temp = serial_getch();
                               if (temp == 1)
                               {
                                     GPO1 = GPO_ON;
                               }*/
      break;
    case 88:  //clear display, cursor home
      lcd.commandWrite(1);
      break;
    case 152: //set and remember (doesn't save value, though)
    case 153: //set backlight brightness
      //not implemented
      break;

      //these commands ignored (no parameters)
      //case 35: //read serial number /*USING FOR EEPROM*/
      //case 36: //read version number
    case 55: //read module type
    case 59: //exit flow-control mode
      //case 65: //auto transmit keypresses
    case 96: //auto-repeat mode off (keypad)
    case 67: //auto line-wrap on
    case 68: //auto line-wrap off
    case 81: //auto scroll on
    case 82: //auto scroll off
    case 104: //init horiz bar graph
    case 109: //init med size digits
    case 115: //init narrow vert bar graph
    case 118: //init wide vert bar graph
      break;
    default:
      //all other commands ignored and parameter byte discarded
      temp = serial_getch();  //dump the command code
      break;
    }
    return;
  } //END OF COMMAND HANDLER

  //change accented char to plain, detect and change descenders
  //NB descenders only work on 5x10 displays. This lookup table works
  //  with my DEM-20845 (Display Elektronik GmbH) LCD using KS0066 chip.
  switch (rxbyte)
  {
    //chars that have direct equivalent in LCD charmap
    /*            case 0x67: //g
                       rxbyte = 0xE7;
                       break;
                 case 0x6A: //j
                       rxbyte = 0xEA;
                       break;
                 case 0x70: //p
                       rxbyte = 0xF0;
                       break;
                 case 0x71: //q
                       rxbyte = 0xF1;
                       break;
                 case 0x79: //y
                       rxbyte = 0xF9;
                       break;
     */  case 0xE4: //ASCII "a" umlaut
    rxbyte = 0xE1;
    break;
  case 0xF1: //ASCII "n" tilde
    rxbyte = 0xEE;
    break;
  case 0xF6: //ASCII "o" umlaut
    rxbyte = 0xEF; //was wrong in v0.86
    break;
  case 0xFC: //ASCII "u" umlaut
    rxbyte = 0xF5;
    break;

    //accented -> plain equivalent
    //and misc symbol translation
  case 0xA3: //sterling (pounds)
    rxbyte = 0xED;
    break;
    /*            case 0xB0: //degrees symbol
                       rxbyte = 0xDF;
                       break;
     */  case 0xB5: //mu
    rxbyte = 0xE4;
    break;
  case 0xC0: //"A" variants
  case 0xC1:
  case 0xC2:
  case 0xC3:
  case 0xC4:
  case 0xC5:
    rxbyte = 0x41;
    break;
  case 0xC8: //"E" variants
  case 0xC9:
  case 0xCA:
  case 0xCB:
    rxbyte = 0x45;
    break;
  case 0xCC: //"I" variants
  case 0xCD:
  case 0xCE:
  case 0xCF:
    rxbyte = 0x49;
    break;
  case 0xD1: //"N" tilde -> plain "N"
    rxbyte = 0x43;
    break;
  case 0xD2: //"O" variants
  case 0xD3:
  case 0xD4:
  case 0xD5:
  case 0xD6:
  case 0xD8:
    rxbyte = 0x4F;
    break;
  case 0xD9: //"U" variants
  case 0xDA:
  case 0xDB:
  case 0xDC:
    rxbyte = 0x55;
    break;
  case 0xDD: //"Y" acute -> "Y"
    rxbyte = 0x59;
    break;
    /*            case 0xDF: //beta  //mucks up LCDSmartie's degree symbol??
                       rxbyte = 0xE2;
                       break;
     */  case 0xE0: //"a" variants except umlaut
  case 0xE1:
  case 0xE2:
  case 0xE3:
  case 0xE5:
    rxbyte = 0x61;
    break;
  case 0xE7: //"c" cedilla -> "c"
    rxbyte = 0x63;
    break;
  case 0xE8: //"e" variants
  case 0xE9:
  case 0xEA:
  case 0xEB:
    rxbyte = 0x65;
    break;
  case 0xEC: //"i" variants
  case 0xED:
  case 0xEE:
  case 0xEF:
    rxbyte = 0x69;
    break;
  case 0xF2: //"o" variants except umlaut
  case 0xF3:
  case 0xF4:
  case 0xF5:
  case 0xF8:
    rxbyte = 0x6F;
    break;
  case 0xF7: //division symbol
    rxbyte = 0xFD;
    break;
  case 0xF9: //"u" variants except umlaut
  case 0xFA:
  case 0xFB:
    rxbyte = 0x75;
    break;
  default:
    break;
  }

  lcd.print(rxbyte);  //otherwise a plain char so we print it to lcd
  return;
}

And here’s the NewSoftSerial Version : (requires at least a 168, but lets you use ANY pin as tx/rx) 4988 bytes

#include "LCD4Bit_mod.h"
#include <EEPROM.h>
#include <NewSoftSerial.h>
const byte backLight = 13;

NewSoftSerial mySerial(9, 10);  // chose 9 and 10 just for the test, worked great

//create object to control an LCD.  
//number of lines in display=1
LCD4Bit_mod lcd = LCD4Bit_mod(2); 


void setup() { 

  pinMode(backLight, OUTPUT);  //we'll use 13 as our backLight
  digitalWrite(backLight,HIGH);
  mySerial.begin(19200);

  lcd.init();
  lcd.clear();
  lcd.printIn("   LCD Ready: ");
}


byte serial_getch(){

  int incoming;  
  while (mySerial.available()==0){
  }
  // read the incoming byte:
  incoming = mySerial.read();

  return (byte) (incoming &0xff);
}



void loop(){

  byte rxbyte;
  byte temp;
  byte addr;
  int val;

  rxbyte = serial_getch();

  if (rxbyte == 254) //Matrix Orbital uses 254 prefix for commands
  {
    switch (serial_getch())
    {

    case 64: // EEPROM Write (address, value) 
      addr = serial_getch();
      delay(10);
      val = serial_getch();
      EEPROM.write(addr, val);
      break;
    case 65: // EEPROM Read  (address)
      addr = serial_getch(); // EEPROM address
      val = EEPROM.read(addr); //
      mySerial.print(val, BYTE);
      break;
    case 69: //backlight on (at previously set brightness)
      // not implemented      
      digitalWrite(backLight, HIGH);                  

      break;
    case 70: //backlight off
      // not implemented      
      digitalWrite(backLight, LOW);                  
      break;
    case 71:  //set cursor position
      temp = (serial_getch() - 1);  //get column byte
      switch (serial_getch())  //get row byte
      {
        //line 1 is already set up
      case 2:
        temp += 0x40;
        break;
      case 3:
        temp += 0x14;
        break;
      case 4:
        temp += 0x54;
        break;
      default:
        break;
      }
      lcd.commandWrite(0b10000000 + temp);
      break;
    case 72:  //cursor home (reset display position)
      lcd.commandWrite(2);
      break;
    case 74:  //show underline cursor
      lcd.commandWrite(0b00001110);
      break;
    case 75:  //underline cursor off
    case 84:  //block cursor off
      lcd.commandWrite(0b00001100);
      break;
    case 76:  //move cursor left
      lcd.commandWrite(16);
      break;
    case 77:  //move cursor right
      lcd.commandWrite(20);
      break;
    case 78:  //define custom char
      lcd.commandWrite(64 + (serial_getch() * 8));  //get+set char address
      for (temp = 7; temp != 0; temp--)
      {
        lcd.print(serial_getch()); //get each pattern byte
      }
      break;
    case 83:  //show blinking block cursor
      lcd.commandWrite(0b00001111);
      break;
    case 86:  //GPO OFF
      //implement later
      break;
    case 87:  //GPO ON
      /*temp = serial_getch();
                               if (temp == 1)
                               {
                                     GPO1 = GPO_ON;
                               }*/
      break;
    case 88:  //clear display, cursor home
      lcd.commandWrite(1);
      break;
    case 152: //set and remember (doesn't save value, though)
    case 153: //set backlight brightness
      //not implemented
      break;

      //these commands ignored (no parameters)
      //case 35: //read serial number /*USING FOR EEPROM*/
      //case 36: //read version number
    case 55: //read module type
    case 59: //exit flow-control mode
      //case 65: //auto transmit keypresses
    case 96: //auto-repeat mode off (keypad)
    case 67: //auto line-wrap on
    case 68: //auto line-wrap off
    case 81: //auto scroll on
    case 82: //auto scroll off
    case 104: //init horiz bar graph
    case 109: //init med size digits
    case 115: //init narrow vert bar graph
    case 118: //init wide vert bar graph
      break;
    default:
      //all other commands ignored and parameter byte discarded
      temp = serial_getch();  //dump the command code
      break;
    }
    return;
  } //END OF COMMAND HANDLER

  //change accented char to plain, detect and change descenders
  //NB descenders only work on 5x10 displays. This lookup table works
  //  with my DEM-20845 (Display Elektronik GmbH) LCD using KS0066 chip.
  switch (rxbyte)
  {
    //chars that have direct equivalent in LCD charmap
    /*            case 0x67: //g
                       rxbyte = 0xE7;
                       break;
                 case 0x6A: //j
                       rxbyte = 0xEA;
                       break;
                 case 0x70: //p
                       rxbyte = 0xF0;
                       break;
                 case 0x71: //q
                       rxbyte = 0xF1;
                       break;
                 case 0x79: //y
                       rxbyte = 0xF9;
                       break;
     */  case 0xE4: //ASCII "a" umlaut
    rxbyte = 0xE1;
    break;
  case 0xF1: //ASCII "n" tilde
    rxbyte = 0xEE;
    break;
  case 0xF6: //ASCII "o" umlaut
    rxbyte = 0xEF; //was wrong in v0.86
    break;
  case 0xFC: //ASCII "u" umlaut
    rxbyte = 0xF5;
    break;

    //accented -> plain equivalent
    //and misc symbol translation
  case 0xA3: //sterling (pounds)
    rxbyte = 0xED;
    break;
    /*            case 0xB0: //degrees symbol
                       rxbyte = 0xDF;
                       break;
     */  case 0xB5: //mu
    rxbyte = 0xE4;
    break;
  case 0xC0: //"A" variants
  case 0xC1:
  case 0xC2:
  case 0xC3:
  case 0xC4:
  case 0xC5:
    rxbyte = 0x41;
    break;
  case 0xC8: //"E" variants
  case 0xC9:
  case 0xCA:
  case 0xCB:
    rxbyte = 0x45;
    break;
  case 0xCC: //"I" variants
  case 0xCD:
  case 0xCE:
  case 0xCF:
    rxbyte = 0x49;
    break;
  case 0xD1: //"N" tilde -> plain "N"
    rxbyte = 0x43;
    break;
  case 0xD2: //"O" variants
  case 0xD3:
  case 0xD4:
  case 0xD5:
  case 0xD6:
  case 0xD8:
    rxbyte = 0x4F;
    break;
  case 0xD9: //"U" variants
  case 0xDA:
  case 0xDB:
  case 0xDC:
    rxbyte = 0x55;
    break;
  case 0xDD: //"Y" acute -> "Y"
    rxbyte = 0x59;
    break;
    /*            case 0xDF: //beta  //mucks up LCDSmartie's degree symbol??
                       rxbyte = 0xE2;
                       break;
     */  case 0xE0: //"a" variants except umlaut
  case 0xE1:
  case 0xE2:
  case 0xE3:
  case 0xE5:
    rxbyte = 0x61;
    break;
  case 0xE7: //"c" cedilla -> "c"
    rxbyte = 0x63;
    break;
  case 0xE8: //"e" variants
  case 0xE9:
  case 0xEA:
  case 0xEB:
    rxbyte = 0x65;
    break;
  case 0xEC: //"i" variants
  case 0xED:
  case 0xEE:
  case 0xEF:
    rxbyte = 0x69;
    break;
  case 0xF2: //"o" variants except umlaut
  case 0xF3:
  case 0xF4:
  case 0xF5:
  case 0xF8:
    rxbyte = 0x6F;
    break;
  case 0xF7: //division symbol
    rxbyte = 0xFD;
    break;
  case 0xF9: //"u" variants except umlaut
  case 0xFA:
  case 0xFB:
    rxbyte = 0x75;
    break;
  default:
    break;
  }

  lcd.print(rxbyte);  //otherwise a plain char so we print it to lcd
  return;


}

And here’s an example of using the commands, so far I’ve only implemented some of the simpler commands. (I’m still learning, so as time progresses, so will this!:D)

#include <NewSoftSerial.h>
#include <myLCD.h>

/***********************/
  const byte rxPin = 2;
  const byte txPin = 3;

 myLCD lcd(rxPin, txPin); //initializes LCD on pins 2 and 3, use any pin
/***********************/


void setup()
{
  lcd.begin(19200);  //begins the LCD
  lcd.clear();
  lcd.BLon();
  lcd.print("Hello!");
}      


void loop()
{
  lcd.flash(250, 4);  // flashes backLight @ 250ms intervals, 4 times
  delay(1000);       // one second delay
}

Here’s the Header file for the library: myLCD.h

#ifndef myLCD_h
#define myLCD_h
/* myLCD library!!
 * LEARN LEARN LEARN
 * SerialLCD Library, using Matrix Orbital Commands
 * 
 * 
 */

#include "Wprogram.h"
#include "Print.h"
#include "..\NewSoftSerial\NewSoftSerial.h"


class myLCD : public Print
{

      public:
            myLCD(int rxpin, int txpin);

            void begin(long baudrate);
            void cursorSet(byte x, byte y);
            void eWrite(byte addr, byte val);
            byte eRead(byte addr);
            void home();
            void clear();
            void BLon();
            void BLoff();
            void flash(int time, byte times);



private:
      virtual void write(uint8_t b);

      NewSoftSerial SwSerial;
};

#endif

And the CPP: myLCD.cpp

#include "WProgram.h"
#include "Streaming.h"
#include "myLCD.h"

myLCD::myLCD(int rxpin,int txpin)  //
:SwSerial(rxpin,txpin)
{
}

void myLCD::write(uint8_t b)
{
      SwSerial << b;
}

void myLCD::begin(long baudrate)
{
      SwSerial.begin(baudrate);
}

void myLCD::cursorSet(byte x, byte y)
{
      SwSerial<<_BYTE(254)<<_BYTE(71)<<_BYTE(x)<<_BYTE(y);
}

void myLCD::eWrite(byte addr, byte val)
{
      SwSerial<<_BYTE(254)<<_BYTE(64)<<_BYTE(addr)<<_BYTE(val);
}

byte myLCD::eRead(byte addr)
{
      SwSerial<<_BYTE(254)<<_BYTE(65)<<_BYTE(addr);
      delay(20);
      byte val = SwSerial.read();
      delay(20);
      return val;
}

void myLCD::home()
{
      SwSerial <<_BYTE(254) <<_BYTE(72);
}

void myLCD::clear()
{
      SwSerial <<_BYTE(254) << _BYTE(88); // 88 == CLEAR byte
}

void myLCD::BLon() //BackLight on
{
      SwSerial << _BYTE(254) << _BYTE(69);
}

void myLCD::BLoff()//Backlight off 
{
      SwSerial << _BYTE(254) << _BYTE(70);
}

void myLCD::flash(int time, byte times)//flash backlight
{
      for(int x = 0; x < times; x++){
      SwSerial << _BYTE(254) << _BYTE(70);
      delay(time);
      SwSerial << _BYTE(254) << _BYTE(69);
      delay(time);
      }
}

All the code is fairly easy!
For example, to read from the EEPROM on the other Arduino:

int myValue = lcd.eRead(address);
analogWrite(ledPin, myValue);

And it’s almost as easy to write to the EEPROM:
byte address = 43 // choose which byte to read from EEPROM
int value; // value to store, can use byte, but int prints as integer without a conversion

lcd.eWrite(address, value); // Reads EEPROM
lcd.print(value); // sends value to LCD

More to come soon! Hopefully this will be semi-useful to someone!:slight_smile:

And by the way, I didn't expect to use these links until now.. so I've got to give them credit:

I used alot of code from this link (by alot of code, I mean I built my library on this guys code!:D): http://laserpup.com/?p=114 Used his code to implement NewSoftSerial

and of course! Can't forget NewSoftSerial, thanks ALOT Mikal for all your hard work! :D http://arduiniana.org/libraries/NewSoftSerial/ don't forget to check out his other awesome libraries, Streaming especially one of my favorites!