The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Hardware::PortScanner - Scan serial ports to find hardware devices at various com ports, baud rates, and settings using request and expected reply message

SYNOPSIS

  $serial = Hardware::PortScanner->new();

  $serial->scan_ports(          BAUD => [  4800, 9600 ],
                                        SETTING => [ '8N1', '7E1X' ],
                                        TEST_STRING => "V\n",
                                        VALID_REPLY_RE => '^UBW FW' );

  $PortObj = $serial->connect_to_device();


  OR

  $serial = Hardware::PortScanner->new();
  $PortObj = $serial->connect_to_device(BAUD => 115200, COM => 'COM3');

DESCRIPTION

This module provides methods to scan and connect to com ports for hardware removing the necessity of knowing the specific com port, baud rate and/or settings (e.g. 8N1, 7E1) of the particular device you want to connect. The module works by sending a command to available devices at a variety of baud rates, com ports and settings. If a message is received back that matches a regular expression, then the connection specifics are returned. Additionally, the module can connect to the device via Device::SerialPort or Win32::SerialPort depending upon the platform.

  • Unplug unused COM cables before installation (or afterword if you notice problems). See Bug/Issues below.

  • Run "manual_test.pl" for device specific tests

Hardware::PortScanner Methods

new()

Returns an Hardware::PortScanner object.

Parameters

MAX_PORT - Optional parameter to specify the high port number on your system. This will speed up the scans.

   Example(s):

   MAX_PORT => 8
   MAX_PORT => "Com5"

   Default if not specified:

   MAX_PORT => 5

scan_ports()

This method will scan for devices at several com ports, baud rate and settings each which can be specified or defaulted. scan_port will stop scanning the current com port when a device is found and continue with the remaining com ports, baud rate and settings. Note that USB devices don't have the concept of baud and settings which is why scanning is stopped on a particular port if a device is found. Otherwise it would appear a device was found at several baud rates. scan_ports stores the results of a scan in this module's object.

