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

Hi, I am using RS-232 to communicate between PC and a device using UARTs. I am using a script in the PC that, keeps sending commands (CMD1 in a loop) to the Device till it responds with the right response (CorrRsp). On receiving this response subsequent commands (CMD2) are sent to the device. The device has stringent timing constraints and must receive CMD2 within 20 ms from the time it sends the CorrRsp to the PC. The issue is I am not able to get consistent results. Some times CMD2 is received at the device end within 20 ms but sometimes not. The script used and the test results that shows the inconsistency with the timing are copied here for reference. Please help me understand if the inconsistency is due to the script or the OS. As the device is tested and certified as stable.
#!/usr/bin/perl use Win32::SerialPort; use Time::HiRes qw(usleep); $com="COM1"; $PortObj=Win32::SerialPort->new($com); $PortObj->baudrate(38400); $PortObj->databits(8); $PortObj->parity("none"); $PortObj->stopbits(1); $PortObj->handshake("xoff"); $PortObj->xon_limit(100); # bytes left in buffer $PortObj->xoff_limit(100); # space left in buffer $PortObj->xon_char(0x11); $PortObj->xoff_char(0x13); $PortObj->read_interval(2); # max time between read char (milliseco +nds) $PortObj->read_char_time(1); # avg time between read char $PortObj->read_const_time(2); # total = (avg * bytes) + const #writes the settings into the port $PortObj->write_settings; $PortObj->are_match("CorrRsp", ""); # correct response for CMD1 my $gotit = ""; until ("" ne $gotit) { $gotit = $PortObj->lookfor(100); #blocking read if($gotit){last;} $cmd= "CMD1\n"; # CMD1 Send in loop till CorrRsp is received $PortObj->write($cmd); $microseconds = "600"; usleep ($microseconds); } $cmd= "CMD2\n"; $PortObj->write($cmd); print("$gotit\n"); $mydata1 = $gotit; $PortObj->close; undef $PortObj;
Test results for time difference between CMD1 and CorrRsp: Test1: 116 ms; test2: 105 ms; test3: 69 ms; test4: 79 ms; test5: 97 ms; test6: 69 ms; test7: 99 ms; test8: 76 ms;

Replies are listed 'Best First'.
Re: Timing issue while accessing serial port
by Marshall (Canon) on Jan 20, 2011 at 10:45 UTC
    First, let's make sure that we have a common understanding of how long it takes to send or recieve a character at 9600 baud - that is in very rough terms about 1 ms per byte (character). Details include: number of "start" bits and the number of "stop" bits.

    I don't know how many bytes the response contains or how the receiver decides that it has not received a "timely reponse" - I mean if the "reponse" takes say 8 characters just to send, then how is that judged against the allowed "20 character" (20 ms) response time? You may have a lot less than 20ms to start your response.

    I can't test your code, but some parts do seem rather weird... what is this usleep() stuff in a blocking loop about? Why?

    I think that this $PortObj method may be being misused?

    $gotit = $PortObj->lookfor(100); #blocking read
    I saw a recent question about this.
    It appeared to me at the time, that this serial port module could be configured to report sucess after a particular stream of input characters were seen - that would appear to me to be inconsistent with this idea of "lookfor(100)", one hundred what?

    What other stuff is running on this Windows machine?
    Numbers like 116 ms are a looonnnng time, can you explain more how you calculated that? A process running under Normal priority in a blocking loop that essentially goes to the OS, should under usual circumstances be able to easily process your requirements.

      Thank you for your response. Let me clarify some of your doubts regarding my script. First of all I am working on Windows XP machine.Other things are as follows 1. My command contains around 65 characters and response also contains same number of character and baud rate is 36400 bps. 2. I am using usleep() after write function to give enough time for the data to get completely written. (Without sleep i was not able to get the complete command in one go.) 3. As per my understanding the lookfor(100) is blocking read that is the script will wait for 100 bytes of data to be available before it starts looking for the pattern. If i have misunderstood the lookfor(100) method then please let me know. 4. There is no other application or program running when the script is running on Windows machine.
