Version 22 of Serial Port

Updated 2013-12-03 03:00:52 by RLE

serial port

See Also

[chan configure] ([fconfigure])
the standard command for configuring serial port channels
channel
nearly everything but the [socket] information is related to serial ports
[close]
for closing a serial port channel
[fileevent]
for readable/writable callbacks
[open]
the standard command to open serial ports. port names are platform-dependent. all relevant serial-port options to fconfigure are described on th man page for [open]
Parallel port
serial ports on Windows
Serial Port Central
Jon Axelson's site about serial links and networks, including RS-232, RS-485, COM ports, and USB virtual COM ports, and his book, "Serial Port Complete"
Simple TCP/IP to serial port gateway

Documentation

serial port ,Wikipedia
RS-232 ,Wikipedia

Examples

apache tcl cgi script with serial port access
Bwise, a serial port tcl script and a Xilinx demo board
A simple serial terminal
How to build a simple serial terminal in a text widget
How to read the serial port, and display in hexadezimal
List computations in a FPGA, driven by Tcl
More Serial Port Samples
How to read the serial port, and display in hexadezimal
Serial Port Logic Analyzer Screen Capture

Tools

moni
a serial line monitor
Serial Player
a Windows utility for recording and playing back data from the serial communications port of the computer
Portmon for Windows
a proprietary but freely available program that displays all serial and parallel port activity on a system
Serial line sniffer
open-source license
Tcl-DP
provides serial line features

Description

On Linux, opening a special device with the proper major/minor numbers always succeeds independent of there being any hardware attached or not. The first error usually happens when doing [chan configure -mode ...]] with -mode not a valid option for ...

On NetBSD, when a terminal file is opened, it normally causes the process to wait until a connection is established. For most hardware, the presence of a connection is indicated by the assertion of the hardware CARRIER DETECT (DCD) line. This is a problem since most hardware and/or cables in common use ignores this line. Using the NONBLOCK flag with [open] is not enough to get things right, because the DCD is also used to detect the [eof] condition, causing the channel to assert [eof] after the first [read] or [gets]. There's patch at feature request 603 which adds a new flag to [chan configure], -ignoredcd (only on unix machines with conforming termios interface) which controls whether the underlying device driver ignores the DCD line or not. Without this patch, the (non-portable) way to use a serial port lacking the DCD line is:

set device /dev/tty00
set fd [open $device {RDWR NONBLOCK}]
exec stty -f $device clocal

Serial lines have many characteristics known to Tcl. fconfigure is their usual interface. Typical options include -ttycontrol, -ttystatus, -timeout, and more.

Basic Example

D. J. Hagberg mailto:[email protected] :

Here is a simple example... The send_exp stuff is pretty generic and should work for any channel that supports non- blocking I/O.

# simple serial port example to send AT to modem and 
# wait for OK response in a fixed amount of time.  At the
# bottom is a simple loop to do this 20x to check serial
# handler reliability...
#
# Works well on Tcl 8.0 and up on Unix (Solaris/NT), poorly on 
# the tclsh included with Tcl 8.1.1 on NT, but pretty well on 
# the wish included with same.
#
# NOTE may need to set comPort appropriately for your
# platform.  Must have a modem configured to respond
# with "OK" to "AT" commands.
#
switch $tcl_platform(os) {
    {Linux}            {set comPort /dev/modem}
    {SunOS}            {set comPort /dev/cua/a}
    {Windows NT}       {set comPort COM2:}
    default            {error "Must configure comPort"}
}
set waitSecs 2
set nTries   20

