http://www.perlmonks.org?node_id=481318

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

I've got a network device connected to my COM1 (serial) port, which is itself connected to several data collecting devices (barcode readers, etc). when these child devices have data, they send it to this parent network device, which stores the data until MY machine polls it for data (by sending a 'POLL' character, the ASCII ENQ char). if it's polled but has no data to send, it responds with a 'RESET' character (the ASCII EOT char)

I'm using Win32::SerialPort to handle all the dirty stuff, and I just need to receive the data in a stream. I know the network device will always send data in chunks LESS THAN $BUFFER amount of bytes, so I tried to read() or sysread() that many bytes from the serial port, but that seems to block until it gets AT LEAST that many bytes.

I guess i need a way to read UP TO $BUFFER amount of bytes until the stream is empty, then return control.

::takes a breath:: So..can anyone help me figure out WHAT to do and HOW to do it? i greatly appreciate the help :)
#!/usr/bin/perl ## Includes #################################################### # import any modules or pragmas we may need use strict; use Win32::SerialPort qw( :STAT 0.19 ); ## Initialization ############################################## # get everything defined and set up for the main loop, which # will sit on COM1, soliciting (polling) the controller # This hash contains all control character acronyms as hash keys, # with their respective ASCII values as corresponding hash values my %IM = ( 'SOM' => chr(2), #Start of message (STX) 'EOM' => chr(3), #End of message (ETX) 'AFF' => chr(6), #Affirmative Acknowledge (ACK) 'NEG' => chr(21), #Negative Acknowledge (NAK) 'POL' => chr(5), #Poll for data (ENQ) 'SEL' => chr(7), #Alarm (BEL) 'EOR' => chr(13), #End of record (CR) 'DLE' => chr(16), #DLE 'RES' => chr(4), #Reset (EOT) ); # Software-side buffer size, the maximum amount of data that # any single transmission will contain my $BUFFER = 2048; # create object bound to COM port, tie it to filehandle SERIAL, # and load settings from a config file my $PortObj = tie (*SERIAL, 'Win32::SerialPort', "C:\\com.cfg") || die + "Can't open COM1: $^E\n"; # some global variables for stat tracking (not yet implemented) my($upstream,$downstream) = 0; # create a flag variable initialized to zero... my $EXIT = 0; # ...when system signal INTERRUPT is sent, set flag to 1 $SIG{INT} = sub { $EXIT = 1 }; # begin new append to logfile open(LOG,">>C:\\COMDUMP.LOG"); print LOG "\n\nBEGIN: ",time,"\n"; ## Main Loop ################################################### # poll host for data every second, when data is recieved, print # it to STDOUT and a logfile (for now) print "\nPress Ctrl+C to exit\n\n"; my $temp; # main loop, continues until user sends INTERRUPT (ctrl+c) while($EXIT == 0) { $temp = pollForData(); if(defined($temp)) { print $temp; print LOG $temp; } sleep(1); } ## Cleanup ################################################### # close any COM ports, untie any filehandles, undefine objects, # and just tidy up in general before we exit close(LOG); $PortObj->close || die "failed to close"; untie *SERIAL; undef $PortObj; ## Subroutines ################################################# # any repetetive actions that can be reused, we'll store in here # as callable subroutines # polls the controller, retreives, and returns data, or returns # undef if there is no data sub pollForData { my($length,$buffer); # Poll controller to send any pending data (if there is none, # it will send back a 'RES' control character print SERIAL $IM{'POL'}; $upstream++; # read in $BUFFER amount of bytes $length = sysread(SERIAL,$buffer,$BUFFER); # update num of bytes received so far $downstream += $length; # if reset signal sent, there's no data pending, return undef if($buffer eq $IM{'RES'}) return undef; # otherwise, return data return $buffer; }

__________
Give a man a match and he'll be warm for an hour. Set him on fire and he'll be warm for the rest of his life.

Replies are listed 'Best First'.
Re: issues reading from serial port
by dave_the_m (Monsignor) on Aug 05, 2005 at 20:34 UTC
    Well, in the UNIX world the trick would be to open the device nonblocking, ie with the O_NONBLOCK flag, or use fcntl to set it later; but I don't know much about windows and what it supports. I suppose you could try
    use Fnctl; sysopen(HANDLE, $path, O_NONBLOCK);
    and see if it works under windows.

    Dave.

      well, I'm using the Win32::SerialPort module, and it doesnt use sysopen() at all, it simply tie()s the filehandle to the serial stream.

      __________
      Give a man a match and he'll be warm for an hour. Set him on fire and he'll be warm for the rest of his life.

Re: issues reading from serial port
by PodMaster (Abbot) on Aug 06, 2005 at 09:31 UTC
    The documentation says:
    Because all the tied methods block, they should ALWAYS be used with timeout settings and are not suitable for background operations and polled loops. The sysread method may return fewer characters than requested when a timeout occurs. The method call is still considered successful. If a sysread times out after receiving some characters, the actual elapsed time may be as much as twice the programmed limit. If no bytes are received, the normal timing applies.
    Timeouts are also discussed further down in the documentation. They are also used in some of the demos. Good luck.

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      Ah! i can't believe I missed the answer and it was IN the documentation! I'm so ashamed...What can i say, it was a very long workday. ::shrugs apologetically::

      UPDATE: ok, this is kind of wierd, it seems that everything is blocked from printing in the main loop until I ctrl+c, after which it all prints out and dumps back to command line.
      while($EXIT == 0) { $temp = pollForData(); print $temp; print LOG $temp; sleep(1); }
      however, if i print the returned data and include something else within the same print command, it works fine.
      while($EXIT == 0) { $temp = pollForData(); print "$temp\n"; print LOG $temp; sleep(1); }
      the thing is, i don't want to print the data every iteration through the loop, but otherwise NOTHING at all happens until i ctrl+c...OH, almost forgot. the pollForData sub:
      sub pollForData { my($length,$buffer); # Poll controller to send any pending data (if there is none, # it will send back a 'RES' control character print SERIAL $IM{'POL'}; $upstream++; # read in $BUFFER amount of bytes $length = read(SERIAL,$buffer,$BUFFER); # update num of bytes received so far $downstream += $length; # return data return $buffer; }

      __________
      Give a man a match and he'll be warm for an hour. Set him on fire and he'll be warm for the rest of his life.