[More Serial Port Samples] and "[How to read the serial port, and display in hexadezimal]" ---- Open the serial port using [[open]] (port names are platform-dependent.) Configure useful comms stuff with [[fconfigure]]. Use [[fileevent]] to get readable/writable callbacks. And use [[close]] when you are done. IOW, pretty much as you would hope! :^) ---- 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]%)" ----- D. J. Hagberg mailto:dhagberg@millibits.com ---- [[Did [TCL-DP] include special serial-line capabilities?]] ---- [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/FTDriver.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. ---- Several Tcl-indifferent products sniff or otherwise help understand serial-line traffic, including Sysinternals' Portmon [http://www.sysinternals.com/Utilities/Portmon.html], ... ---- Of related interest: [Parallel port]. ---- [Category Device Control]