Using TK to read/write to serial port, allowing stdout display and TK GUI control 
Author Message
 Using TK to read/write to serial port, allowing stdout display and TK GUI control

Hello,

I'm using a TK GUI to write data to and read data from a serial port.
One of the button widgets needs to send a specific command to the
serial port, which programs a microprocessor to essentially run in a
loop until interrupted. To interrupt the microprocessor, I am using the
same TK GUI (another button widget) to send the appropriate interrupt
character to the serial port.

To try to implement this, I send the initial microprocessor programming
command to the serial port, and it's received correctly. The
microprocessor then sends the text strings back to the TK GUI. What I
want to do is read the data from the serial port, and display it to
stdout, but still allow the TK GUI to send a character down the serial
port when the button widget is pressed, causing the microprocessor loop
to be terminated. When terminated, the microprocessor sends a '>'
character, which would be easy to parse from a read command.

I'm new to TK, so I'm not sure how to achieve the above. I tried using
the code snipped below. The first chunk sends the 'M' character to the
serial port, which programs it. I then read from the serial port byte
by byte, until I receive the acknowledgment command "Waiting for
trigger". This works. It's the second chunk that fails miserably! The
TK GUI hangs in a loop, because I can never invoke the TK button widget
to send the break command.

If this is a bit complicated to do, rather than write to stdout, I
could use message windows to relay info (e.g. to essentially echo back
the commands from the microprocessor), as long as the TK GUI button
widget can still send the interrupt command.

If anyone has any suggestion for how I can get this to work, I'd really
appreciate it.

Thanks!

****

   set i 0
   fileevent $comm_port writable [flush $comm_port; puts $comm_port
"M"; flush $comm_port]
   fileevent $comm_port readable [set read_str [read $comm_port 1]]
   while { ! [regexp {Waiting for trigger} $read_str match] } {
      fileevent $comm_port readable [append read_str [read $comm_port
1]]
      incr i
      if {$i > 100000} {
         puts "Error entering stim mode \"M\", exiting TCL from routine
WriteScript ..."
         puts "read_str: $read_str"
         exit
      }
   }

   fileevent $comm_port readable [set read_str [read $comm_port 1]]
   while { ! [regexp {> } $read_str match] } {
      fileevent $comm_port readable [set read_str [read $comm_port 1]]
      puts "$read_str"
   }

****



Tue, 09 Dec 2008 19:48:23 GMT  
 Using TK to read/write to serial port, allowing stdout display and TK GUI control


Hi Norbert,
basically there is a misunderstanding of Tcl's filevent. You are
supposed to assign a _script_ and whenever the vent triggers, this
script will get executed.
A consequence is that (usually) you assign this script only once.

Quote:
>   set i 0
>   fileevent $comm_port writable [flush $comm_port; puts $comm_port
>"M"; flush $comm_port]

What you are doing here:
- You execute the command sequence "flush ... ; puts ... ; flush ...",
which happens to send out 'M' - but has nothing to do with file event.
- You then assign the result of this sequence (the result of the last
command (flush) - probably the empty string) as file event.

In the loop below you do basically the same: You read one character,
assign it to fileevent (which is absolutely pointless and would result
in an error if the event would ever trigger - which doesn't happen
because you do all the reading yourself).

Quote:
>   fileevent $comm_port readable [set read_str [read $comm_port 1]]
>   while { ! [regexp {Waiting for trigger} $read_str match] } {
>      fileevent $comm_port readable [append read_str [read $comm_port
>1]]
>      incr i
>      if {$i > 100000} {
>         puts "Error entering stim mode \"M\", exiting TCL from routine
>WriteScript ..."
>         puts "read_str: $read_str"
>         exit
>      }
>   }

My advice:
1) Read up on fileevent.
2) Don't use it for writing - it will fire endlessly whenever the port
can be written, which doesn't make sense.
3) Start out simple (without events).
proc initDevice (comPort) {
    puts $comPort "M" ;# note: this appends a \n or \r\n
    flush $comPort      ;# shouldn't be necessary
    # add your read loop here (without (!!) fileevent
    # and return 0 / 1 for failure success
    ...

Quote:
}

4) Look on the wiki for examples for fileevent / handling of the
serial port - sorry, got no URL right now. Do you know how to search
on the wiki?
5) (This would be the way I'd do it.) Get it to work without a GUI,
then add one - however you then need to start the filevent yourself
and this might complicate matters for you - don't know.
6) Get moni - Rolf Schroedter's excellent serial communication
program. It can at least help to get any communication problem solved,
because you "just see" what happens - it might even solve all your
needs.
7) Come back here if you have further questions
HTH
Helmut Giese


Tue, 09 Dec 2008 20:41:12 GMT  
 Using TK to read/write to serial port, allowing stdout display and TK GUI control

Quote:

>Hello,

>I'm using a TK GUI to write data to and read data from a serial port.
>One of the button widgets needs to send a specific command to the
>serial port, which programs a microprocessor to essentially run in a
>loop until interrupted. To interrupt the microprocessor, I am using the
>same TK GUI (another button widget) to send the appropriate interrupt
>character to the serial port.

