Shaoboy has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks!

I'm currently working on a project for one of my university class. It's a temperature logger between 2 microcontroller through RF. The MASTER device sends its temp to the SLAVE device which tags a timestamp to it and sends this datastream to the PC through Serialport. Each sec I send a stream like this: Temperature: tempval Time: hh:mm:ss

Now i have to write the PC-side application, but the problem is that we had only few lesson in Perl programming and we mainly focused to the basics and Perl/tk.

The main goal is to make graphs from the data what i get from the serialport. I want to bound the port opening/closing to buttons, but when i do this it's starting read the previously chosen port and then the GUI freeze out. I know it's because i use a while()-loop for reading the serialport which run up against Mainloop();. I read about 'threads' and 'repeat' and that they can solve my problem, but it's not clear how to use them,and how can i pass data from a thread to the Tk where i could use it for displaying/editing etc.

sub opening_port { print "Connecting to $ports[$portname]..."."\n"; $serial->baudrate(9600) || die "Bad baudrate"; $serial->parity('none') || die "Bad parity"; $serial->databits(8) || die "Bad databits"; $serial->stopbits(1) || die "Bad stopbits"; #$serial->buffers(4096,4096) || die "Buffer error"; $serial->handshake("none") || die "Bad handshake method"; $serial->write_settings; #print $serial."\n"; while($port_opened) { my @array; my $string; my $i=0; do { $string=$serial->read(1); if((ord($string) >= 0x09 && ord($string) <=0x7A) || $string eq '\t' + || $string eq '\n') { $array[$i]=$string; $i++; } }while($string ne "\n" ); $i=0; $string = join( '' , @array ) ; chomp($string); my @values = split('\t',$string); print $values[3]; } close($serial); }

Open - button runs the

sub open_port { if($listbox1->curselection) { $portname=$listbox1->index($listbox1->curselection()); $port_opened=1; &port_init; } else { &portwarning; }

In port_init subroutine i just checking the os system and calling the required SerilPort modul and sub opening_port

Buttons for the serailport routine

$btn1=$mw->Button(-text=>"Open",-width=>5,-height=>2,-command=>\&open_ +port) ->place(-relx=>0.05,-rely=>0.25); $btn2=$mw->Button(-text=>"Close",-width=>5,-height=>2,-command=>\&clos +e_port) ->place(-relx=>0.15,-rely=>0.25);
Hope you can help me out! ps.:Sorry for my English

Replies are listed 'Best First'.
Re: Perl/Tk vs Win32::SerialPort
by Anonymous Monk on Dec 10, 2013 at 03:25 UTC
Re: Perl/Tk vs Win32::SerialPort
by zentara (Cardinal) on Dec 10, 2013 at 14:34 UTC
    First of all, Tk and threads need special handling, which may confound a beginner. You can find alot of sample code with a google for "Perl/Tk threads". However, I will tell you that even with a thread, you will still need to run a timer to update the shared variable in the main Tk thread. So I will skip the thread approach and show a linux example which just uses a timer to repeatedly read the serial port. This example is for a Linux system, but you should be able to adapt it to Win32.

    The basic idea, is to set a textvariable $input which automatically gets updated from the repeater.

    use warnings; use strict; use Tk; use Device::SerialPort; #my $serial_port = "/dev/ttyS0"; my $serial_port = new Device::SerialPort ("/dev/ttyS0") || die "can't open COM1\n"; #Setup Perl/Tk my $mw = MainWindow->new(); $mw->title("Scale"); #Close button my $button = $mw->Button( -text => 'Close', -command => sub {$mw->destroy} # exit is better here )->pack( -pady => 20, -side => 'bottom' ); my $title_label = $mw->Label( -text => "Weigh-Tronix" )->pack( -side => 'left'); my $input = '---------------'; my $display_label = $mw->Label( -textvariable => \$input, -width=>15, #keep the width stable at 15 )->pack( -side => 'left'); # every 10 millisecs update from port $mw->repeat(10, sub{ # $input = $port->input; #or maybe better if(defined $port->input){ $input = $port->input } }); MainLoop; __END__

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Perl/Tk vs Win32::SerialPort
by Shaoboy (Initiate) on Dec 10, 2013 at 16:53 UTC

    Thank you, eventually i managed to set up the 'serial' thread and i can share its '@values' with Tk but the problem is that it's still in an infinite while loop.

    $mw->repeat(1000,sub{ print "@values"."\n"; });

    But is it possible to run a thread when i want it to run not just in a while(1) loop all the time?

    I tried to pass another variable to my serial thread which determines the running state

    share @values; share $opened_port;

    Where $opened_port is set when i click on the Open button

    sub opening_port { require Win32::SerialPort; $serial=Win32::SerialPort->new("COM6"); $serial->baudrate(9600) || die "Bad baudrate"; $serial->parity('none') || die "Bad parity"; $serial->databits(8) || die "Bad databits"; $serial->stopbits(1) || die "Bad stopbits"; #$serial->buffers(4096,4096) || die "Buffer error"; $serial->handshake("none") || die "Bad handshake method"; $serial->write_settings; while(1) { print $opened_port; if($opened_port) { my @array; my $string; my $i=0; do { $string=$serial->read(1); #Beolvass egy karakter +et a sorosportról if((ord($string) >= 0x09 && ord($string) <=0x7A) || $string eq '\t' + || $string eq '\n') #megvizsgálja, hogy a beolvasott karakter megfel +el-e az ASCII kritériumoknak { $array[$i]=$string; #elmenti a tömbbe i.-ik helyére az adott karakt +ert $i++; #növeli az index értékét } }while($string ne "\n" ); #addig fut a program amíg nem \n a beolva +sott karakter $i=0; $string = join('' , @array ) ; #ezután egy skalárba összef&#369;z +i a tömb elemeit, egymás után - nincs szeparátor chomp($string); # leszedi a \r és \n karaktereket a string végér&#337; +l @values = split('\t',$string); #majd felbontja a skalárt a tabul +árotok mentén # az 1-es index tartalmazza a a h&#337;mérsékleti adatokat, a 3-as ind +ex az id&#337;t } } }

    Of course i get an error which says that $opened_port is uninitialized, so i guess that shareing variable is a child to parent thing and it won't work in the other way. But what could be the proper way to read the serial port on button press and close it on another button event?

      Okay I was a noob and shared a wrong variable with the serial thread...Now everything works like a charm!! Thank you Anonymous Monk and zentara for helping me!

        One more question :) Is there a way to share my $serial object with the serialport thread?

        I want to share this:

        $serial=(Win32::SerialPort->new($ports[$portname])

        So my serial worker thread could read from this. The only need for that is cause i select COMx ports from a listbox and i don't know how to tell the thread to read from the specific port

        my $serial :shared = shared_clone({}); bless($serial, 'Serial');

        When i share my serial object i get an error at $serial->read(1); line saying "Thread 1 terminated abnormally: Can't call method "read" on an undefined value"