Also, due the method of operation of this method, other devices that are connected to an available com port may be connected to and interrogated (e.g. sent the request message). It is assumed this will not cause undesirable behavior (doesn't for my stuff). Additionally, the device may be send the message several time since, at this time, a reply that does not match the VALID_REPLY_RE is treated as a non-response causing other setting and baud rate to be attempted.

Parameters

COM - Specifies the COM ports to be searched. Can be either a com number or a string like "COM8".

   Example(s):

   COM => [ 1, 2, "COM3", "COM8" ]
   COM => "COM3"

   Default if not specified:

   COM => [ qw/1..20/ ]

BAUD - Specifies the BAUD rates ports to be configured. These baud rates are not limited to reasonable values but the Device::SerialPort or Win32::SerialPort modules used to connect may complain if it doesn't like the values. A warning in scan_logs will also occur if the baud rate is invalid. Bauds are always tested in fasted to slowest or to support USB virtual devices which will connect and multiple baud rates (not that it will matter functionally).

   Example(s):

   BAUD => [ 1200, 2400 ]
   BAUD => 9600

   Default if not specified:

   BAUD => [ qw/1200 2400 4800 9600 19200 38400 57600 115200/ ]

SETTING - String(s) matching the regular expression "/^([5678])([NEO])([12])([NRX])?$/" where the first digit is the databits (5, 6, 7 or 8), the second character is the parity (N = None, E = Even, O = Odd), the third digit is the stopbits (1 or 2 - no 1.5 since it doesn't seem to be supported) and finally the handshaking character (N= none, R = RTS and X = XOFF) which is optional and defaulted to "N".

   Examples:

   SETTING => [ "8n1", "7E1" ]
   SETTING => "8N1"

   Default if not specified:

   SETTING => "8N1"

TEST_STRING - A string that will produce a response from the device that can be matched against a regular expression (VALID_REPLY_RE below) to confirm the device in desired has been found. This string must include any command terminators (newlines, carriage returns, etc) that are necessary to get the device to recognize the command. Often, a version request command works well as this string.

   Examples:

   TEST_STRING => "V\n"    # Version request for a UBW (USB Bit Whacker - UBW)
   TEST_STRING => "VER\r" # Version request for a SSC-32 servo controller

   No default and required parameter.

VALID_REPLY_RE - A regular expression that, if matched, indicates the desired device has been found.

   VALID_REPLY_RE => '^UBW FW [CD] Version.* '
   VALID_REPLY_RE => '^SSC[-]32'

MAX_WAIT - The maximum number of seconds, as a float, to wait for the expected reply after writing/sending the TEST_STRING to the serial port being tested. This can slow down the scan_port process depending upon MAX_WAIT value, number of available ports and options (BAUDS, SETTINGS, etc). Set this to say 5 seconds if your device is not detected. Then, assuming it is now detected, print a scan report to see the number of seconds before the response was recieved. Set MAX_WAIT to a figure higher than this.

This feature was added when my SSC-32 Servo controller failed to respond quickly enough to a version request to get detected (but only on Ubuntu strangly). Default is around .1 seconds which is the minimum even if set to 0.

   MAX_WAIT => 2
   MAX_WAIT => 0.23

connect_to_device()

This method will connect to a com port as specified by the parameters and return a standard *::SerialPort object if successful. If no parameters are given, then connect_to_device will attempt to connect to the device found by the previous scan_port. A errror will be given if no device was found (or scan_port was not invoked) or more than one device was found.

Parameters

COM - Specifies the COM ports to be searched. Can be either as a com number or in with a string like "COM8".

   Example(s):

   COM => 1
   COM => "COM3"

BAUD - Specifies the BAUD rates ports to be configured. These baud rates are not limited to reasonable values but the Device::SerialPort or Win32::SerialPort modules used to connect may complain if it doesn't like the values. A warning in scan_logs will also occur if the baud rate is invalid. Bauds are always tested in fasted to slowest or to support USB virtual devices which will connect and multiple baud rates (not that it will matter functionally).

   Example(s):

   BAUD => 9600

SETTING - String(s) matching the regular expression "/^([5678])([NEO])([12])([NRX])?$/" where the first digit is the databits (5, 6, 7 or 8), the second character is the parity (N = None, E = Even, O = Odd), the third digit is the stopbits (1 or 2 - no 1.5 since it doesn't seem to be supported) and finally the handshaking character (N= none, R = RTS and X = XOFF) which is optional and defaulted to "N".

   Examples:

   SETTING => "7E1"
   SETTING => "8N1"

   Default if not specified:

   SETTING => "8N1"

DEVICE - A Hardware::PortScanner::Device object returned from found_devices(). This option will pull the need connection parameters from the device specified and is incompatible with the other options.

   Examples:

   DEVICE => ($serial->found_devices)[0]    # Use the first device found
   DEVICE => $device_obj

num_found_device()

This method will return the number of devices successfully found by the previous scan_port()

found_devices()

This method will return an array of devices objects (Hardware::PortScanner::Device objects) successfully found by the previous scan_port()

connection()

Returns the *::SerialPort connection from the connect_to_device() method (see above). Additional *::SerialPort methods could be done against this object.

scan_log()

scan_log returns an array of info/debug messages to assist the end user in identify device not found issues. As an example, the code below was used to search for a USB Bit Whacker (UBW). An example of its use:

   $serial = Hardware::PortScanner->new();

   $serial->scan_ports(
                        COM => [ 3, 11 ],
                        BAUD => [ 4800, 9600 ],
                        SETTING => [ '8N1', '7E1X' ],
                        TEST_STRING => "V\n",
                        VALID_REPLY_RE => '^UBW FW'
                      );

   foreach ($serial->scan_log)
   {
      print "$_\n";
   }

   Generated the following output:


   Scan Ports Request
   ==================
   Scan Port COM3 @ /dev/ttyS2
      Checking with baudrate of 9600
         Checking with setting of 7E1X
            Sending test string "V\n"
            Received back from device ""
         Checking with setting of 8N1
            Sending test string "V\n"
            Received back from device ""
      Checking with baudrate of 4800
         Checking with setting of 7E1X
            Sending test string "V\n"
            Received back from device ""
         Checking with setting of 8N1
            Sending test string "V\n"
            Received back from device ""
   Scan Port COM11 @ /dev/ttyS10
      Checking with baudrate of 9600
         Checking with setting of 7E1X
            Sending test string "V\n"
            Received back from device "UBW FW D Version 1.4.3"
            Matched valid reply RE so returning

scan_report()

Convenience method to print out the scan_log (see above). Functionally equivalent to the following code:

   foreach ($serial->scan_log)
   {
      print "$_\n";
   }

Hardware::PortScanner::Device Methods

These methods provide connection information for a device objects as returned from found_devices().

com_port()

Returns the com number of the provided device.

baudrate()

Returns the baud rate of the provided device.

databits()

Returns the databits of the provided device.

parity()

Returns the parity of the provided device.

stopbits()

Returns the stopbits of the provided device.

handshake()

Returns the handshake of the provided device.

port_name()

Returns the port name of the provided device. Which is suitable for the PortName parameter in the *::SerialPort new command.

Below is an example from Device::SerialPort where the port name is used as "$PortName".

   $PortObj = new Device::SerialPort ($PortName, $quiet, $lockfile)
       || die "Can't open $PortName: $!\n";

PREREQUISITES

Attempts were made to minimize the dependencies in case this module is being deployed on a minimal embedded system. However, some are required for proper functioning.

  • Device::SerialPort from CPAN if using a Linux based system (including Cygwin)

  • Win32::SerialPort from CPAN if using Windows

  • Carp

SUPPORTED PLATFORMS

Cygwin, Windows (but least tested) and Ubuntu

BUGS/ISSUES

  • Strange behavior (e.g. lock ups, odd low level errors, etc) occurs sometimes when some COM cables are plugged in but not attached to anything. Only seen this happen when testing on Windows.

  • Have seen Device::SerialPort freeze (never return) when configuring certain handshaking settings (5N1R) on particular com ports. Don't know if this was due to Device::SerialPort, Windows, Cygwin (which I was using at the time) or the COM hardware. I backed off the handshaking tests in the test scripts because of this.

  • Under certain circumstances the *::SerialPort warning of "can't getattr: Bad file descriptor" or "can't getattr: Input/Output ...: occurs. This always occured on a particular port when a particular device I had on that port had a low battery or a particular unconnect cable. This has been mitigated a bit by ignoring these errors during a connect attempt. They do show up however in a scan_report.

TODO

  • Add a write then read method to wrap the lower level *::SerialPort commands of write and read. This would also use the MAX_WAIT parameter provided to the new() or provided as a option to this command.

  • Complete and improve documentation

  • Consider any features upon request

  • Add more tests during installation

AUTHOR

John Dennis <jdennis30064@galileotech.com>

COPYRIGHT AND LICENSE

Copyright (C) 2009 by John Dennis This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.1 or, at your option, any later version of Perl 5 you may have available.