Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Trying to read from serial port - not getting a response

by adiuva (Sexton)
on Dec 04, 2012 at 12:57 UTC ( #1007081=perlquestion: print w/replies, xml ) Need Help??
adiuva has asked for the wisdom of the Perl Monks concerning the following question:

Dear great perl masters, I've got this code which should send an AT-command to a serial port (with a modem behind that) and read the response from the modem. The sending of the command is working fine but I'm not getting any response in my loop. Do you maybe see the error and give me some advice?
use IO::Socket; use IO::Select; my $COMport=33; while (1) { #open Port or die if blocked / wrong port if (!open( PORT, "+>\\\\.\\COM$COMport" )) { die ("Can't open COM$COMport"); } PORT->autoflush(1); PORT->flush(); print "\nEnter AT command:"; my $ATcmd=<STDIN>; chomp($ATcmd); # remove CRLF $ATcmd =~ s/^\s+//; # remove leading space $ATcmd =~ s/\s+$//; # remove trailing space print "Sending: '$ATcmd'\n"; print PORT $ATcmd . "\r\n"; print "reading\n"; #Reading the response while (my $ret=<PORT>) { print $ret; if ($ret=~/^OK/ || $ret=~/^ERROR/) { last} } close(PORT); } print "done!";
Thank you in advance! Regards, Stefan

Replies are listed 'Best First'.
Re: Trying to read from serial port - not getting a response
by graff (Chancellor) on Dec 05, 2012 at 02:20 UTC
    Do you have any method at all (apart from a perl script) that can let you know that stuff really does come in from the serial port? It would be a comfort, at least, to rule out hardware problems (e.g. having a badly wired RS-232 connector).

    Apart from that, you might need to be more explicit about the file handle discipline being used for reading from the port. You seem to be using a default read protocol, whereby a line-termination pattern (probably "\r\n" in your case) needs to be detected by perl in order to return a value from the file handle via the <PORT> read operation.

    How about trying a read or sysread instead? That is, something that will read some quantity of bytes, regardless of whether there's any specific "record delimiter" character to be looked for. update:It wouldn't be a bad idea to use one of these functions to read just one byte at a time; that way, it's unlikely that anything will be missed by virtue of being "cached" by the file-handle object.

      Well, if i connect to the modem directly with Putty for example and execute the same command I'm getting the expected feedback. I also tried different modems with the same result. I think for some reason the response is not ending up the file handle. If you look at the first code I posted you see that the loop to output the response should only be entered if there is anything in the filehandle. But it never entered it. In the second code it's just getting stuck as there is nothing in the filehandle as well. Could you maybe explain more detailed (or provide links) what you mean with "more explicit file handle discipline"? I'm not a very experienced programmer so I would need more input to understand :) I tried both now - read and sysread. But both loops are not beeing entered at all:
      my $read; my $sysread; #Reading the response print "reading...\n"; while ( read(PORT,$read,2048) ) { print "using read:\n"; print $read; } while (sysread(PORT, $sysread, 10000)) { print "using sysread:\n"; print $sysread; }
      Here's what's happening:
      c:\Dropbox\Scripts>perl Enter AT command:ATI0 Sending: 'ATI0' reading... done! c:\Dropbox\Scripts>
        When you say "both loops are not being entered at all", I would guess that it's because you have asked the "read" or "sysread" to return at the point where the serial-port device has sent 2048 or 10000 bytes worth of content, and maybe the device isn't sending that much data. (How much data are you expecting to get back after you send a given "AT" command to the device?)

        Have you tried it this way?

        my $buffer; while (1) { read( PORT, $byte, 1 ); $buffer .= $byte; if ( length( $buffer )) { printf( "Buffer now has %d bytes; last byte read was 0x%02x\n" +, length( $buffer ), $byte ); } }
        You can kill that process (i.e. ^C or some other method) when you feel that it has run long enough so you know what's going on. Then you can add logic inside the while loop so that when the input buffer attains some particular state (or the last byte was some particular value), you break out of the loop and do something else.

        The term "discipline" applied to reading from a file handle covers things like: (a) how to delimit input records (i.e. either by number of bytes, or by watching for a particular byte value or byte sequence; and (b) whether or not your process should wait for data when it tries to read (i.e. using a "blocking" vs. "non-blocking" read - "blocking" is the default).

        It's been a while since I've tried to play with non-blocking reads, but you should be able to look that up. The basic idea is that when "read" is called, it always returns pretty much right away; if the device has data to be read, your input buffer gets the data, otherwise your input buffer remains empty and your process continues to execute. This is how people handle devices where input data arrives at unpredictable intervals, but other things have to be done while waiting for input.

        The example above assumes blocking input (read doesn't return until the device has delivered a byte to be read). If you were to convert it to use a non-blocking input, you'd want to set $byte to empty string or undef before the read call, then check if it contains a value after the read; if it does, then append to the buffer and print the report. (When using non-blocking input, it can also a good idea in many cases to include a "sleep()" call, to keep the loop from consuming too many cpu cycles.)

        I found something else that might be useful regarding read() and sysread():

Re: Trying to read from serial port - not getting a response
by ColonelPanic (Friar) on Dec 04, 2012 at 13:22 UTC

    The first time you reach the part that reads the response, there probably won't be anything there yet, because it takes some time for the response to come back. However, your outer while loop contains a flush(), which will discard any data before you see it.

    I suggest, at least to start out, that you redo your code in a simpler format: open the port only once, send your data, then have a while loop that reads the port until you see data. i.e. use something like this to read the result:

    while (1) { my $ret=<PORT> print $ret; if ($ret=~/^OK/ || $ret=~/^ERROR/) { last} }

    Caveat: not tested; I don't really have a way to do so.

    When's the last time you used duct tape on a duct? --Larry Wall
      I got it "simpler" before changing to this version, but it didn't work either. Here's the modified version:
      use Strict; use IO::Socket; use IO::Select; my $COMport=33; #open Port or die if blocked / wrong port if (!open( PORT, "+>\\\\.\\COM$COMport" )) { die ("Can't open COM$COMport"); } PORT->autoflush(1); PORT->flush(); print "\nEnter AT command:"; my $ATcmd=<STDIN>; chomp($ATcmd); # remove CRLF $ATcmd =~ s/^\s+//; # remove leading space $ATcmd =~ s/\s+$//; # remove trailing space print "Sending: '$ATcmd'\n"; print PORT $ATcmd . "\r\n"; sleep(2); #Reading the response print "reading\n"; while (1) { my $ret=<PORT>; print $ret; if ($ret=~/^OK/ || $ret=~/^ERROR/) { last} } close(PORT); print "done!";

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1007081]
Approved by marto
[choroba]: Tux Why? It works for me with 16 :-)
[Tux]: and the defailt is 15?
[choroba]: sorry, typo. Works for me with 15 which is the default
[choroba]: Timestamps should work correctly now

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (8)
As of 2017-09-19 20:14 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (228 votes). Check out past polls.