Device::Arduino::LCD - Perl Interface to the PerLCD Arduino Sketch.


Device-Arduino-LCD documentation Contained in the Device-Arduino-LCD distribution.

Index


Code Index:

NAME

Top

Device::Arduino::LCD - Perl Interface to the PerLCD Arduino Sketch.

SYNOPSIS

Top

  use strict;
  use Device::Arduino::LCD;

  my $lcd = Device::Arduino::LCD->new;
  $lcd->clear;
  $lcd->first_line("Hello World");

See examples/demo.pl for a more comprehensive example.

DESCRIPTION

Top

The Arduino is an open-source physical computing platform. Among the many things one might want to do with such a device is connect an LCD to it and print stuff (at least that's what I wanted to do with it).

There are a couple of excellent low-level libraries that can be linked into an Arduino sketch to provide this functionality. I've chosen the LCD4Bit library to link against. The PerLCD sketch provides a few higher level functions as well as a serial listener.

This Perl library provides a very high level interface for formatting and sending messages to the sketch's listener. Once the device is wired up to an LCD (a fairly trivial task), the USB serial drivers installed, and the sketch compiled and uploaded, getting text on the screen should be no more difficult than the example above: Zero knowledge of LCDs required.

The sketch provided can obviously be used with a client library written in any language, the choice of Perl was (almost) arbitrary.

VARIABLES

Top

Package variables representing (supposedly) sensible default. May be changed as necessary before new() is called.

METHODS

Top

new(class, [device, [baud]])



Returns a Device::Arduino::LCD object or dies if unable to open the serial device.

first_line(Device::Arduino::LCD, text)



Print text on the first line of the LCD. Characters exceeding the length of the display are truncated.

second_line(Device::Arduino::LCD, text)



As above, on the second line.

clear(Device::Arduino::LCD, [pre-delay, [post-delay]])



Clears the LCD. Waits pre-delay seconds before sending the command and post-delay seconds before returning.

scroll_left(Device::Arduino::LCD, [delay])



Scrolls both lines of the LCD to the left, at a rate of delay ms.

scroll_up(Device::Arduino::LCD, text, [pre-delay, [internal-delay, [post_delay]]]);



Scrolls the text up (i.e., line 2 becomes line 1; line 2 contains text). Waits pre-delay before sending the command; scrolls at a rate of internal-delay seconds; waits post_delay seconds before returning. Text is an array ref.

place_string(Device::Arduino::LCD, text, row, column)



Places character one of text at the row and column specified. Rows (on a two line display) range from 1 to 2; columns, however, are zero indexed. (Sorry about that.)

gauge_pct(Device::Arduino::LCD, gauge-number, x%)



Sends x% of 5V to the port specified by gauge-number. Gauges are numbered 1, 2, and 3 corresponding to PWM pins 3, 5, and 6 on the arduino. This has bugger all to do with LCDs, but since the pins are available it seems to make sense to provide a way of addressing them.

command(Device::Arduino::LCD, LCD-command)



Send (numeric) LCD-command directly to the LCD. Useful for sending the sorts of commands listed here: http://tinyurl.com/234d8z

make_char(Device::Arduino::LCD, ascii-num, data)



Installs data (an eight element array or array ref) as ascii character 0 - 7. (NB: the LCD allows eight characters, ASCII 0-7 to be defined by the user; that's what make_char and write_ascii as well as the bargraph functions are all about.)

write_ascii(Device::Arduino::LCD, ascii-num, row, col)



Prints the ascii character (0-7) at row, col. This is particularly useful for printing the custom characters created with make_char().

init_bargraph(Device::Arduino::LCD);



Defines eight custom characters (overwriting whatever's been defined before) as a series of bars for use in graphing.

graph(Device::Arduino::LCD, value, row, column);



With values ranging from 0 - 8 prints that many solid horizontal bars (starting at the bottom) in the position indicated. So

  $lcd->graph(2, 1, 0); $lcd->graph(5, 1, 1);



prints this in the first and second blocks of the top row:

  . . . . .  . . . . .
  . . . . .  . . . . .
  . . . . .  . . . . .
  . . . . .  x x x x x
  . . . . .  x x x x x
  . . . . .  x x x x x
  x x x x x  x x x x x
  x x x x x  x x x x x

tallgraph(Device::Arduino::LCD, value, column);



Like graph() above but using a 16 x 5 font; the value can range from 0 to 16.

convert_to_char(Device::Arduino::LCD, ascii-num, @data)



Convert an array of arrayrefs of text to custom character ascii-num. A call to convert_to_char might look like.

  my $ret = $lcd->convert_to_char(0,
  				[ qw[ . x . x . ] ],
  				[ qw[ x . x . x ] ],
  				[ qw[ . x . x . ] ],
  				[ qw[ x . x . x ] ],
  				[ qw[ . x . x . ] ],
  				[ qw[ x . x . x ] ],
  				[ qw[ . x . x . ] ],
  				[ qw[ x . x . x ] ]);



Any position indicated by an x (or X) will be lit, everything else will be off. The choice of period here is arbitrary. To print this character one could say $lcd->write_ascii(0, 1, 0).

PREREQUISITES

Top

    • Device::SerialPort from CPAN (http://tinyurl.com/2tee6b)
    • GNU avr-gcc, uisp, and avrdude. Note: it's probably possible to use the Arduino development environment instead. I much prefer typing "make" and "make upload" (and editing in emacs).
    • An Arduino NG board and an LCD. In the US www.sparkfun.com distributes the board at a reasonable price. I also bought an LCD from them (the Xiamen GDM1602K).
    • Appropriate USB serial drivers. For OS X these are included with the Arduino development environment package.
    • The Arduino PerLCD sketch compiled and loaded onto the board. Edit arduino/Makefile (there are instructions at the top) and then run 'make'. If that goes well, frob the arduino reset switch and immediately execute 'make upload'.
  • SEE ALSO

    Top



    The Arduino homepage: http://www.arduion.cc, particularly Heather Dewey-Hagborg's LCD tutorial (http://www.arduino.cc/en/Tutorial/LCDLibrary) and neillzero's LCD4BitLibrary page (http://www.arduino.cc/playground/Code/LCD4BitLibrary).

    The Hitachi HD44780 Datasheet: http://www.electronic-engineering.ch/microchip/datasheets/lcd/hd44780.pdf

    Dincer Aydin's LCD page on geocities was also a good resource, particularly the Custom-Character Calculator. http://www.geocities.com/dinceraydin/lcd/intro.htm

    Erik Nordin's HD44780-Based LCD FAQ: http://www.repairfaq.org/filipg/LINK/F_LCD_HD44780.html

    BUGS

    Top



    Rows are are indexed from 0; columns from 1: The LCD4Bit library is shining through, it was kept this way for consistency but it's probably not very abstract or intuitive.

    AUTHOR

    Top



    Kevin Montuori, <montuori@gmail.com>

    COPYRIGHT AND LICENSE

    Top


    Device-Arduino-LCD documentation Contained in the Device-Arduino-LCD distribution.

    package Device::Arduino::LCD;
    
    use strict;
    use Device::SerialPort qw[ :ALL ];
    
    our $VERSION = '1.02';
    
    $|++;
    
    # defaults.
    $Device::Arduino::LCD::Device	     = '/dev/tty.usbserial';
    $Device::Arduino::LCD::Baud	     = 9600;
    $Device::Arduino::LCD::READ_TIMEOUT  = 10;
    
    use Class::MethodMaker [ scalar => [ qw[ port baud ] ]];
    
    use constant {
      ROW_ONE_TEXT => '01',
      ROW_TWO_TEXT => '02',
      SCROLL_UP    => '03',
      PLACE_STRING => '04',
      SCROLL_LEFT  => '05',
      CLEAR        => '06',
      SET_GAUGE    => '07',
      MAKE_CHAR    => '08',
      SEND_CMD     => '09',
      PRINT        => '10',
      WRITE_ASCII  => '11',
      RESET        => '99',
    };
    
    # transmission control.
    our $HEADER_START = "\x1A";
    our $DATA_START   = "\x1B";
    our $DATA_END     = "\x1C";
    our $STRING_TOK   = "\x1D";
    
    sub new {
      my $class = shift;
      my ($device, $baud) = @_;
      $device ||= $Device::Arduino::LCD::Device;
      $baud   ||= $Device::Arduino::LCD::Baud;
      my $port = Device::SerialPort->new($device)
        or die "can't open serial device: $!";
      $port->baudrate($baud);
      $port->read_char_time(0);
      $port->read_const_time(1000);
      return bless { port => $port, baud => $baud }, $class;
    }
    
    sub send {
      my ($self, $command, $payload) = @_;
      my $cmd = $self->encapsulate($command, $payload);
      $self->port->write($cmd);
    }
    
    sub encapsulate {
      my ($self, $command, $payload) = @_;
      return join '' => $HEADER_START, $command, $DATA_START, $payload, $DATA_END;
    }
    
    sub receive {
      my $self = shift;
      my ($buffer, $chars, $timeout) = 
        ("", 0, $Device::Arduino::LCD::READ_TIMEOUT);
      while ($timeout > 0) {
        my ($count, $saw) = $self->port->read(255);
        if ($count > 0) {
          $chars  += $count;
          $buffer .= $saw;
          last if $chars;
        }
        else {
          $timeout--;
        }
      }
      return $buffer;
    }
    
    sub reset {
      my $self = shift;
      $self->send(RESET);
    }
    
    sub first_line {
      my ($self, $text) = @_;
      $self->send(ROW_ONE_TEXT, $text);
    }
    
    sub second_line {
      my ($self, $text) = @_;
      $self->send(ROW_TWO_TEXT, $text);
    }
    
    sub clear {
      my ($self, $pre_delay, $post_delay) = @_;
      sleep ($pre_delay || 0);
      $self->send(CLEAR);
      sleep ($post_delay || 0);
    }
    
    sub scroll_left {
      my ($self, $delay) = @_;
      $self->send(SCROLL_LEFT, $delay);
    }
    
    sub scroll_up {
      my ($self, $text, $pre_delay, $internal_delay, $post_delay) = @_;
      my @text = ref $text eq 'ARRAY' ? @$text : ($text);
      sleep ($pre_delay || 0);
      for (@text) {
        $self->send(SCROLL_UP, $_);
        sleep ($internal_delay || 0);
      }
      sleep ($post_delay || 0);
    }
    
    sub place_string {
      my ($self, $text, $row, $col) = @_;
      my $payload = join $STRING_TOK => $row, $col, $text;
      $self->send(PLACE_STRING, $payload);
    }
    
    sub gauge_pct {
      my ($self, $gauge, $pct) = @_;
      $pct = $pct > 1 ? $pct/100 : $pct;
      my $step_level = 255 * $pct;
      my $payload = join $STRING_TOK => $gauge, $step_level;
      $self->send(SET_GAUGE, $payload);
    }
    
    sub command {
      my ($self, $command) = @_;
      $self->send(SEND_CMD, $command);
    }
    
    sub print_char {
      my ($self, $char) = @_;
      $self->send(PRINT, ord(substr($char, 0, 1)));
    }
    
    sub write_ascii {
      my ($self, $ascii, $row, $col) = @_;
      my $payload = join $STRING_TOK => $row, $col, $ascii;
      $self->send(WRITE_ASCII, $payload);
    }
    
    sub make_char {
      my ($self, $ascii, @data) = @_;
    
      die "out out bounds" unless $ascii <= 7 and $ascii >=0;
      @data = ref $data[0] eq 'ARRAY' ? @{ $data[0] } : @data;
      die "bad character data" unless scalar @data == 8;
      my $payload = join $STRING_TOK => $ascii, @data;
      $self->send(MAKE_CHAR, $payload);
    }
    
    sub convert_to_char {
      my ($self, $ascii, @lines) = @_;
      return undef unless $ascii >=0 and $ascii <= 7;
    
      my @values = ();
    
      for my $line_number (0 .. 7) { # starting at the top
        $values[$line_number] = 128;
        my $line = $lines[$line_number];
        return undef unless (ref $line eq 'ARRAY');
        my @line = @$line;
        for my $i (0 .. 4) {
          $values[$line_number] += (2 ** (4-$i)) if lc $line[$i] eq 'x';
        }
      }
    
      $self->make_char($ascii, @values);
      return \@values;
    }
    
    
    
    # bargraph support.
    
    sub graph {
      my ($self, $val, $row, $col) = @_;
      if ($val == 0) { # print a space.
        $self->place_string(" ", $row, $col);
      }
      elsif ($val <= 8) {
        $self->write_ascii($val - 1, $row, $col);
      }
    }
    
    sub tallgraph {
      my ($self, $val, $col) = @_;
      if ($val == 0) {
        $self->place_string(" ", 1, $col);
        $self->place_string(" ", 2, $col);
      }
      elsif ($val <= 8) {
        $self->place_string(" ",      1, $col);
        $self->write_ascii($val - 1,  2, $col);
      }
      elsif ($val <= 16) {
        $self->write_ascii($val - 9, 1, $col);
        $self->write_ascii(7,        2, $col);
      }
    }
    
    sub init_bargraph {
      my ($self) = shift;
      my $data = [ [128,128,128,128,128,128,128,159],
    	       [128,128,128,128,128,128,159,159],
    	       [128,128,128,128,128,159,159,159],
    	       [128,128,128,128,159,159,159,159],
    	       [128,128,128,159,159,159,159,159],
    	       [128,128,159,159,159,159,159,159],
    	       [128,159,159,159,159,159,159,159],
    	       [159,159,159,159,159,159,159,159] ];
      my $i = 0;
      for (@$data) { $self->make_char($i++, $_) };
    }
    
    
    
    1;
    
    __END__