Print to LCD from Perl via Arduino Duemilanove?

Hello,
I have the 20 x 4 Blue LCD with LCD117 kit from here, along with the Arduino Duemilanove. I'm trying to be able to print to the LCD from a Perl script, but I can't seem to get it to work.

Here is Perl script I'm trying to use:

#!/usr/bin/perl -w

use POSIX qw(setsid);
use Time::HiRes qw(usleep);

## chdir '/'                 or die "Can't chdir to /: $!";
## umask 0;
#open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
#open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
#open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
## defined(my $pid = fork)   or die "Can't fork: $!";
## exit if $pid;
## setsid                    or die "Can't start a new session: $!";



use Device::SerialPort;

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);


while(1)

{

my $var="testing...";

my $return=$port->write("$var");

# !!!!!!!!!!!!!!!!!!!?nIntruder?nBeware?n!!!!!!!!!!!!!!!!!!!



sleep(1)

}

Here is the sketch I'm trying to use:

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>

void setup()
{
  Serial.begin(9600);
}


void loop()
{

  if (Serial.available()) {
    delay(100);
    Serial.write(Serial.read());
  }
}

Once I get this working, I would like to modify the Perl script a bit, namely make it fork and print the "Intruder Beware" message (hence the comments).

Your sketch is a fine "echo server." It should return back to you, everything that you feed it. (You don't need the delay() call at all, but if you want to keep it, I'd put it outside the if block.)

Test your sketch by using the Arduino client's serial port first. After uploading the sketch, press the rightmost button on your toolbar. Then start sending strings to the Arduino with the little "Send" button.

Does this part work?

halley -
Thank you for the response. Yes, I can use the LCD screen by typing directly into the Serial Monitor.

I'm guessing this means it is something wrong with the Perl script - but what?

Try this and carefully check your pin assignments :slight_smile:

#include <LiquidCrystal.h>

// LiquidCrystal display with:
// rs on pin 12
// rw on pin 11
// enable on pin 10
// d0, d1, d2, d3 on pins 5, 4, 3, 2
LiquidCrystal lcd(12, 11, 2, 3, 4, 5, 6, 7, 8, 9, 10);

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  // when characters arrive over the serial port...
  if (Serial.available()) {
    // wait a bit for the entire message to arrive
    delay(100);
    // clear the screen
    lcd.clear();
    // read all the available characters
    while (Serial.available() > 0) {
      // display each character to the LCD
      lcd.write(Serial.read());
    }
  }
}

Can you have both the computer and the LCD sharing the same serial on the arduino? It looks like your code simply says to echo back anything received on the hardware serial back to the hardware serial.

Shouldn't the LCD be a software serial setup?

!c

@drone:
I really don't know... I'm about as much of a newbie as one can be to this sort of thing.

@gnu_linux:
Thanks for the code - it worked!
However, I had to tweak my Perl script a little bit.

To anyone it may help:
The Perl script was executing too fast for the LCD/Arduino to keep pace. (See my note at the top of the new script.)

#!/usr/bin/perl -w

### Keep in mind that we need
### to sleep a lot so that the
### LCD/Arduino can keep up!


use POSIX qw(setsid);
use Time::HiRes qw(usleep);
use Device::SerialPort;

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

my $return0=$port->write("?c0");
usleep(100000);
my $return1=$port->write("?f");
usleep(100000);
my $return2=$port->write("!!!!!!!!!!!!!!!!!!!!");
usleep(1000);
my $return3=$port->write("      Intruder?n");
usleep(1000);
my $return4=$port->write("       Beware?n");
usleep(1000);
my $return5=$port->write("!!!!!!!!!!!!!!!!!!!!");
usleep(1000);
while(1)

{

my $return_1=$port->write("?B00");
usleep(500000);
my $return_3=$port->write("?BFF");

sleep(1)

}

Thanks again, everyone.

Great :slight_smile:

Thanks for sharing the code :slight_smile:

@computer_freak:

