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

Hi All,

I need to manipulate data sent from a Nurse call system ( Device::SerialPort ). The Nurse call system introduces delays during transmission, it gets interpreted by my code as a newline.

lookfor() by default looks for \n and works fine in testing when there is no delay.

#/usr/bin/perl use Device::SerialPort; my $port = Device::SerialPort->new("/dev/ttyUSB0"); $port->baudrate(9600); # Configure this to match your device $port->databits(8); $port->parity("none"); $port->stopbits(1); $port->stty_icrnl(1); $port->handshake("none") || print "failed setting handshake"; $port->write_settings || print "no settings"; $port->buffers(4096, 4096); @max_values = $port->buffer_max; $port->reset_error; while (1) { my $char = $port->lookfor(); if ($char) { print "$char\n"; open (DO, '>/home/pi/temp.txt'); print DO $char; close DO; } $port->lookclear; # needed to prevent blocking sleep (1); }

If I use this code to send data to the script, it works fine:

$port->write("26Jul1998 16:00 P000 0080 CALL , unit 0\r\n");

To reproduce the Nurse call system transmission, I use this:

#/usr/bin/perl use Device::SerialPort; my $port = Device::SerialPort->new("/dev/ttyUSB0"); $port->baudrate(9600); # Configure this to match your device $port->databits(8); $port->parity("none"); $port->stopbits(1); $port->handshake("none") || die "failed setting handshake"; $port->write_settings || die "no settings"; $port->write("26Jul1998 16:00"); sleep(1); $port->write(" P000 0070 CALL "); $sleep(1); $port->write(" , unit 0\r\n");

I have done a Hex dump of the Nurse call system transmission and confirmed there is no hidden new lines or cartridge returns. I'd appreciate any tips. Thanks you.

Replies are listed 'Best First'.
Re: Serial data delay
by cavac (Prior) on Oct 23, 2018 at 10:24 UTC

    I don't think you are dealing with a Perl issue here, not directly. But just in case, add this at the start of your script to make your life easier and help us get to the root of the problem:

    use strict; use warnings; use diagnostics;

    You are running this on a Raspberry Pi, right? In my experience, this hardware had a lot of "unexpected features", like messing up SPI timing when a new ethernet packet arrives.

    In your case, you seem to transfer more than 16 bytes at one time, but i rather suspect the serial hardware has a 16 byte limited buffer, and that is most likely where your timing gets messed up and you drop some bytes. This happens when the system is busy with other stuff (like dealing with ethernet or other interrupts) and can't get to working on the serial buffer in time.

    You have quite a few alternatives here. You could use another hardware or try one of those serial-to-ethernet boxes. You could also try a different Serial-to-USB adapter.

    perl -e 'use MIME::Base64; print decode_base64("4pmsIE5ldmVyIGdvbm5hIGdpdmUgeW91IHVwCiAgTmV2ZXIgZ29ubmEgbGV0IHlvdSBkb3duLi4uIOKZqwo=");'
Re: Serial data delay
by haukex (Archbishop) on Oct 23, 2018 at 14:23 UTC

    Unfortunately I can't reproduce the issue you describe on my end (at least with a fake serial port). When I've worked with Device::SerialPort, I've had the most success reading a single byte from the port at a time. However, I've had even better experiences on *NIX (including RPi) with IO::Termios, which I wrote about here: Lower-Level Serial Port Access on *NIX

      Hi haukex, looks like I’ll be jumping ship from from Device::Serialport. My programming knowledge is limited, I mainly work from examples I find online. I will try IO::Termios after work with examples from the link you provided. Thank you.

Re: Serial data delay
by StepSteve (Novice) on Oct 24, 2018 at 00:43 UTC

    Hey guys, I've found something that works on the bench. Fingers crossed it works in production. Was a simple matter of adding a sleep().

    my $char = $port->lookfor(); sleep(4); # number will be decreased in production if ($char) { print "$char\n";

    First time posting here, was not disappointed. Thanks heaps!

      I've found something that works on the bench.

      sleep(4); # number will be decreased in production

      That looks quite scary. Seeing delays in code handling serial transmissions is usually an indicator for misconfiguration, i.e. lack of proper handshake. In Serial data delay, you explicitly disable all handshake:

      $port->handshake("none")

      Check the documentation of the serial device. I would expect to find either handshake lines (RTS/CTS, sometimes DSR/DTR), or some kind of software handshake. XON/XOFF is common for software handshake, but people have been (and still are) very creative in re-inventing the wheel, especially with embedded devices. There are literally hundreds of strange software handshake protocols.

      Software handshake using XON/XOFF can be handled by the operating system or some low-level driver library, but you have to enable it. If you find the device sending unexpected 0x13 and 0x11 bytes, especially when writing much data to it, it may implement XON/XOFF. 0x13 = 19 = DC3 is XOFF, meaning that you should stop sending more data. 0x11 = 17 = DC1 is XON, telling you to continue sending data. For non-standard software handshake variants (i.e. everything but XON/XOFF), RTFM.

      Hardware handshake is usually easier. Some embedded devices implement only one half of the hardware handshake, a pin to indicate "input buffer is full, stop sending more data". That is usually labelled RTS (according to RS232E, it should be labelled RTR, ready to receive) and should be connected to CTS on the other side. The other pin, CTS, is omitted because they expect their communication partner (i.e. the PC) to be able to handle all incoming data at full speed.

      For hardware handshake, a three-wire connection (RxD, TxD, GND) is not sufficient, you need at least one extra wire from device RTS (or DTR) to PC CTS (or DSR). Use at least a five-wire connection (RxD, TxD, GND, RTS, CTS), preferably a cable with all signals connected.

      In Serial data delay, I see you using /dev/ttyUSB0. I know there are some USB RS232 converters that DO NOT implement the handshake lines. Make sure you don't have such a troublemaker device. FTDI FT232-based adapters should be fine. FTDI FT230 implements only RTS and CTS, but lacks support for DSR, DTR, DCD, RI. Prolific-based adapters cause a lot of pain on Windows due to crappy drivers, but work ok on Linux.

      At work, we have banned everything but FTDI, and we buy them only from major distributors. They costs a few cents more, but it's simply not worth fiddling with some random garbage chip that either crashes the system, stops transmitting data, or damages data in the middle of a 24 hour aerospace test procedure. The costs for supporting crap chips in that environment buy you a truckload of FTDI chips. For debugging, you also do not want a chip that is unreliable. Your development target is sufficiently unreliable to cause lots of trouble. You do not want to waste time debugging your debug tools.

      Also in Serial data delay, I see /home/pi. Are you using a Raspberry Pi? Its serial port does support RTS/CTS, but you have to enable it: https://ethertubes.com/raspberry-pi-rts-cts-flow-control/, http://www.deater.net/weave/vmwprod/hardware/pi-rts/. Remember that the Raspi uses 3.3V signals, you have to add a level converter. Using a MAX3232 or similar is quite easy, as they come as ready-to-use adapter boards, including pin headers for the Raspi side and DB9 connector for the RS232 side.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Serial data delay
by StepSteve (Novice) on Oct 23, 2018 at 23:05 UTC

    Thanks heaps for the replies, I had time to try a couple things before work, with the same result.

    Used a laptop (Linux Mint) to receive the serial data serialUSB to SerialUSB.

    Used laptop (Linux Mint) to received serial data on onboard serial ttyS0, sent from SerialUSB.