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

Perlmonks,

My problem is the following: I have a script that slurps a text file into an array and sends data to a serial port. The data slurped in contains a timecode and data.

The timecode is in milliseconds.

I want to ensure that my data is sent to the serial port at exact times as specified.

Example:
- slurp in file that has the following:
0035020 (first five are timecode, 350ms for this line)
0029024 (last 2 are data to be sent, 24 for this line)
1042201

- send the data "20" over the com port
- wait 350 ms
- send the data "24" over the com port
- wait 290 ms
- send the data "1" over the com port
- wait 10422 ms

the following code shows what I'm doing, but somehow the times are getting off sync...

Is there something I'm overlooking?

use warnings; use Win32::SerialPort; use Time::HiRes qw (usleep); open (CODES, "$ARGV[0]"); my @codes = <CODES>; close CODES; my $serial = new Win32::SerialPort ('COM3') or die "Can't open COM3: $ +^E\n"; $serial->databits(8); $serial->baudrate(9600); $serial->parity("none"); $serial->stopbits(1); $serial->write_settings; my $clearout = $serial->input; undef $clearout; my $correctionUs = 13800; //setting to offset the transfer time of s +erial communication, not working print "Hit Enter To Begin"; <STDIN>; my $starttimeepoch = time; my $starttime = localtime; print "Start Time is: $starttime, epoch time is: " . $starttimeepoch . + "\n"; print "Correction is: " . $correctionUs . "\n"; foreach my $line (@codes){ $line =~ /(\d{5})(\d{2})/; $serial->write($2); usleep(($1 * 1000 )- $correctionUs); } my $endtimeepoch = time; my $endtime = localtime; print "End Time is: $endtime, epoch time is: " . $endtimeepoch . "\n"; print "Total time is: " . ($endtimeepoch - $starttimeepoch) . "\n"; print "I should be done now, sleeping for 10 seconds\n"; sleep(10); $serial->close or die "Failed to close COM3"; undef $serial;

There must be some blocking or something going on, the first hundred or so lines send well, then they get off-sync, then after some time, they'll resync by themselves.

Any ideas?

Replies are listed 'Best First'.
Re: Serial Port timing and write blocking?
by TGI (Parson) on Aug 04, 2008 at 19:00 UTC

    A couple of thoughts:

    • usleep uses microseconds, not milliseconds for its time scale. I don't see any multiplication to account for this in your code.
    • Since you are so sensitive to timing, try using write_bg instead of write.
    • How accurate are the clocks in your sending and receiving devices? If each device is +/-5%, then you can wind up with a big discrepancy.


    TGI says moo

      TGI,

      Correct you were on the first part, I had that in my code, I watered the code down a bit for the forum. I've corrected that math error for clarity here. Thanks.

      As for the write_bg, that's something I'll definitely try. Never used that before.

      As for the clock on the device I'm writing to...not really important, because the recieving device loops and waits for 2 bytes of input. As soon as the receiving device gets it's 2 bytes, it takes actions based on the data code it received. The sending device (as you can guess by the code) is a windows pc.

        How much out of sync are you getting? 1 ms, 100ms or 10s? How much drift is acceptable?

        What exactly should the timing look like? Consider the timing diagram below:

        A. Start sending command C. Wait period ends / / ---+------+--------------------+-----------+---- \ \ B. Command finish transmission D. Send next command.

        If I understand correctly, you want to have the time between A and C be X milliseconds as determined by input from the file. The time between C and D should be as close to zero as possible.

        Are you reading in all of the commands in the command set at once, and operating from memory? Or, are you reading each command from a file? The second approach could add time between C and D.

        My next step in trouble shooting this would be to try and figure out which part of the process we are at when the time is being lost/gained.


        TGI says moo

Re: Serial Port timing and write blocking?
by Illuminatus (Curate) on Aug 05, 2008 at 00:21 UTC
    Keep in mind that unless you are running on a hard real-time system, there are no guarantees on usleep. All it guarantees is that it will wait at least as long as the the argument. You might look into Time::Clock, which says it guarantees time to within nanoseconds.

      Your point about hard real-time systems is very good.

      Time::Clock uses Time::HiRes. It doesn't really buy anything. Especially since it doesn't offer a sleep function. Time::HiRes also offers a nanosecond sleep function.

      If you have access to a high-precision timer, you can use clock_nanosleep to control your timing based on the high precision timer.

      It's also worth checking the return value of your sleep-type calls. They tell what the actual sleep time was. That way, if your sleep was interrupted by a signal, or if your sleep ran long for some reason, you will know, and can take appropriate action.


      TGI says moo