#
# A cheap version of expect.
#
# Set up a event-driven I/O reader on the channel, output the
# string, and wait some number of seconds for the result.
#
# @param fh
#        a channel opened in non-blocking mode for I/O
#        with buffering turned off.
#
# @param outstr
#        a string to send to the output channel -- note: end-
#        of-line characters must be included in this string,
#        if desired.
#
# @param regexp
#        regular expression to match in the incoming data.
#
# @param seconds
#        number of seconds to wait for above match
#
# @throws error
#        if eof is detected on the channel while waiting
#
# @returns int
#        1 if a match is found, 0 otherwise.
#
proc send_expect {fh outstr regexp seconds} {
    global send_exp

    # make sure global vars are initialized properly
    set send_exp($fh.matched)        0
    if {![info exists send_exp($fh.buffer)]} {
        set send_exp($fh.buffer) {}
    }

    # set up our Read handler before outputting the string.
    if {![info exists send_exp($fh.setReader)]} {
        fileevent $fh readable [list private_send_exp_reader \
                        $fh $regexp]
        set send_exp($fh.setReader) 1
    }

    # output the string to send
    puts -nonewline $fh $outstr
    flush $fh

    # set up a timer so that we wait a limited amt of seconds
    set afterId [after [expr {$seconds*1000}] \
                [list set send_exp($fh.matched) 0]]
    vwait send_exp($fh.matched)
    set matched $send_exp($fh.matched)
    unset send_exp($fh.matched)
    catch [list after cancel $afterId]

    # If we got an eof, then throw an error
    if {$matched < 0} {
                error "Channel EOF while waiting for data"
                return 0
    }
    return $matched
}

#
# PRIVATE channel read event handler for send_expect.  Should
# not be called by user.
#
proc private_send_exp_reader {fh regexp} { 
    global send_exp
    
    if {[eof $fh]} {
        close $fh
        set send_exp($fh.matched) -1
        return
    }
    append send_exp($fh.buffer) [read $fh]
    if {[regexp $regexp $send_exp($fh.buffer)]} {
        set send_exp($fh.matched) 1
    }
}

#
# Return the current contents of the send_expect buffer
#
# @param fh
#        channel identifier that was used with send_expect
#
# @returns string
#        the current contents of the buffer for the channel
#
proc send_exp_getbuf {fh} {
    global send_exp
    return $send_exp($fh.buffer)
}

#
# Reset the send_expect buffer, returning its contents
#
# @param fh
#        channel identifier that was used with send_expect
#
# @returns string
#        the current contents of the buffer for the channel
#
proc send_exp_resetbuf {fh} {
    global send_exp 

    set buf $send_exp($fh.buffer)
    set send_exp($fh.buffer) {}
    return $buf
}

#
# Close out a send_expect session, closing I/O event handler
#
# @param fh
#        channel identifier that was used with send_expect
#
# @returns
#        the channel identifier passed as the fh parameter
#
proc send_exp_end {fh} {
    global send_exp

    fileevent $fh readable {}
    foreach v [array names send_exp $fh.*] {
        catch [list unset send_exp($v)]
    }
    return $fh
}


##
## MAIN
##
set fh [open $comPort RDWR]
fconfigure $fh -blocking 0 -buffering none \
        -mode 9600,n,8,1 -translation binary -eofchar {}

# Loop nTries times, sending AT to modem and expecting OK.
set nMatches 0
for {set i 0} {$i < $nTries} {incr i} {
    if {[send_expect $fh "AT\r" "OK" $waitSecs]} {
        incr nMatches
        regsub -all "\r" [send_exp_getbuf $fh] {\r} buf
        regsub -all "\n" $buf {\n} buf
        puts "GOT MATCH: <$buf>"
    } else {
        puts "NO MATCH IN $waitSecs SECONDS"
    }
    send_exp_resetbuf $fh
}
send_exp_end $fh
close $fh
puts "Matched $nMatches/$nTries\
        ([expr 100.0*$nMatches/$nTries]%)"

RJM: Via the serial communication resources, one can also communicate via USB. However, this will surely work with one specific USB peripheral chip, namely from FTDI.

Resource: http://www.ftdichip.com/FTDrivers.htm

They provide a royalty-free USB driver. With Tcl apps the virtual com port driver can be used.

Advantages over RS-232:

  • USB protocol handles transmission errors, so protocol free communication is possible
  • high speed

I myself created a project with Tcl and USB: http://www.msp430web.de/msp430-usb.htm (note that downloadable software resources here may need an update).

MS 2005-01-09: If you want to use the ftdi-chip with a Linux system you can also use the RS-232-emulation of the Linux driver. The USB chip can then be accessed as if it were an RS-232 chip. You can look at http://www.enertex.de/linux/ftdi for some basic code examples.


Twylite: Note that on Windows you can use the device aliases "COM1" .. "COM9" (the trailing colon is not necessary), but to access higher numbered COM ports (quite common when using USB-to-serial converters or Bluetooth dongles) you must use the Win32 device name e.g. {\\.\COM14} .