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

Good Afternoon Monks,

I have a program that uses Win32-Serial and I am sending commands to a receiving decoder which is installed into a model railroad locomotive. Individually the commands work: Ring the bell, blow the horn etc. However, when each command is sent it shuts off the previous command. Below is my code and what I need to be able to do is for example: Lights On stays on even if Bell On is selected. The receiver/decoder does not store commands...it just executes what is sent.

### LIGHTS ON/OFF CODE### sub leftefzeroone { $button53->configure(-text => 'F0 - Left Lights On'); $button53->bind('<ButtonPress>', \&leftefzerotwo); my $tlightson = "<f 1108 144>"; $port->write($tlightson); $port->lookclear(); } sub leftefzerotwo { $button53->configure(-text => 'F0 - Left Lights Off'); $button53->bind('<ButtonPress>', \&leftefzeroone); my $tlightsoff = "<f 1108 128>"; $port->write($tlightsoff); $port->lookclear(); } ### BELL ON/OFF CODE### sub leftefoneone { $button54->configure(-text => 'F1 - Left Bell On'); $button54->bind('<ButtonPress>', \&leftefonetwo); my $tbellon = "<f 1108 136>"; $port->write($tbellon); $port->lookclear(); } sub leftefonetwo { $button54->configure(-text => 'F0 - Left Bell Off'); $button54->bind('<ButtonPress>', \&leftefoneone); my $tbelloff = "<f 1108 128>"; $port->write($tbelloff); $port->lookclear(); }

As you can see from the code the number 128 is the byte number in the receiver/decoder that shuts any function ON to OFF. What I need to implement is a type of IF EXISTS statement. I'm lost as to where to begin on that. I can do simple IF FILE EXISTS type of routines. Thanks in advance!

Replies are listed 'Best First'.
Re: Saving Options
by cragapito (Scribe) on Dec 20, 2015 at 21:01 UTC

    Seems that, to your controller, the most significant bit is expected to be aways set, the 4th bit sets the lights and the 5th the bell. So you can store the start state (or we could call it reset state) "128" in a variable, toggle the state of each bit with XOR bitwise against a mask and interpolate with a command to receiver "<f 1108 ...

    To start only (not toggling) you could check the state with AND against the interest mask, if state is off the AND result is zero (or false):

    my $state = 128; # start state, binary: 1000 0000 my $lightmask = 16; # 0001 0000 my $bellmask = 8; # 0000 1000 sub togglelight { $state = $state ^ $lightmask; $port->write("<f 1108 $state"); $port->lookclear(); } sub bellon { if (not ($state & $bellmask)) { $state = $state ^ $bellmask; $port->write("<f 1108 $state"); $port->lookclear(); } } sub belloff { if ($state & $bellmask) { $state = $state ^ $bellmask; $port->write("<f 1108 $state"); $port->lookclear(); } }

    Hope this helps.

    Cleyton

      I will try this and report back....I can do the not state with the lights as well....since I can turn the lights on and off too the same as the bell and horn correct?

        Yes, Each call to togglelight inverses the state. Useful to have a flashing light.

        Calling bellon/belloff there are a conditional, so it only executes the block (and send command to receiver) if needed.

        These are two ways to send command, with toggle and with pre-determined state. If you want to determine the state of the lights use the bellon/belloff example, only changing bell by light. One will not interfere in other.

        We don't have the horn code. Take the horn number, subtract 128, the result will be the hornmask.

