garlinto has asked for the wisdom of the Perl Monks concerning the following question:

Howdy folks! New here but not new to programming in general...most of my background is in php. Right now however, I am playing around with home automation using Xbee transceivers connected via serial over USB (via FTDI chip). Using CentOS 6.2. I am currently using this code for reading data from the USB port:
#!/usr/bin/perl use strict; use warnings; use IO::Handle; open( COM, "/dev/ttyUSB0") || die "Cannot read serial port : $!\n"; while( <COM> ){ # do stuff here }
This works very well, but makes it difficult for writing data to the port. My guess is, after much googling, that the port is being blocked by the open command/while loop and making it to where I can't use another script to open the same port for write access. There are a number of Event modules in perl for handling event-driven communications, but after reading some posts here, seems like overkill for my simple app. Ideally, this is what I would love to do: In the gnokii package (a tool for reading/writing to a nokia phone for sending sms messages, etc) is a daemon called smsd. I LOVE this little daemon: whenever a text message is received, the daemon writes the message data to a database table. Conversely, when an insert is made into the outbox table, the daemon gets wind of this and sends the text message. I would love to find/write a similar daemon for doing the same thing over a USB/serial port, i.e. writing incoming data to a "received" table and then sending data that is inserted into a "send" table. However, such a daemon is way over my head I am sure, which is why I am trying to do this in perl with as little code as possible. Looking at my example above, what would be the best way to read and write to a serial port with perl? Should I run a perl script as a cron job every 30 seconds or so to see if there is any incoming data, and then use another script that sends data when needed, first checking to make sure that the port can be opened for writing? Am I really over thinking this and there already exists a simple solution?

Replies are listed 'Best First'.
Re: Best way to read/write to a serial port
by Khen1950fx (Canon) on Jan 11, 2012 at 22:48 UTC
    Here's a little script to get you up and running. Note that I used a tty rather than a ttyUSB---you can use any tty, stty, or ttyUSB. On my system, the ttyUSB is usbdev.
    #!/usr/bin/perl -l use strict; use warnings; use Device::SerialPort qw( :PARAM :STAT 0.07 ); $| = 1; my $port = '/dev/tty24'; my $conf = '~/.conf'; my $ob = Device::SerialPort->new($port, 1) || die "Can't open $port: $!"; my $STALL_DEFAULT = 10; my $timeout = $STALL_DEFAULT; my $arb = $ob->can_arbitrary_baud; my $data = $ob->databits(8); my $baud = $ob->baudrate(115200); my $parity = $ob->parity("even"); my $hshake = $ob->handshake("rts"); my $stop = $ob->can_stopbits; my $rs = $ob->is_rs232; my $total = $ob->can_total_timeout; $ob->stopbits(1); $ob->buffers( 4096, 4096 ); $ob->can_baud; $ob->can_databits; $ob->can_dtrdsr; $ob->can_handshake; $ob->can_parity_check; $ob->can_parity_config; $ob->can_parity_enable; $ob->can_rtscts; $ob->can_xonxoff; $ob->can_xon_char; $ob->can_spec_char; $ob->can_interval_timeout; $ob->can_ioctl; $ob->can_status; $ob->can_write_done; $ob->can_modemlines; $ob->can_wait_modemlines; $ob->can_intr_count; $ob->write_settings; print "A = $arb\n", "B = $baud\n", "D = $data\n", "S = $stop\n", "P = $parity\n", "H = $hshake\n", "R = $rs\n", "T = $total"; $ob->write_drain; $ob->close; undef $ob;
Re: Best way to read/write to a serial port
by flexvault (Monsignor) on Jan 11, 2012 at 21:55 UTC

    You may want to look at the 3 parameter 'open'.

    open( $com, "+<","/dev/ttyUSB0" ) || die "Cannot open serial port : $! +\n"; while( 1 ) { my $in = <$com>; # input # do stuff here ( You should be able to do both input and output w +ithin the loop. print $com "..."; # output if ( $in eq "quit" ) { last; } }

    The default behavior of a 2 parameter 'open' is output only. Look at the documentation for open for more information. Also look at 'read/write' and 'sysopen/sysread/syswrite' if you need to input/output a specific number of bytes. ( Especially if your going to pad with the "\0" null character. )

    Good Luck.

    "Well done is better than well said." - Benjamin Franklin

Re: Best way to read/write to a serial port
by wink (Scribe) on Jan 11, 2012 at 21:06 UTC

    I haven't done a lot of read/writing to ports so I don't know if my answer will be the best. But instead of keeping the handle open all the time, could you do a periodic poll?

    while(1) { open( COM, "/dev/ttyUSB0") or die "Can't read serial port : $!\n"; while(<COM>) { # stuff } close(COM); sleep(1); }

    However, I'm not sure what about Device::SerialPort is overkill to you. Seems like a pretty simple implementation to me. Here's an example of it being used (which also uses a sleep): http://www.windmeadow.com/node/38

Re: Best way to read/write to a serial port
by wrog (Friar) on Jan 11, 2012 at 22:39 UTC
    My guess is, after much googling, that the port is being blocked by the open command/while loop and making it to where I can't use another script to open the same port for write access. There are a number of Event modules in perl for handling event-driven communications, but after reading some posts here, seems like overkill for my simple app

    it may seem that way, but you need something to arbitrate between the different processes contending for a given resource; a serial port can only do one thing at a time. While you can try leaving it up to the OS, without any guidance, the OS is most likely going to fall back on something simple/stupid like "first process that gets there monopolizes it until it's done", which is evidently what you're seeing.

    My guess is select() is what you want. It may be annoyingly low level, but it was explicitly intended for this kind of situation, it's one of the builtins, thus guaranteed to be there, and it's a fairly old feature (read battle tested).

    Or you can grab somebody's event module...