Seriously Cheap Serial LCD

I wanted a couple of serial LCDs for a project but was appalled by the price of them. Having already used serial between 2 arduinos succesfully , I thought whats stopping me using a DIY Arduino instead of an expensive converter ?.

It turns out its far superior to a serial LCD as it can do a lot of stuff on its own without reference to its host.

Total cost including the snazzy box (the seriously nice fit round the LCD panel isn't my work, I work at a school who happens to own a CNC router ;) ) is less than a Tenner UK money (Try around 16 bucks US) which is less than I can buy a serial to LCD convertor for on ebay. Most expensive bit was the LCD panel, followed by the 168 chip, and then the ABS case, the rest of it came to about a pound or two.

It uses more or less the same circuit as the RBBB http://moderndevice.com/RBBB_revB coupled to a 16 pin socket the LCD plugs into.

Nice setup :smiley: I’ve got some breadboard setups with the same concept, added some eeprom read/write and analog/read commands for using the extra features as I’m sure you have as well. :smiley:

I borrowed the code from http://www.nuelectronics.com/download/projects/lcd_smartie_v1.pde and just took away the extra code for buttons and such, then added the extra commands. Then I made a simple library for using the Arduino from the master Arduino with NewSoftSerial.

You can plug this into say an Arduino board, or other USB to serial converter, and then run the LCD-smartie program which enables you to control the LCD from the computer. They have examples of how to set it up here:
http://www.nuelectronics.com/estore/index.php?main_page=project_lcdsmartie

Here’s the sketch for the serial lcd, it uses the lcd4bit-mod library from nuelectronics.com(http://www.nuelectronics.com/download/projects/LCD4Bit_mod.zip)
Binary sketch size: 3974 bytes (of a 7168 byte maximum)

/* Serial LCD, using Matrix Orbital command set*/
//        These must be changed in LCD4Bit_mod.cpp, this is just for reference
//       RS, RW and Enable can be set to whatever you like
//       int RS = 4;
//       int RW = 13; // not used, ground the RW pin
//       int Enable = 3;
//       DB should be an unseparated group of pins  - because of lazy coding in pushNibble()
//       int DB[] = {5, 6, 7, 8};  //wire these to DB4~7 on LCD.


#include "LCD4Bit_mod.h"
#include <EEPROM.h>
//#include <Streaming.h>
const byte backLight = 13; // backLight for LCD: 69 = on, 70 = off

//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 the debug LED to output a heartbeat
  digitalWrite(backLight,HIGH); //turn backLight on
  Serial.begin(9600);

  lcd.init();
  lcd.clear();
  lcd.printIn("   LCD Ready: ");  // Let you know LCD is 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;
  byte pin;
  int myVal;
  int val;

  rxbyte = serial_getch();

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

      case 50: // Read analog pin, then send value back
      pin = serial_getch();
      myVal = analogRead(pin)/4;
      delay(10);
      Serial.print(myVal, BYTE);
      break;
    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;


}

They use the Matrix Orbital Commands (minus some, such as auto-wrap functions and a few others, then add the first few that are shown above.)

You can then use the Master Arduino and connect it to the LCDs rx pin (and tx pin if you use the EEPROM and analog read functions) and use the examples shown here:
http://www.arduino.cc/playground/Learning/SerialLCD

Hope the code is helpful!:smiley: (thanks for another write up :))

nicely done!

Grr that was so my idea :P. Just kidding, but I did have this idea too. I was wondering what would stop me from just using a $5 ATMega chip to translate Serial commands from my Arduino into something on the LCD :P. Now that I know it works, I may try it myself :).

Theres even a pre written sketch for it in the IDE examples - I had to adjust the pin numbers since the pins were chosen for ease of making the stripboard rather than to suit a sketch. :)

G'day pluggy . Well its certainly cheap enough. Is this related to the transmitter you made ? Did you get your receiver going too . I got mine away and its working fine . I'm working on your bash script and rrdtool to try to record it to a graph. That's pretty difficult with no prior knowledge of bash scripting .

If its OK with you I'd like to send ,or post here ,what I have ,and maybe you could give me some pointers.

On this LCD, I am missing something I think . The sketch you mention is standard stuff and I dont see where a serial converter comes in . I run my 16 X 4 straight from the arduino as per that sketch and there is a long thread with David Mellis involved on the larger Graphical LCD which I also run straight from the arduino.

So I don't see where the serial converter fits in? You might explain that a bit more please. The arduino is about $ 20 and the LCD the same so $50 AUD all up with a box and a hacksaw cut that way as against $18 AUD for yours , so cheap it is.

Also what is the component marked 10K 289 a crystal or is that your resonator near the capacitor centre of the chip? Could I get a look at the cuts on the back of the board , save the hard work of redesigning it.

I don't think this has anything to do with his wireless transmitter/receiver setup he made previously. You can buy "Serial Enabled LCD's", which only require 1 pin, rather than 6 or 7 you need regularly from parallel LCD's.

Most of the Serial-LCDs you buy, are ONLY that, a serial-enabled LCD. Using an Atmega8, you can use it as a serial LCD, but use some extra features.. like the analog inputs.. the extra digital pins.. etc.

I could be wrong.. but I think when he's referring to the "converter", he means the Atmega chip itself, which takes Serial input, then displays it on the LCD. :)