>To try to implement this, I send the initial microprocessor programming
>command to the serial port, and it's received correctly. The
>microprocessor then sends the text strings back to the TK GUI. What I
>want to do is read the data from the serial port, and display it to
>stdout, but still allow the TK GUI to send a character down the serial
>port when the button widget is pressed, causing the microprocessor loop
>to be terminated. When terminated, the microprocessor sends a '>'
>character, which would be easy to parse from a read command.

>I'm new to TK, so I'm not sure how to achieve the above. I tried using
>the code snipped below. The first chunk sends the 'M' character to the
>serial port, which programs it. I then read from the serial port byte
>by byte, until I receive the acknowledgment command "Waiting for
>trigger". This works. It's the second chunk that fails miserably! The
>TK GUI hangs in a loop, because I can never invoke the TK button widget
>to send the break command.

>If this is a bit complicated to do, rather than write to stdout, I
>could use message windows to relay info (e.g. to essentially echo back
>the commands from the microprocessor), as long as the TK GUI button
>widget can still send the interrupt command.

>If anyone has any suggestion for how I can get this to work, I'd really
>appreciate it.

>Thanks!

>****

>   set i 0
>   fileevent $comm_port writable [flush $comm_port; puts $comm_port
>"M"; flush $comm_port]
>   fileevent $comm_port readable [set read_str [read $comm_port 1]]
>   while { ! [regexp {Waiting for trigger} $read_str match] } {
>      fileevent $comm_port readable [append read_str [read $comm_port
>1]]
>      incr i
>      if {$i > 100000} {
>         puts "Error entering stim mode \"M\", exiting TCL from routine
>WriteScript ..."
>         puts "read_str: $read_str"
>         exit
>      }
>   }

>   fileevent $comm_port readable [set read_str [read $comm_port 1]]
>   while { ! [regexp {> } $read_str match] } {
>      fileevent $comm_port readable [set read_str [read $comm_port 1]]
>      puts "$read_str"
>   }

>****

What you want absolutely is possible.

Your use of [fileevent] is misguided.

I can't make the time now to detail all you should do.  Possibilities
that come to mind:
1.  Wait for someone else to correct what's above.
2.  Read up on fileevent <URL: http://wiki.tcl.tk/fileevent >
    yourself, and rewrite your code in a more conventional
    style.  You might well find all your problems solved.
3.  Restate your problem with a narrow focus on just the
    reproducibly misbehaving "second chunk", so suggestions
    can be more narrowly focused.
You choose.



Tue, 09 Dec 2008 21:29:42 GMT  
 Using TK to read/write to serial port, allowing stdout display and TK GUI control
Quote:
>5) (This would be the way I'd do it.) Get it to work without a GUI,
>then add one - however you then need to start the filevent yourself
>and this might complicate matters for you - don't know.

Correction: You need to start "Tcl's event loop" yourself (so that
file events can trigger) - not the 'file event'.


Tue, 09 Dec 2008 22:10:20 GMT  
 Using TK to read/write to serial port, allowing stdout display and TK GUI control
Hi,

Thank you for the quick response and helpful advice. I am going to
re-write the code using fileevent properly (if at all?). In the
meantime, I was able to use the 'after' command to recursively call a
read procedure (to read from the serial port), similar to the following
code I found for another entry on this group (for creating a counter):

***

set Seconds 0
label .sec -textvariable Seconds
pack .sec

proc tick {} {
   global Seconds
   incr Seconds
   after 1000 tick

Quote:
}

tick

***

Using this approach, I am able to acheive what I want at the moment,
however I do want to change the code to read/write from/to the serial
port in a more conventional manner.

It looks like other postings may have closed their issues. Should I
flag this as a closed issue?

Again, thanks for all the help, I really appreciate it.
Norbert



Fri, 12 Dec 2008 10:49:43 GMT  
 Using TK to read/write to serial port, allowing stdout display and TK GUI control

Quote:
> [...] In the
> meantime, I was able to use the 'after' command to recursively call a
> read procedure (to read from the serial port), similar to the following
> code I found for another entry on this group (for creating a counter):
> [...]
> proc tick {} {
>    global Seconds
>    incr Seconds
>    after 1000 tick
> }

> tick

To be pedantic, this isn't recursion, since each call to [tick] actually
terminates long before the next call is started by the event loop.
Think of it more as a user-level interrupt service routine that's tied to
timer ticks.  I think the distinction is important because if it really
*were* recursion (say, due to a logic bug), you'd probably have a stack
overflow and much badness before long.

In any case, the above is a classic Tcl idiom viz. programming with
events, and is certainly worth keeping in mind.

- Adrian



Fri, 12 Dec 2008 13:49:32 GMT  
 
 [ 6 post ] 

 Relevant Pages 

1. Using the serial ports under Win95/python/Tk

2. Read/write PCI Contro Registers of x86

3. How do I read/write serial port?

4. writing/reading from serial port

5. Reading and writing individual bits on the serial port

6. Reading/writing to serial port w/ VISA in Labview

7. Read/Write to serial port?

8. READ/WRITE serial Port MF 3.4

9. TIP #47: Modifying Tk to Allow Writing X Window managers

10. Serial port read/write

11. read/write serial port -- again ...

12. Bug in writing result to stdout in tk?

 

 
Powered by phpBB® Forum Software