Re: Saving Options
by FreeBeerReekingMonk (Deacon) on Dec 20, 2015 at 21:28 UTC

    The IF EXISTS can be stored in a STATE hash, like so:

    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = new MainWindow(); my %STATE=( 'lights'=>0, 'bell'=>0 ); my %ENABLE=( 'lights'=>144, 'bell'=>136, 'lights&bell' => 144|136, # not sure here 'nothing' => 128 # enable nothing ); my $lights; my $bell; # Exit-Button my $message = $mw->Entry()->pack; $mw->Button(-text=>'End', -command=>sub {exit} )->pack; $lights = $mw->Button(-text=>'Turn LIGHTS on', -command=>sub { # toggle lights $STATE{'lights'} = 1 - $STATE{'lights'}; # can also do != # send data to turn on/off lights calculateInstruction(); $lights->configure(-text => 'Turn LIGHTS '.($STATE{'lights'}?'off' +:'on')); updateMessage(); } )->pack; $bell = $mw->Button(-text=>'Turn BELL on', -command=>sub { # toggle lights $STATE{'bell'} = 1 - $STATE{'bell'}; # can also do != # send data to turn on/off lights calculateInstruction(); $bell->configure(-text => 'Turn BELL '.($STATE{'bell'}?'off':'on') +); updateMessage(); } )->pack; sub calculateInstruction{ my $n; if($STATE{'lights'} && $STATE{'bell'}){ $n = $ENABLE{'lights&bell'} }elsif($STATE{'lights'}){ $n = $ENABLE{'lights'} }elsif($STATE{'bell'}){ $n = $ENABLE{'bell'} }else{ $n = $ENABLE{'nothing'} } my $instruction = "<f 1108 $n>"; print "Would send $instruction to serial\n"; # $port->write($instruction); # $port->lookclear(); } sub updateMessage{ my $msg = 'LIGHTS='.$STATE{'lights'}." BELL=".$STATE{'bell'}; $message->configure(-text => $msg); } MainLoop;

    Not sure about the exact codes you need to send, check your manual. Would sending <f 1108 144> then <f 1108 136> turn on both lights and horn? If you send the same command twice, would it toggle the lights? or would this work:

    sub calculateInstruction{ my $instruction; if($STATE{'lights'} && $STATE{'bell'}){ $instruction = "<f 1108 144><f 1108 136>" }elsif($STATE{'lights'}){ $instruction = "<f 1108 128><f 1108 144>" }elsif($STATE{'bell'}){ $instruction = "<f 1108 128><f 1108 136>" }else{ $instruction = "<f 1108 128>" } print "Would send $instruction to serial\n"; # $port->write($instruction); # $port->lookclear(); }

    If it is bitwise, like cragapito thinks, then this should work:

    sub calculateInstruction{ my $n = $ENABLE{'nothing'}; for my $k (keys %STATE){ $n |= $ENABLE{$k} if ($STATE{$k}); } my $instruction = "<f 1108 $n>"; print "Would send $instruction to serial\n"; # $port->write($instruction); # $port->lookclear(); }

      I will try this and report back

Re: Saving Options
by poj (Abbot) on Dec 20, 2015 at 21:50 UTC

    Store the output states (off/on as 0,1) in an array and convert to decimal when you make a change. Something like this perhaps

    #!perl use strict; my @io = (1,0,0,0,0,0,0,0); # all off switch(3,1); # left light on switch(3,0); # left light off switch(4,1); # left bell on switch(4,0); # left bell off switch(3,1); # left light on switch(4,1); # left bell on switch(3,0); # left light off switch(4,0); # left bell off sub switch { $io[$_[0]] = $_[1]; my $num = oct('0b'.join '',@io); # bin to dec my $cmd = '<f 1108 '.$num.'>'; print $cmd."\n"; #$port->write($cmd); #$port->lookclear(); }
    poj

      Might want to add a sleep(2); between every switch() command to see light changes, or else they would flicker too fast

Re: Saving Options
by Mr. Muskrat (Canon) on Dec 21, 2015 at 20:09 UTC

    Here's some code that I based on the links below with changes for the values you gave for F0 and F1. (It sounds like yours is wired in a better fashion than theirs.) It's a subroutine for calculating the command to send.

    #!/bin/env perl use strict; use warnings; use List::Util qw(sum0); use List::MoreUtils qw(uniq); { my %buttons = ( '0' => 16, '1' => 8, '2' => 4, '3' => 2, '4' => 1, ); sub buttonsPressed { my ($cab, @pressed) = @_; my @values = map { 0 + $buttons{$_} } uniq @pressed; return sprintf "<f %d %d>", $cab, 128 + sum0 @values; # Yes, these + two lines could be combined } } my $cab = 1108; my @tests = ( [ 0 ], [ 1 ], [ 2 ], [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 0, 1, 2 ], ); for my $test ( @tests ) { print "@{$test} : ", buttonsPressed( $cab, @{$test} ), "\n"; } __END__ 0 : <f 1108 144> 1 : <f 1108 136> 2 : <f 1108 132> 0 1 : <f 1108 152> 0 2 : <f 1108 148> 1 2 : <f 1108 140> 0 1 2 : <f 1108 156>

    Related links?

      That is correct...I am part of that group on TrainBoard talking about DCC Plus and the person coding the base station and controller code uses Java and Win32-Serial is what I use to communicate with the Arduino while others are using JMRI etc. So far everything I've been doing has been working out very well in communicating with decoders etc.