Training, Open Source computer languages
PerlPHPPythonMySQLApache / TomcatTclRubyJavaC and C++LinuxCSS 
Search for:
Home Accessibility Courses Diary The Mouth Forum Resources Site Map About Us Contact
Server under maintenance; some slow running (0)
Serial I/O Fileevent  - vwait always req'd?

Posted by neilvp (neilvp), 18 September 2006
In experimenting with TCL & serial comms under Windows I tried the following code :-


*******************************
set baudrate 9600
set parity N
set databits 8
set stopbits 1

# Open up COM1 as Read Write

set com1port [open com1 RDWR]

# Now configure the port to the correct parameters

fconfigure $com1port -mode "$baudrate,$parity,$databits,$stopbits"
fconfigure $com1port -blocking 0 -pollinterval 10 -handshake none

fileevent $com1port readable { serial_receiver $com1port }

set x 0

proc serial_receiver { chan } \
{
    global x
    puts "Serial Rx called\r"  
    if { [eof $chan] } \
    {
        puts stderr "Closing $chan"
        catch {close $chan}
        return
    }
    set data [read $chan]
    set size [string length $data]
    puts "received $size bytes: $data"
   
    incr x
    puts "X = $x\r"
   
}


 puts -nonewline $com1port "\r"
 flush $com1port

#vwait x


close $com1port
puts "Program Ends\r"

**************************

and nothing happend, i.e. no response was registered despite there being activity on the port  - measured with a 'scope.

However, if I added in the vwait statement at the bottom I then got what i was (sort of ) expecting.

My question is, is a vwait statement always required to kick off the event handler as I have not seen this explicity stated in any of the books I have on TCL?

Many thanks

Neil

PS. Does anyone have any successful examples of using Expect on serial comms under windows?

Posted by admin (Graham Ellis), 19 September 2006
The 'quick' answer is that I suspect you do always need a vwait. Otherwise, your program is likely to have run to completion and exited before any events happen so you'll miss all the inputs.   But please take my answer with a pinch of salt; this in't my strong area!

Posted by Custard (Custard), 19 September 2006
A vwait or some kind of loop.

The real processing is done by the event handler which changes x at the end triggering the vwait.  This means only one event has to be triggered for the program to end. That event might not contain all the data you are looking for.

I haven't looked to see how filevent is triggered, but it could be that part of your message triggers one event, and some other portion of the message arrives later triggering another event, so you'd need to check for that.

I can look up the code I came up with last time I did some serial IO in TCL if you like..

B

Posted by neilvp (neilvp), 20 September 2006
I would appreciate any hints as to how best to implent interactive serial I/O as it is proving more awkward then it should be.

In further tests I have found that it look as though fileevent is triggered as soon as something arrives in the I/O buffer - but this is not at the end of the message. If this data is then read fileevent triggers again when somemore of the message arrives. This makes checking what has come in (and reacting accordingly) somewhat what difficult.
The work around I came up with is to check the length of the queue, (fconfigure $fileID -queue) wait awhile and then check it again, if the length has not changed assume the whole message has come in, read it and act upon it.

I could try concatenating the inbound strings but how would I know when to stop waiting for further fileevents?

# Set up parameters for Serial port

set baudrate 9600
set parity N
set databits 8
set stopbits 1


set delaytime 5

set com1port [open com1 RDWR]

puts "Com 1 open\r"

# Now configure the port to the correct parameters

fconfigure $com1port -mode "$baudrate,$parity,$databits,$stopbits"
fconfigure $com1port -blocking 0 -handshake none -buffering line

fileevent $com1port readable { serial_receiver $com1port }

set x 0

proc serial_receiver { chan } \
{
    global x
    global com1port  
    global delaytime
   
    puts "Serial Rx called\r"
   
    if { [eof $chan] } \
    {
        puts stderr "Closing $chan"
        catch {close $chan}
        return
        incr x
    }
   
# What we are now going to do is wait until the characters stop arriving in the buffer.
# fileevent triggers as soon as something is in the buffer but not necessarily when
# the buffer is full or stuff stops arriving.

# So, measure the queue length, wait a bit and then measure it again, if the two match
# then assume stuff has stopped arriving and read the buffer.
# This is of course dangerous if the stuff is not arriving at a regular rate.
# At 9600 it is approx 1ms / character.

# NB fconfigure $fileID -queue returns two values, one for the inbound queue, the other
# for the outbound queue
#
# Could use -queue option instead of fileevent. Simply check the length of the inbound
# if the length is not zero, deal with it. If it is zero wait a while and check again.


   
   set quelength 0
   set quetest  1
   
    while {$quetest != $quelength} \
    {

# Only take the inbound queue length i.e. the first value, so use [lindex <string> 0]
       
       set quelength [lindex [fconfigure $com1port -queue] 0 ]
       after $delaytime
       set quetest [lindex [fconfigure $com1port -queue] 0 ]
       puts "QL = $quelength QT =$quetest\r"
       
    }  
   
   
    set data [read $chan]
    set size [string length $data]
    puts "received $size bytes: $data"

    switch -regexp -- $data \
    {
               React accordingly....
               default \
       {
           puts "Rx'd $data - nothing matched\r"
           incr x
       }
   }
}


 puts -nonewline $com1port "\r"
 flush $com1port

# Start the event loop. The program will keep running until the value of x changes.

vwait x


close $com1port
puts "Program Ends\r"



Posted by Custard (Custard), 20 September 2006
Sounds like you're on the right lines.

My solution was a bit rougher than yours actually. It used 'after' and polled the serial port.  'fileevent' seems much neater.

Basically, what you'd do is:-

*) Collect your data in a buffer string

*) Then scan the string to see if it has whatever you are using as an end marker in it.

*) If it has, then chop the string up at the marker, and the first half becomes your data message to pass on, and the second half becomes the start of the next buffer.

*) If not, then go back and wait for more data

Looks like you started looking for end markers with your regexp so I think you are almost there.

I really don't know the format of your messages or what you are doing with them so I can't be more specific.

B

Posted by iamxiaop (iamxiaop), 31 July 2007
If you change "vwait x" to "vwait y" or "vwait any other variables not changeable" , you will make the programe runing all the time, and you will recieve data as long as there is data.

for example,

Example 16-2 Using vwait to activate the event loop
proc Stdin_Start {prompt} {
  global Stdin
  set Stdin(line) ""
  puts -nonewline $prompt
  flush stdout
  fileevent stdin readable [list StdinRead $prompt]
  vwait Stdin(wait)
}
proc StdinRead {prompt} {
  global Stdin
  if {[eof stdin]} {
     exit
  }
  append Stdin(line) [gets stdin]
  if {[info complete $Stdin(line)]} {
     catch {uplevel #0 $Stdin(line)} result
     puts $result
     puts -nonewline $prompt
     flush stdout
     set Stdin(line) {}
  } else {
     append Stdin(line) \n
  }
}

because  vwait Stdin(wait) will never change, so the programe loops.



This page is a thread posted to the opentalk forum at www.opentalk.org.uk and archived here for reference. To jump to the archive index please follow this link.

You can Add a comment or ranking to this page

© WELL HOUSE CONSULTANTS LTD., 2014: Well House Manor • 48 Spa Road • Melksham, Wiltshire • United Kingdom • SN12 7NY
PH: 01144 1225 708225 • FAX: 01144 1225 899360 • EMAIL: info@wellho.net • WEB: http://www.wellho.net • SKYPE: wellho