Hmm, it just seems like if you're using a LCD on serial that just takes characters via serial and prints them, and it's on the same serial line as the computer... That your perl script would be printing directly to the LCD?

I could be confused here, but I don't see how there is a distinction between the perl script printing to the lcd and the arduino printing to the lcd... It seems like, given the code you've shown, you should just be getting the same text twice on the lcd... Then again, I haven't used these serial lcd's so there may be something I'm missing.

This code, right here is what looks like the smoking gun to me, especially combined with the comment "the perl script was sending too fast.." Too fast for what? The LCD's required delay between data being sent on the serial line? =)

 if (Serial.available()) {
    delay(100);
    Serial.write(Serial.read());
  }

That is, effectively, just echoing back on the same line you received the data you received, 100ms after you receive it.

What happens when you remove that block of code? Does the LCD still print the text properly?

!c

@drone:
Curious, (as I always am,) I tried the sketch with my old code, and then with the loop as just the opening and closing braces. It worked both ways.

Then, I took out the sleeps from the Perl script, and it did not work. The main issues I have (without Perl sleeping) is that it adds somewhat random characters, and the lines are off/repeating at odd intervals, instead of being formatted as I intended.

I say "somewhat random" because I think it only appears random. I think that, given my code in the Perl script, it is catching part of the last byte and the first of the next, and this translates into a (not quite) "random" character. Example: the lowercase letter "r" really likes to pop up at/near the beginning of a line.

This code, right here is what looks like the smoking gun to me, especially combined with the comment "the perl script was sending too fast.." Too fast for what? The LCD's required delay between data being sent on the serial line? =)

Here's my only real theory on this:
My computer = 2.66 GHz x 4 cores.
Arduino = 16 MHz x 1 core.
According to my calculations, assuming that the computer is using only one core for the Perl script, it has 166.25 times the speed of the Arduino. I don't know about you, but if someone were talking even 100 times as fast as my ears could hear, I'd probably be catching "somewhat random" parts of what was actually being said. Then again, the USB 2.0 standard does not allow for anywhere near 2 billion bits per second to be transmitted/received; if it did, I doubt there as many Ethernet networks as there are.

Does this make sense? Also, if this is incorrect, will someone out there with more knowledge please correct me?