Re: Timing issue while accessing serial port
by dasgar (Priest) on Jan 20, 2011 at 14:10 UTC

    Thinking out loud, here are my thoughts on how I personally would go about debugging the situation.

    • First, I'm assuming from your use of the Win32::SerialPort module that you're on a Windows box. My first step would be to try using Hyperterminal or putty to manually test issuing the commands and to see what kind of responses that you get back from the device.

    • $PortObj->are_match("CorrRsp", ""); # correct response for CMD1
      If I'm reading the documentation and your code correctly, you're telling the module that null ("") is a valid possible end string to determine the end of a response from the device. That might be correct, but it doesn't look right to me. In my mind, going with "\r" and/or "\r\n" makes more sense, but that may be completely wrong for your device.

    • $gotit = $PortObj->lookfor(100); #blocking read
      Based on the documentation, you're read blocking until 100 characters are received. Unless you're 100% sure that you'll get at least 100 characters in response, this might create some problems. For debugging, you might want to try following the example "until" loop in the Win32::SerialPort documentation in the Methods_for_I/O_Processing section. Also, that particular example loop that I'm referring to has a $PortObj->lookclear; line to clear out the buffer. Your code is not calling this function. If you're going to reissue the command, it makes sense to me to clear out the read buffer to ensure that the next read call is reading the response to the latest issued command instead of previously issued commands.

    • Timeouts: Near the end of the Configuration and Capability Methods section of the Win32::SerialPort documentation, there's some notes about timeouts. Speaking from personal experience with using an older version of this module, I had to read this information several times to wrap my head around the timeouts in the module so that I could add in the proper delay (via sleep command) between sending a command and reading the response back. It might be worth it to take a few minutes to look at that stuff and your code to make sure that you're not trying to read before the device sends a response. Might not be a problem in your case, but it definitely was a problem for me when adding retries of commands in my code.

    • The Lookfor and I/O Processing section of the Win32::SerialPort documentation discusses the lastlook method, which can be used to "view" the "actual match and the characters after it (if any)". For debugging, it might be helpful to add a few lines inside your "until" loop to print out this information when the lookfor method doesn't find the proper success message from the device. That might help you figure out if you're reading too early or if you're hitting some edge case that you have not anticipated.

    Well, those are my thoughts on what I personally would do to try to debug the problem. Hopefully one of these thoughts will either help you or at least trigger better ideas/suggestions that will help you solve the problem.

    Good luck in your debugging efforts!

      First, I'm assuming from your use of the Win32::SerialPort module that you're on a Windows box. My first step would be to try using Hyperterminal or putty to manually test issuing the commands and to see what kind of responses that you get back from the device.
      This is a fantastic idea and the right way to go. I wish I would have suggested it!
      Thanks a lot. 1) I have used HyperTerminal to check the command response behavior between PC and the device. And it is same as expected. 2) As per my understanding the are_match("rsp1", "rsp2") is used to check the "rsp1" or "rsp2" pattern in the incoming data from the serial port. I had taken the are_match("rsp1","rsp2") as it is from the example given in Win32::SerialPort documentation as standard syntex for using are_match, as i want only single pattern to be matched that is "CorrRsp" i thought i will use second parameter as "" (null), I did not use "\n" or "\r\n" because if "CorrRsp" is not received then the script will look for "\n" as the required pattern and once it gets "\n" it will come out of loop and i don't want that to happen. If i have misunderstood something regarding the are_match function please let me know. 3) I have not used lookclear because i want to print the data received before the pattern which is stored in $gotit. If i clear the buffer then i will not able to print the data. If i misunderstood something or if there is any other way to print the data received till the pattern is matched then please let me know.
Re: Timing issue while accessing serial port
by Marshall (Canon) on Jan 20, 2011 at 13:07 UTC
    There are a bunch of statements that over-ride defaults.
    I do not believe that to be a wise decision.
    Over-ride a default when you know why you are doing it.
    Some examples are:
    $PortObj->handshake("xoff"); $PortObj->xon_limit(100); # bytes left in buffer $PortObj->xoff_limit(100); # space left in buffer $PortObj->xon_char(0x11); $PortObj->xoff_char(0x13);
    In the "bad old days", and still sometimes today, there is what is called "hardware flow control" and serial ports wiggle dedicated lines to regulate the flow of information..."Wait,...my buffer is full right now!". In "Dilbert-ese", "my brain is full!".

    These "flow control" lines took extra pins on the connector. On the modern DB-9 connectors, not all of these lines are even there!

    The old farts were not stupid. But, they didn't have smart IC's which in themselves contained buffer memory. Modern UARTs (Universal Asynchronous Receiver/Transmitter) can send an "in band" signal to tell the other guy to "shut up". This is the X-ON/X-OFF protocol.

    Its like the "mother-in-law" is spewing out massive amounts of verbage and you just whisper "stop" and she does! Magic!

    I would leave that X-ON/OFF protocol alone unless a good reason to over-ride it surfaces. And of course that certainly can happen!

    I would also concentrate on what you send and what you receive. Forget performance until you get the correct command and response data. I'm not sure that you have that yet.

Re: Timing issue while accessing serial port
by Phemto (Acolyte) on Jan 20, 2011 at 10:27 UTC

    I'm inclined to blame the OS. You didn't mention what the is, by the way. If it's a unix variant, you could try renicing the program to give it a higher priority. (There is also a way to do this under windows, but I don't know it off the top of my head). I don't know that it would completely solve the problem, but if it makes a difference in your test results, it would imply that your program is not getting enough CPU cycles to do this in real time.

    One non-perl solution could be to use a microcontroller (eg arduino) to communicate with the device. It could handle the triggering and also report back to the PC when it has happened.

      As slow as Windows is, it is hard for me to say that Windows is to blame here. 1,000 ms is one second. 116ms is about 1/10th of a second. There is something wrong with the code or the code that this code calls.