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

Dear Bretheren, I have this great gadget, called the BMV-600s, a battery monitor, which with a means of a shunt is capable of very precise estimation of state of charge of my battery array (which is powerd by solar panels). Long story short. The device transmitts data via an RS238 interface. It send out the following data every 1 second:
V 24453 I -717 CE -23571 SOC 835 TTG 4559 Alarm OFF Relay OFF AR 0 BMV 600S FW 212 Checksum � 4: H1 -27277 H2 -27277 H3 0 H4 0 H5 0 H6 -48242 H7 17697 H8 28750 H9 140703 H10 1 H11 0 H12 0 Checksum
This is the code I use to get this data:
#!/usr/bin/perl use warnings; use strict; use Device::SerialPort; my $ccnt; my $string; my $port = '/dev/ttyUSB0'; 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(19200); my $parity = $ob->parity("none"); 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"; my ($count, $string); while(1) { $ccnt++; $ob->are_match("Checksum", "\n"); ($count, $string) = $ob->read(500); #read 500 bytes #print "$ccnt: count=$count string=$string\n\n" if ($string); #$string =~ m/(.*?)V\t(\d*)\n\rI\t(\d*)\n\rCE\t(\d*)\n\rSOC\t( +\d*)\n\rTTG\t(\d*)\n\r(.*?)/; #my ($v, $i, $ce, $soc, $ttg, $more) = ($1,$2,$3,$4,$5,$6); #print "$ccnt: ($v, $i, $ce, $soc, $ttg, $more)\\n"; $string =~ m/V\t\d*\n/; print "$ccnt: $string\n"; sleep(1); } $ob->write_drain; $ob->close; undef $ob
Hence 2 questions: 1. How do I parse the data that I get? 2. How do I configure the READ from the COM port so it syncs with the transmission? riht now I'm just somehow getting chuncs of data via
($count, $string) = $ob->read(500); #read 500 bytes
, and the $ob->are_match("Checksum", "\n"); does not really work (The word Checksum with a hex value actually closes the one batch of data transmission). A great many thanks to all!

Replies are listed 'Best First'.
Re: parse data from serial port
by CountZero (Bishop) on Oct 15, 2013 at 06:37 UTC
    I read in the docs of Device::SerialPort:
    Lookfor and I/O Processing

    Some communications programs have a different need - to collect (or discard) input until a specific pattern is detected. For lines, the pattern is a line-termination. But there are also requirements to search for other strings in the input such as "username:" and "password:". The lookfor method provides a consistant mechanism for solving this problem. It searches input character-by-character looking for a match to any of the elements of an array set using the are_match method. It returns the entire input up to the match pattern if a match is found. If no match is found, it returns "" unless an input error or abort is detected (which returns undef).

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
      thank you everybody! this is how I got the name/value pares into variables
      #!/usr/bin/perl use warnings; use strict; use Device::SerialPort; my $ccnt; my $port = '/dev/ttyUSB0'; my $conf = '~/.conf-pasha'; 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(19200); my $parity = $ob->parity("none"); 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; my ($count, $string, $name, $value); my @vals; while(1) { $ccnt++; $ob->lastline("\n"); ($count, $string) = $ob->read(255); print "$ccnt\n\n"; @vals = split("\n", $string); foreach (@vals) { $_=~ s/\t/\=/; $_=~ s/\r//; $_=~ s/\n//; ($name, $value) = split("=", $_); next unless ($name); $value = 0 unless ($value); $value = $value/1000 if $name =~ /I/g; print "$name=$value\n"; } sleep(1); } $ob->write_drain; $ob->close; undef $ob
Re: parse data from serial port
by boftx (Deacon) on Oct 15, 2013 at 05:36 UTC

    Many, many moons ago (early 90s)I had reason to collect data from a PBX system that was sent out over an RS232 connection. I found that I had to read a line at a time instead of a fixed block in order to get reasonable data chunks to work with. It looks like you have a similar situation to that.

    I haven't had a chance to grok Device::SerialPort yet, but I would suspect it has some kind of input function that is similar to readln that would be suitable.

    Assuming that is the case, then it should be relatively simple to bring in a line at a time for parsing/storing and then processing the collected data whenever the closing "Checksum" entry is detected. Doing a split /\s+/ might come in handy for that. (I tend to be partial to dispatch tables for working with record types.)

    The answer to the question "Can we do this?" is always an emphatic "Yes!" Just give me enough time and money.