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

Monks, your wisdom is required
I am writing to an embedded system over a serial port. The system is extremely limited and writing to it any faster than 50ms per character causes data to be dropped/lost.
My code is as follows
sub configPort($,$){
        my $port = ($_[0]);
        my $refConfigHash = ($_1);
        $$port->baudrate($refConfigHash->{portBaudRate}) || die "Cannot Set Baud Rate \n";
        $$port->databits($refConfigHash->{portData})     || die "Cannot Set Data Bits \n";
        $$port->stopbits($refConfigHash->{portStopBits}) || die "Cannot Set Stop Bits \n";
        $$port->parity($refConfigHash->{portParity})     || die "Cannot Set Parity \n";
	$$port->handshake("none");
$$port->read_char_time(0); # don't wait for each character $$port->read_const_time(1000); # 1 second per unfulfilled "read" call $$port->get_tick_count; $$port->write_settings; }
sub subSerialPortWrite($,$){ my $port = ($_[0]); my $buffer = $_1; my $i; $$port->write("\r"); for($i=0;$i<=length($buffer);$i++){ $$port->write(substr($buffer,$i,1)); Time::HiRes::usleep(50000); } }

What I believe is happening is that each character gets buffered and finally the entire string gets spit out to the serialport at once, which of course cannot respond fast enough.
My questions are
1. Am I right?
2. If so, how do I work around it?
3. If not, what is my problem and how do I fix it?

I'm using perl version 5.10.0
Please help Thanks
  • Comment on Writing to serial port with a delay between each character

Replies are listed 'Best First'.
Re: Writing to serial port with a delay between each character
by mr_mischief (Monsignor) on Apr 21, 2009 at 18:04 UTC
    It looks like you are using Device::SerialPort as your serial port management module. Please correct me if that's wrong. That kind of information can really be helpful in diagnosing your problem.

    Does the device to which you're sending data handle rts/cts signals? If so, get it to send you a clear-to-send and send a character only once that's received. Lowering your baud rate to something that guarantees your characters don't go faster may help, too.

      Thanks for the response. To answer your questions

      1. Yes I am using Device::SerialPort. Should have mentioned that before.
      2. The device does not do any flow control RTS/CTS. So that option is out
      3. I could lower the baud rate, but its a significant task on the system, since it is not entirely in my control.
      Thanks
        You can get a count of how many characters (under Linux at least) with the TIOCOUTQ function. Search the module's docs for that. You may be able to keep from flooding the device by keeping your own buffer (which it seems you already do) and only writing when the Device::SerialPort buffer is empty.

        You might also want to look into setting the mode to 'raw', but I'm not sure if that actually bypasses buffers or not. You could also look into the write_drain method which seems to go ahead and flush the transmit buffer. You could try calling that after every character you write.

        I've only done rudimentary work with the module, and I've never had to deal with such a limited device on the other end. YMMV, but I hope some of this babbling helps.

Re: Writing to serial port with a delay between each character
by roboticus (Chancellor) on Apr 21, 2009 at 20:30 UTC
    sidthakur:

    Double-check the embedded devices manual to see if it has any type of flow-control (XON/XOFF, RTS/CTS, DSR/DTR) available. Hacking in timeouts to replace flow-control is guaranteed to be a continual headache.

    NOTE: Some embedded devices don't use a standard flow-control mechanism, but they echo the characters back to the sender. You can use that for flow control by not sending another character until you receive the echoed one. That way, you know that the device has seen your character and is ready for the next one.

    Also, some embedded devices are line oriented, in that they just buffer the characters until a carriage return is detected. In most of those devices I've worked with, there wasn't a problem with the timing between the characters .. the time that was critical was the time after the carriage return was received, as that's when the command is parsed and executed. If that's the case, and if the device has a prompt, you could just send the entire command and then wait for the next prompt.

    Finally, I've never used Device::SerialPort before (all my embedded work has been in assembly, C and C++), but a quick review shows that it has possible support for all the schemes above. First, I'd look at changing read_char_time(0) to something else, to make it wait for each character. That seems like it would be the first step in making sure that the character was sent before proceeding to your timeout. If there's some buffering going on, it could hide it from you with the current setting you're using. Secondly, the write_done() or write_drain() functions might allow you to ensure the data is sent before starting a timeout. Review the other functions, I'm sure there are other bits in there...

    I hope you find something in this rambling post useful...

    ...roboticus
      Thanks for all the help. I guess while upgrading from Perl version 5.8.8 to 5.10.0 on that particular machine, I must have upset the fine balance of buffering. I upgraded the Linux distro I was using and it was an effective workaround. Since the system I was controlling was something that I could not change, changing the baud rate or introducing handshaking could not be tried as options. All other options on the controlling system did not help. But for now I should be ok. Thanks once again
Re: Writing to serial port with a delay between each character
by afoken (Chancellor) on Apr 21, 2009 at 19:49 UTC

    I am writing to an embedded system over a serial port. The system is extremely limited and writing to it any faster than 50ms per character causes data to be dropped/lost. ... What I believe is happening is that each character gets buffered and finally the entire string gets spit out to the serialport at once, which of course cannot respond fast enough.

    It seems your embedded system does not use any kind of handshake, telling the "big" system that it can't handle more data. RTS/CTS and XON/XOFF can be handled in hardware or by the OS, so everything would be completely transparent to your application. In the worst case, the OS would tell you it could deliver only n bytes, when you wanted to write m bytes (with n < m). In that case, you would have to try writing the remaining bytes again. (Any serial I/O library should handle that for you.)

    Try enabling (or adding) handshaking at RS232 level and all your problems should be gone. If the embedded device can handle really slow RS232 speeds, try connecting at a low speed, eg. 300 or even 75 bits per second. This should at least reduce your problem.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      Some more investigation reveals that the problem does not exist with Perl version 5.8.8, but does exist with 5.10.0
      Does that help any?
      I'm trying the ioctl TIOCOUTQ method to see how full the buffers get, but i don't seem to have ioctl.ph for 5.10.0.