in reply to serial port tied filehandle autoflush

In my experience, getting serial ports working with the higher-level reading functions (also timeouts and graceful closing of the ports) can be quite finicky. In fact, in a recent script I wrote that does something similar to yours, I just read the port one byte at a time and do the buffering myself, it's not the most elegant but it's robust. The code resembles the "EXAMPLE" in Device::SerialPort.

However, from perlvar:

HANDLE->autoflush( EXPR ) ... This has no effect on input buffering. See getc for that.

I don't see you writing to the port anywhere. So I don't think "autoflush" is the right term here, and perhaps you need to explain your problem better: In regards to what is being read/written from the port, what exactly is the expected behaviour, and what behaviour are you getting instead? Perhaps a short sample would help?

How can I create a filehandle for this module that it will always return me the last line from the serial port output?

By "last", do you mean "most recent"? I see a couple of options: Keep the port closed until you actually want to read; continually read the port and just remember the most recent line; or try the lookclear method from Device::SerialPort (although the docs are a little unclear to me whether lookclear will do what you need).

Replies are listed 'Best First'.
Re^2: serial port tied filehandle autoflush
by kkaiser (Initiate) on Dec 05, 2014 at 07:56 UTC

    Thanks for your response,

    the serial port is a heading compass. It will give you heading strings like:

    $HEHDT,123.49,T*12 $HEHDT,123.43,T*18 $HEHDT,123.43,T*18 $HEHDT,123.49,T*12 $HEHDT,123.49,T*12 $HEHDT,123.42,T*19

    According to the direction from true north (0°-359.9°). The last value is a checksum.

    I never write to the port. I just want the most recent heading (yes that's what I meant with last, sorry about that). The problem is the serial interface is rather fast. The output comes 10 times a second or one time a second.

    In my main routine I don't want the heading that fast. I just want it when I need it. When I call my method I don't get the most recent value I get a value from the buffer.

    There is many different sorts of compasses also some that output much more then just the heading. (GPS and other NMEA strings). I liked the tied filehandle approach (SerialPort::Port Methods for tied filehandles). My code already does what I want but I always retie or recreate the object. That way the first string I get is incomplete and thus invalid all the time plus the time I have to recreate the object, which is still fast but I don't believe it's the proper way.

    Here's my two different Log::Log4perl output:

    What I get:

    05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving ¤EHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:19 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:276)> + Not receiving valid NMEA data or cut off string: ¤EHDT,217.62,T*1F ! 05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:19 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1F', 'TrueHeading' => '217.62', '_STRING_' => '$HEHDT,217.62,T*1F', '_calcCKS_' => '1F', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:46:19 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 217.62 ----------------------------------------------------- 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving ¦L“ÉS‰•)ÓTHø$HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:20 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:276)> + Not receiving valid NMEA data or cut off string: ¦L“ÉS‰&# +149;)ÓTHø$HEHDT,217.62,T*1F ! 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,217.62,T*1F on /dev/nmea 05.12.2014-08:46:20 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1F', 'TrueHeading' => '217.62', '_STRING_' => '$HEHDT,217.62,T*1F', '_calcCKS_' => '1F', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:46:20 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 217.62 ----------------------------------------------------- Heading ACU: FE0080DA000000000000 Heading degrees: 217.62
    What I expect:
    05.12.2014-08:43:02 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,123.47,T*1C on /dev/nmea 05.12.2014-08:43:02 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '1C', 'TrueHeading' => '123.47', '_STRING_' => '$HEHDT,123.47,T*1C', '_calcCKS_' => '1C', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:43:02 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 123.47 ----------------------------------------------------- 05.12.2014-08:43:04 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:271)> + Receiving $HEHDT,123.48,T*13 on /dev/nmea 05.12.2014-08:43:04 TRACE AA0009E (GPS/NMEAserial.pm:get_heading:292)> + $VAR1 = bless( { '_CKS_' => '13', 'TrueHeading' => '123.48', '_STRING_' => '$HEHDT,123.48,T*13', '_calcCKS_' => '13', '_ELEMENT_' => 'HDT', 'HDTT' => 'T', '_ERROR_' => undef, '_DEVICE_' => 'HE' }, 'NMEAdata' ); 05.12.2014-08:43:04 DEBUG AA0009E (GPS/NMEAserial.pm:get_heading:295)> + Received true heading: 123.48 ----------------------------------------------------- Heading ACU: FE00807B000000000000 Heading degrees: 123.48

    If there's more strings involved it just will take longer when there is only one dataset per second.

    So I would expect that I don't have to retie the serial port and not to get the first "bad" line all the time.

    If there's another way I could get rid of the filehandle I would be open for it. Important is just that I can use something similar to readline() to process all the strings I need. Like GPS and Heading for instance:

    $GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F $GPRMB,A,,,,,,,,,,,,V*71 $GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75 $GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D $GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71 $GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77 $PGRME,22.0,M,52.9,M,51.0,M*14 $GPGLL,3907.360,N,12102.481,W,183730,A*33 $GPHDM,093.8,M*2B $PGRMZ,2062,f,3*2D $PGRMM,WGS 84*06 $GPBOD,,T,,M,,*47 $GPRTE,1,1,c,0*07 $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67 $GPRMB,A,,,,,,,,,,,,V*71
    I need to go through all lines to find $GPRMC and $GPHDM which is what I want.

    Thanks for your help. It's really appreciated.

      Thanks for the additional information. To get the most recent line, there are the three previously mentioned approaches:

      1. Keep the port closed until you actually want to read. Advantage is that there is no CPU used while you don't want to read the port. Disadvantage is that the first line you read will very likely be partial, but since NMEA is checksummed you can identify and discard bad lines (or just always discard the first line). Since you want to read the port less than once per second, I wouldn't worry too much about the overhead of re-opening the port (unless it becomes a measurable issue).
      2. Continually read the port and remember the most recent line. Advantage is that you have full control over buffering and which line you keep and return to your program, and you won't get partial lines except for the very first one. Disadvantage is that some extra CPU is used to read and discard the unnecessary lines.
      3. Keep the port open, but clear the input buffers before reading. Not having used the tied interface before, I can't say for certain, but from the docs it sounds like Device::SerialPort's lookclear method might do what you want. Note that if the buffers are not line-based but byte-based, then after clearing the buffers it's very likely you'd get partial lines like in the first approach above. Another possible disadvantage to this method is that it's more wasteful than the first if you only read rarely, plus I don't know off the top of my head what the driver's behavior is if you let the input buffers fill up.
      So I would expect that I don't have to retie the serial port and not to get the first "bad" line all the time.

      In that case that limits you to the second or third approaches. (Although personally I don't mind getting and discarding garbled first lines when opening a serial port - that's always a side effect of receiving a continuous stream of data on a serial port.)

      My suggestion would be that (if you have the time for it) you could try out the third approach, and if that does turn out to have issues, go with the first or second approach depending on how often you want to read from the port (if often, I'd go with the second, if less often, the first).