in reply to Re^3: Win32::SerialPort, buffer overflow
in thread Win32::SerialPort, buffer overflow

Serial devices usually have a very small buffer, like 16 bytes or something. Could the problem be that the device isn't consuming data as fast as you write it? Is that module supposed to block your script until there is room in the buffer, or does it just throw that exception when the buffer is full?

You might try a quick experiment to see what other cases can give you the same error. What if you write a 17-byte string once? What if you write two 9-byte strings back-to-back without waiting? What if you write 5000 bytes?

There are also flow-control lines on the serial connection, so the device can indicate when it is ready to read or not. Do you have a breakout-box showing you those signals on LEDs? What happens if you write data on the line when the device isn't ClearToSend?

Ah, and I just learned something. (from that StackOverflow post)

RTS/CTS wasn't supposed to ever be a flow control mechanism, originally; it was for half-duplex modems to coordinate who was sending and who was receiving. RTS and CTS got misused for flow control so often that it became standard.
  • Comment on Re^4: Win32::SerialPort, buffer overflow

Replies are listed 'Best First'.
Re^5: Win32::SerialPort, buffer overflow
by afoken (Chancellor) on Nov 12, 2024 at 21:00 UTC
    There are also flow-control lines on the serial connection, so the device can indicate when it is ready to read or not.

    Not all RS232 devices implement hardware flow control. Some don't implement anything but the bare minimum (GND, RxD, TxD).

    Just last week, I've wired a new, major brand UPS to a small server. That UPS lacks USB for some unknown reason, but supports RS232. Of course, it does not use the usual PC pinout. That would be far too easy. And the official adapter cable just shorts the two main handshake pairs on the PC side (RTS to CTS and DCD to DTR). Only RxD, TxD and GND are connected to the UPS.

    That server, by the way, has one RS232 with a PC pinout on a DB9 connector, and a second serial port on an internal pinheader with CMOS 3.3V levels lacking the handshake lines. They are present at the UART, but not connected to the pinheader.

    From my experiance with modern ARM-based microcontrollers, RS232 works fine in home and office environments, even without handshake lines, and even at relatively high baud rates (115200 bit/s). The old 8-bit AVRs (and probably also the 8-bit PICs) can be problematic without handshake lines, they just can't get the data out of the UART receive buffer fast enough. If you connect a USB UART (e.g. FTDI FT232) close to the microcontroller board instead of using a long RS232 cable, ARM-based microcontrollers can even work at much higher speeds without handshake, up to the limits of the USB UART.

    In an industrial environment, I would use something with differential signals instead (RS485, RS422, CAN), or a current loop (e.g. 4-20 mA), because those systems are almost immune to noise.

    Do you have a breakout-box showing you those signals on LEDs?

    At 38400 Bit/s, a data byte encodes to at least 10 bits (start, 8x data, stop), so you end up with up to 3840 Bytes/s. That's almost 4 bytes per ms, or 4 kHz. You won't be able to see an LED flashing at that speed, if one of the two devices sets or clears a handshake signal for a few bytes. That's just a limit of your wetware. You can hear a 4 kHz signal, but visually, you are limited to about 25 Hz.

    At 300 Bit/s = 30 Bytes/s, you could clearly see the LEDs flickering. At 38400 Bit/s or more, you can just hope that the handshake lines change their level very often, so they effectively drive the LEDs with a PWM signal and make them change their apparant brightness.

    If you want to see short signal changes, with a duty cycle close to 0 % or close to 100 %, you need an oscilloscope. At RS232 speeds, any cheap one with at least 2 Megasamples per second / 1 MHz bandwith will do. (Note that some people sell cheap junk kits that can't even measure outside the audio range.)

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      Hi.

      Thanks to NERDVANA and soonix and afoken. With respect to access to the RS232 serial, the module I am accessing is an OBD2 automotive module with a USB to serial chip embedded within a plastic sealed module. The handskaking on the serial port is switched off ("none" as indicated with the serial initialisation). Also, the initialisation also seems to suggest a buffering of 256 bytes in both directions.

      I have tried changing the size of the write (shorted and longer) but the issue still follows how the data is written to the sub Serial_TrxRcv($parameter). I have a "yucky" work around where I avoid taking the $ccmd (command) directly from the hash, but copy to a $newcmd variable in a very specific way. This is in the following code presentation.

      sub ProcessPIDs { # print "Inside ProcessPIDs \n"; # Process the input arguments my ( $nickname, $ref_nicknametocommand ) = @_; my %InSubNickNameToCommand = %{$ref_nicknametocommand}; # De-ref +rence the reference to an hash my $ccmd; $ccmd = $InSubNickNameToCommand{ $nickname }; # need E +RROR checking here !!!!!! my $newcmd ; if ($ccmd eq '0101') { $newcmd = '0101'; } elsif ($ccmd eq '0103') { $newcmd = '0103'; } elsif ($ccmd eq '0104') { $newcmd = '0104'; } elsif ($ccmd eq '0105') { $newcmd = '0105'; } elsif ($ccmd eq '0106') { $newcmd = '0106'; } elsif ($ccmd eq '0107') { $newcmd = '0107'; } elsif ($ccmd eq '010c') { $newcmd = '010c'; } elsif ($ccmd eq '010d') { $newcmd = '010d'; } elsif ($ccmd eq '010e') { $newcmd = '010e'; } elsif ($ccmd eq '010f') { $newcmd = '010f'; } elsif ($ccmd eq '0110') { $newcmd = '0110'; } elsif ($ccmd eq '0111') { $newcmd = '0111'; } elsif ($ccmd eq '0113') { $newcmd = '0113'; } elsif ($ccmd eq '0114') { $newcmd = '0114'; } elsif ($ccmd eq '0115') { $newcmd = '0115'; } elsif ($ccmd eq '011c') { $newcmd = '011c'; } elsif ($ccmd eq '0121') { $newcmd = '0121'; } elsif ($ccmd eq '012e') { $newcmd = '012e'; } elsif ($ccmd eq '012f') { $newcmd = '012f'; } elsif ($ccmd eq '013c') { $newcmd = '013c'; } elsif ($ccmd eq '0142') { $newcmd = '0142'; } elsif ($ccmd eq '0143') { $newcmd = '0143'; } elsif ($ccmd eq '0144') { $newcmd = '0144'; } elsif ($ccmd eq '0145') { $newcmd = '0145'; } elsif ($ccmd eq '0149') { $newcmd = '0149'; } elsif ($ccmd eq '014a') { $newcmd = '014a'; } elsif ($ccmd eq '014c') { $newcmd = '014c'; } else { print "Warning; failed to match $ccmd\n"; die "AMEN !\n"; } # my $newcmd = $ccmd; # this does not work print "\$ccmd; $ccmd ".length($ccmd). " \$newcmd; $newcmd ".length +($newcmd)."\n"; my $response = Serial_TrxRcv($newcmd); my $result; .... etc ...

      As indicated in the code above, I could not just copy with "my $newcmd = $ccmd" but had to involve an if statement. In this circumstance the $newcmd can be passed to the sub without the buffer error occurring.

      If the $newcmd is copied from a constant in the program then it works.

      It's hard to known if the issue is from a very fine timing problem with the serial port treatment or related to how perl reads from a simple scaler embedded with a hash. Please consider issues ?

      Regards JC....

        That... is a truly strange workaround you have there. I can't imagine how that is different from '=', nor do I expect it to change execution speed much. Is there any chance that the difference is the 'die' that stops you from sending any other codes?

        The most relevant "gotcha" compared to what you're doing here would be if something inside Serial_TrxRcv tried checking whether you gave it a number or a string, in which case $ccmd could have picked up a "dualvar" nature (both a number and a string) which gets removed by assigning a string constant (in certain perl versions). But, you already showed us the code of Serial_TrxRcv and the first thing in there concatenates it with "\r" which will always treat it as a string.