But an Atemga8 chip.. is about $3 US.. the LCDs.. you can get for about $7 US.. so about $10.. then the caps.. crystals.. but if you buy in bulk all that is very cheap. So I've made a few Serial LCDs for about $13, but they have extra features like analog inputs and whatnot, plus I can use the EEPROM from the Master arduino. ... and program it again!

:)

Its not connected with the wireless setup I made earlier. I didn't actually implement it in the end, I wired it, couldn't live without a back channel and I had doubts about using a TX and RX in the same vincinity. Wiring it gave me a handy way of powering it too (spare wires in the cat 5).

The arduino chip does the job of the convertor as CO says.

The 2 brown blobs just above the 168 chip are a 16mHz Ceramic resonator (cheaper than a crystal and capacitors ;) ) and a 100 nF decoupling capacitor. The brown blob at the left side is another 100nF capacitor which forms part of the auto reset circuit for programming it. The '10K 289' thingy is a 10K preset pot for the contrast on the LCD. I'll post a piccie of the back of the board when I get near the board and a camera again. I pretty much designed it as I made it so it could probably be made simpler with a bit of thought. 4 of the digital pins on the arduino align directly with the upper 4 data pins of the LCD.

The prices of the bits are :

LCD Panel £2.99 Ebay (Black on Green - No backlight) 168 Chip £2.65 http://www.bitsbox.co.uk/ (Blank - needed blowing) ABS Box £1.89 http://www.bitsbox.co.uk/ Board £0.60 http://www.bitsbox.co.uk/ Approx -Cut from a big bit Resonator £0.40 " Sockets £0.60 approx - cut from larger ones for LCD, 28 pin for chip Button £0.18 Bitsbox again Preset £0.15 Bitsbox Sundries £0.20 (resistors, capacitors, LED) Bits Box Odd bits of wire out of the junk box.

I was tempted by 10 of the panels for £10 + £2 carriage which would have made them even cheaper.

Fire away on the Bash scripting. :)

I get temperatures each 30 seconds into the terminal window of ttyUSB0 that are like this

-14.75
-14.77
-14.80

The script I have so far is this


#!/bin/bash
cat </dev/ttyUSB0 >> fromarduino &
temp1= tail -c8 fromarduino
rrdtool update freezer.rrd N:$temp1

#echo $temp1
date > arduinolastdate

stty -F /dev/ttyUSB0 cs6 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -clocal sane (notice I’ve got all yours in here with sane at the end)


I run this with the command “nohup bash myscript < /dev/null > sh.err 2>&1”
which appears to give me an updated “fromarduino” file each 30 seconds but the rrd database does not want to update . I’m getting NaN’s instead of values.(Only one when I run the script then no more.)

I suspect placement of the update statement is probably an issue

I created the database with
rrdtool create freezer.rrd -s 30 DS:temp:GAUGE:60:0:1000 RRA:AVERAGE:0.5:1:1000 (just 1000 atm until I get it going)

So I want to get the database to update each 30 seconds with good values.
Thats first.

The second is that having an arduino receiver hanging on the wall receiving temps sent by the remote radio unit on the freezer, and feeding that via USB to the computer, I want the computer to just record the time and temp each time it receives one without me having to start the terminal each time I turn on. Something that goes into autostart might do it or perhaps a dedicated “C” program that starts as a service at boot.

However I don’t know how to make a program in “C” that can read the USB serial input and write it to the rrd.
So any assistance appreciated Pluggy - Your system is still rumbling away there nicely I see and with more added.http://pluggy.is-a-geek.com/arduino/index.html

Try just plotting the integer part for starters

temp1=` tail -c8 fromarduino|cut -d '.' -f1`

BASH doesn’t understand non integer numbers without help.
You need to install “bc” and pipe stuff through it to give it that help.

Its no accident I run all my stuff once a minute, its just done with crontab :wink:

I had a similar idea a couple of months back. I breadboarded it and recently had the opportunity to make it into a PCB. I'll post a link to my blog page about the design in another message since I can't link to it in this message.

You can find the design for my PCB at http://dorkbotpdx.org/blog/scott_d/my_first_pcb_project

Nice-looking board, Scott!

A couple of suggestions for your second (and maybe third) PCB projects:

  1. Consider using the RBBB as a starting point, rather than the Dorkboard. This is partly personal bias because I discovered it before I heard of the Dorkboard, but I really like the fact fact that it gives you the options of using a TO-220 regulator (with a heatsink, if desired) and a power connector. Or of easily cutting off that part of the board if you don't need them. That doesn't matter for most "serial backpack" uses, but it increases flexibility for projects where you'd use it as the Arduino core.

  2. A version of the board to work with graphic LCDs would be terminally cool. I've been thinking about this for a while, but haven't had the time to do it (though someone else has recently made a first run at it). My idea is that you make your "backpack" Arduino-compatible, and put the GLCD library on it. The "main" Arduino would have a "stub" version of the library, and use a mechanism kinda like RPC to pass the parameters to the "backpack" Arduino via I2C (or maybe serial). This would make it possible to attach a GLCD to your project without the huge consumption of memory and I/O pins, while retaining the well-established software interface.

I'm not 100% certain that the "RPC GLCD" project is feasible: it might turn out that the "stub" library needs access to the internal variables of the library in a way that rules out an RPC-style interface. But, at the very least, the board would be helpful in reducing the number of hand-wired connections to the LCD board, so there should be quite a few people interested in using it even if the RPC idea doesn't fly.

Ran