The rate at which data is sent on a serial connection has very little, if nothing at all, to do with the speed of your computer (assuming it's fast enough to send at the baud rate). If you notice, you initialized the serial connection at 9600 BPS - that is the maximum rate at which data will be sent, no matter how fast your connection is. The LCD has a different timing issue, which is how long to wait between sending data to it. This is necessary so that the LCD may know its received everything you intend, and time to process it.

If, in fact, your perl script works when the arduino is not actually doing anything at all - that means that yes, the problem is that you are directly sending to the LCD via your perl script, and you were not waiting long enough between transmissions (that is different than "sending too fast" -- the data is sent at the same rate no matter what, it's just that you're not waiting long enough between transmissions). The LCD's driver should define the minimum time period between transmissions, and if you intend to send directly to the LCD via the perl script, your perl script will have to adhere to the timing of the LCD.

However, if you wish to do anything other than send the data directly to the LCD from your perl script, such as send a command to the arduino, and have the arduino decide what to send to the LCD... You will need to use a different serial connection (such as software serial) on the arduino to connect to the LCD, otherwise the LCD will continue to just print whatever the computer is sending.

That is to say, you could achieve the same effect you have now by completely removing the arduino from the picture, and connecting the serial RX/TX pins on the LCD to the RX/TX pins on a standard serial port. There is no "addressing" on serial connections, if you have the LCD plugged into the hardware serial on the arduino, you are talking to both at the same time from your computer.

!c

Oh, duh, the baud rate!
Thanks for clarifying that; it makes sense now. I don't know my LCD specifications, so I can't say for sure.

However, another question came up: How do I turn on an LED, (from the Perl script,) with or without it printing the command out on the LCD?

You need to be careful with the baud rate, some serial LCD backpacks are pre-programmed to work at a certain baud rate

Some do 19200, 9600 or 2400

It is quite possible yours is set at 9600 or 2400

Serial LCD backpacks intended for microcontrollers are often set at 2400

I looked at the documentation but could not find the baud rate

You can write a switch case statement in your C program on the duino to turn an LED on and off

    char ch = Serial.read();
    switch(ch) {
      case '0':
              digitalWrite(ledPin, LOW);
              break;
      case '1':
              digitalWrite(ledPin, HIGH);
              break;
      case '?':
              // got an LCD command
              LCD_command = Serial.read();
              // send "?" and command to LCD
              ...
              ...
              break;

Sorry, the code is missing a few lines, these depend on your implementation

You will need to make a few other modifications as well

:slight_smile:

Hmm... I don't really understand that code... maybe I wasn't quite clear. What I am attempting to do is have something like this (note the bold) :wink:
.
.
.

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

my $returnPRE=$port->write("digitalWrite(12, HIGH);");
usleep(100000);
my $return0=$port->write("?c0");
usleep(100000);
my $return1=$port->write("?f");
usleep(100000);
my $return2=$port->write("!!!!!!!!!!!!!!!!!!!!");
usleep(1000);
my $return3=$port->write(" Intruder?n");
usleep(1000);
my $return4=$port->write(" Beware?n");
usleep(1000);
my $return5=$port->write("!!!!!!!!!!!!!!!!!!!!");
usleep(1000);

.
.
.

Is this possible?
Note: I put the "code" tags like that on purpose so the bold tags would work.

my $returnPRE=$port->write("digitalWrite(12, HIGH);");

Sorry, you cannot do that

You could modify the C code in arduino to look for a special command like a single '0' for off and a single '1' for on

Of course this will prevent you from printing a single '0' or '1' to the LCD unless you code around that

The switch case statement in the example was attempting to do that

:slight_smile:

Okay, I need some help with debugging. Here are my three programs:

Arduino sketch

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>

LiquidCrystal lcd(13, 12, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int Pin11=11;

void setup()
{
  Serial.begin(9600);
}


void loop()
{
  char ch = Serial.read();
  switch(ch) 
  {
  case '0':
    lcd.write('?c0?f?B00');
    digitalWrite(Pin11, LOW);
    break;

  case '1':
    digitalWrite(Pin11, HIGH);
    lcd.write('!!!!!!!!!!!!!!!!!!!!');
    lcd.write('      Intruder?n');
    lcd.write('       Beware?n');
    lcd.write('!!!!!!!!!!!!!!!!!!!!');
    lcd.write('?BFF');
    delay(500);
    lcd.write('?B00');
    delay(500);
    lcd.write('?BFF');
    delay(1000);
    break;
  }
}

LIB0 (Perl script to output 0s)

#!/usr/bin/perl -w

### Keep in mind that we need
### to sleep a lot so that the
### LCD/Arduino can keep up!


use POSIX qw(setsid);
use Time::HiRes qw(usleep);

chdir '/'                 or die "Can't chdir to /: $!";
umask 0;
#open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
#open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
#open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
defined(my $pid = fork)   or die "Can't fork: $!";
exit if $pid;
setsid                    or die "Can't start a new session: $!";



use Device::SerialPort;

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

while(1)
{
my $return0=$port->write("0");
usleep(100000);
my $return1=$port->write("0");
usleep(100000);
my $return2=$port->write("0");
usleep(100000);
}

LIB1 (Perl script to output 1s)

#!/usr/bin/perl -w

### Keep in mind that we need
### to sleep a lot so that the
### LCD/Arduino can keep up!


use POSIX qw(setsid);
use Time::HiRes qw(usleep);

chdir '/'                 or die "Can't chdir to /: $!";
umask 0;
#open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
#open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
#open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
defined(my $pid = fork)   or die "Can't fork: $!";
exit if $pid;
setsid                    or die "Can't start a new session: $!";



use Device::SerialPort;

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

while(1)
{
my $return0=$port->write("1");
usleep(100000);
my $return1=$port->write("1");
usleep(100000);
my $return2=$port->write("1");
usleep(100000);
}

I plug in the Arduino, and the LCD is cleared with full backlight and the LED off. I run LIB1 and the LED turns on, and the LCD screen rapidly fills with the number 1 for each character on the screen. So, I unplug the Arduino and kill the script. Then, I run the LIB0 script. The same thing happens, (LED on, screen fills,) except that the screen is filled with the 0s instead of 1s.

Edit: I had the LED in an incorrect pin. I corrected that problem, and the LED still comes on, but it is very dim, especially compared to what it was when it was in the wrong spot.

Computer_freak:

(Starting to feel a little repetitious, but I know it must be hard the first time you try this =)

If your Serial LCD (don't use the LCD library for serial LCDs -- It's for parallel LCDs, so is non-operative in your case.) is hooked up to pin 0/1 on the arduino, it is ALSO directly connected to the serial port on the computer! The arduino is not acting as some gateway on the serial, it cannot.

When you send "1" to the serial from the perl script, you are sending it to the Arduino and the LCD at the same time!

If you want to talk to the arduino from the computer, and have the arduino talk to the LCD for the computer you must use SoftwareSerial. You can find the explanation of this library in the documentation here on the site.

Until you take the LCD off the hardware serial pins on the arduino (digital 0/1), you will not be able to make the arduino a gateway between the computer and the LCD, as long as it is attached to the hardware serial pins on the arduino, it is also directly connected to the PC serial.

So, to clarify:

  1. Remove all the references to the LiquidCrystal library and lcd.* from your sketch. It doesn't work with serial LCDs.

  2. You've included software serial, but not done anything with it. Attach your LCD to some pins other than digital 0/1, and initialize software serial for those pins.

  3. Use software serial you create to communicate with the LCD

!c

@drone:
Thanks for the response (again).

This is definitely difficult, as I am not only new to programming, but I am new to interfacing with hardware like this. I'm trying, and thank you for your patience. I really do appreciate it.

I think part of what might have been happening is that I was getting terms such as "serial LCD", "Software Serial" and "LCD library" mixed up with each other. I think I get it now, though. I'm going to play around with it a bit, and I'll report back later.

Thanks again!

Have you tried using BBB or RBBB Pin 13?

Pin 13 has a resistor so you can connect an LED directly to it

The other pins do not and they require an external resistor to drive an LED since LEDs are 3.3V and the arduino pins are 5V

:slight_smile:

@computer_freak:

No problems =) We all have to start somewhere!

The easy to think of the differences between serial/parallel LCDs:

  1. Serial LCDs have just two pins (RX/TX) to communicate with them - you use a serial protocol (as in one-byte-at-a-time) to communicate with them. They are more expensive than parallel LCDs, but much easier to use. You interface with them either using the hardware serial built in, or via the software serial library.

  2. Parallel LCDs have numerous data pins, they cost less but require more I/O lines to communicate with them. They receive their commands through multiple pins, where each pin represents part of the data -- to interface with them, you must either write a lot of code to change the states of pins, or you use a library like LiquidCrystal that does it for you.

The software serial library was specifically written to allow you to communicate with things like serial LCDs while still allowing you to interface the arduino with some other device (e.g. your computer) on the faster built-in serial lines. Just remember that hooking up multiple things to the hardware serial is just like soldering them all together. They will all receive the same information.

!c

Alright, I've played around with this a bit, and I have gotten a little ways, but not too far.

At one point, I was able to run the Perl script for 1s and have the LED come on, the LCD to slowly fill with the two characters "gA", and the Arduino to become unresponsive to anything else, even after killing the script. With that code, the Perl script for 0s did nothing. (It didn't turn off the backlight, nor counteract what the 1s script started.)
Unfortunately, I forgot to save a copy of this code, and I can't seem to reproduce it.

The code I think I came the closest with is this:

#include <ctype.h>

#define bit9600Delay 84  
#define halfBit9600Delay 42
#define bit4800Delay 188 
#define halfBit4800Delay 94 

byte rx = 0;
byte tx = 7;


void setup() {
  pinMode(rx,INPUT);
  pinMode(tx,OUTPUT);
  digitalWrite(tx,HIGH);
  Serial.begin(9600);
}

void SWprint(int data)
{
  byte mask;
  //startbit
  digitalWrite(tx,LOW);
  delayMicroseconds(bit9600Delay);
  for (mask = 0x01; mask>0; mask <<= 1) {
    if (data & mask){ // choose bit
     digitalWrite(tx,HIGH); // send 1
    }
    else{
     digitalWrite(tx,LOW); // send 0
    }
    delayMicroseconds(bit9600Delay);
  }
  //stop bit
  digitalWrite(tx, HIGH);
  delayMicroseconds(bit9600Delay);
}
void loop()
{
  char ch = Serial.read();
  switch(ch) 
  {
  case '0':
    SWprint('?c0?f?B00');
    digitalWrite(13, LOW);
    break;

  case '1':
    digitalWrite(13, HIGH);
    SWprint('!!!!!!!!!!!!!!!!!!!!');
    SWprint('      Intruder?n');
    SWprint('       Beware?n');
    SWprint('!!!!!!!!!!!!!!!!!!!!');
    SWprint('?BFF');
    delay(500);
    SWprint('?B00');
    delay(500);
    SWprint('?BFF');
    delay(1000);
    break;
  }
}

I could turn the LED on and off at will, but nothing happens with the LCD. The reason I feel that this is the closest is that I at least know that the "case" statement is working.

There is another set of code that I tried; it did nothing as far as I could tell. Here it is:

#include <ctype.h>

#define bit9600Delay 84  
#define halfBit9600Delay 42
#define bit4800Delay 188 
#define halfBit4800Delay 94 

byte rx = 0;
byte tx = 7;


void setup() 
{
  pinMode(rx,INPUT);
  pinMode(tx,OUTPUT);
  digitalWrite(tx,HIGH);
}

void SWprint(int data)
{
  byte mask;
  //startbit
  digitalWrite(tx,LOW);
  delayMicroseconds(bit9600Delay);
  for (mask = 0x01; mask>0; mask <<= 1) 
  {
    if (data & mask) // choose bit
    {
      digitalWrite(tx,HIGH); // send 1
    }
    else
    {
      digitalWrite(tx,LOW); // send 0
    }
    delayMicroseconds(bit9600Delay);
  }
  //stop bit
  digitalWrite(tx, HIGH);
  delayMicroseconds(bit9600Delay);
}

// int SWread()
// {
//   byte val = 0;
//   while (digitalRead(rx));
//   //wait for start bit
//   if (digitalRead(rx) == LOW) 
//   {
//     delayMicroseconds(halfBit9600Delay);
//     for (int offset = 0; offset < 8; offset++) 
//     {
//       delayMicroseconds(bit9600Delay);
//       val |= digitalRead(rx) << offset;
//     }
//     //wait for stop bit + extra
//     delayMicroseconds(bit9600Delay); 
//     delayMicroseconds(bit9600Delay);
//     return val;
//   }
// }


void loop()
{
//  char ch = SWread();
    char ch = Serial.read();
  switch(ch) 
  {
  case '0':
    SWprint('?c');
    delay(100);
    SWprint('?f');
    delay(100);
    SWprint('?B00');
    delay(100);
    digitalWrite(13, LOW);
    break;

  case '1':
    digitalWrite(13, HIGH);
    SWprint('!!!!!!!!!!!!!!!!!!!!');
    SWprint('      Intruder?n');
    SWprint('       Beware?n');
    SWprint('!!!!!!!!!!!!!!!!!!!!');
    SWprint('?BFF');
    delay(500);
    SWprint('?B00');
    delay(500);
    SWprint('?BFF');
    delay(1000);
    break;
  }
}

Any ideas?

Note: The Perl scripts for the 1s and 0s are the same ones I posted in this thread.