File Coverage

blib/lib/Device/Delcom/VSI.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Device::Delcom::VSI;
2 1     1   38168 use warnings;
  1         3  
  1         34  
3 1     1   5 use strict;
  1         2  
  1         35  
4 1     1   642 use Device::USB;
  0            
  0            
5             use Carp;
6              
7             use base "Device::USB::Device";
8              
9             =head1 Device::Delcom::VSI
10              
11             This class encapsulates access to one or more Delcom VSI devices.
12              
13             =cut
14              
15             =head1 NAME
16              
17             Device::Delcom::VSI - Use Device::USB to access Delcom VSI devices.
18              
19             =head1 VERSION
20              
21             Version 0.08
22              
23             =cut
24              
25             our $VERSION = 0.08;
26              
27             #use constant VENDORID => 0x0fc5;
28             #use constant PRODUCTID => 0x1223;
29             use constant VENDORID => 4037;
30             use constant PRODUCTID => 4643;
31              
32              
33             my %colors=(
34             green => 0,
35             red => 1,
36             blue => 2,
37             yellow => 2,
38             );
39              
40             ##my %options; #temporary
41              
42             my $DEBUG=0;
43             sub dprint {print @_ if $DEBUG};
44             sub dprintf {printf @_ if $DEBUG};
45              
46              
47              
48             =head1 SYNOPSIS
49              
50             Device::Delcom::VSI provides a Perl object for accessing a Delcom VSI
51             device using the Device::USB module.
52              
53             use Device::Delcom::VSI;
54              
55             my $vsi = Device::Delcom::VSI->new();
56              
57             $vsi->color_set( red => 'on', blue => 'off' );
58             $vsi->led_duty_cycle( 'green', 200, 100 );
59             $vsi->color_set( green => 'flash' );
60              
61             =head1 DESCRIPTION
62              
63             This module defines a Perl object that represents the data and functionality
64             associated with a USB device. The object interface provides read-only access
65             to the important data associated with a device. It also provides methods for
66             almost all of the functions supplied by libusb. Where necessary, the interfaces
67             to these methods were changed to better match Perl usage. However, most of the
68             methods are straight-forward wrappers around their libusb counterparts.
69              
70             =head2 FUNCTIONS
71              
72             =over 4
73              
74             =item new
75              
76             Create an object for manipulating the first VSI device detected.
77              
78             Returns a Device::Delcom::VSI object that supports manipulation of the device.
79              
80             Croaks on error.
81              
82             =cut
83              
84             sub new
85             {
86             my $class = shift;
87             my $usb = Device::USB->new();
88             croak( "couldn't get usb:$!" ) unless $usb;
89              
90             my $obj = $usb->find_device( VENDORID, PRODUCTID );
91             croak( "couldn't open device: $!" ) unless defined $obj;
92             $obj->open();
93              
94             return bless $obj , $class;
95             }
96              
97              
98             =item list
99              
100             Generate a list of Device::Delcom::VSI objects, one for each VSI on the
101             current system.
102              
103             =cut
104              
105             sub list
106             {
107             my $class = shift;
108             my $usb = Device::USB->new();
109             croak( "couldn't get usb:$!" ) unless $usb;
110             my @objs = $usb->list_devices( VENDORID, PRODUCTID );
111            
112             foreach my $obj (@objs)
113             {
114             $obj->open();
115             $obj = bless $obj, $class;
116             }
117            
118             return wantarray ? @objs : \@objs;
119             }
120              
121             =item debug_mode
122              
123             Enable or disable debugging based on the value of the supplied parameter.
124             A true value enables the debug printing, while a false value disables it.
125              
126             =cut
127              
128             sub debug_mode
129             {
130             my $class = shift;
131             $DEBUG = shift;
132             }
133              
134              
135             #
136             # Utility function for converting various on/off/1/0 values to proper
137             # flags. Returns C if the value is not recognized.
138             #
139             # 'on' -> 1
140             # 1 -> 1
141             # 'off' -> 0
142             # 0 -> 0
143             # otherwise -> undef
144             #
145             sub _onoff_to_num
146             {
147             my $val = shift;
148              
149             if('on' eq $val or '1' eq $val)
150             {
151             return 1;
152             }
153             elsif('off' eq $val or '0' eq $val)
154             {
155             return 0;
156             }
157            
158             return;
159             }
160              
161              
162             =item color_set
163              
164             Turn the leds on, off, or make them flash.
165              
166             The parameters to this function are expected in pairs: a color followed by
167             a command. The expected colors are: red, blue, green. For convenience if using
168             the red/green/yellow version of the VSI, yellow can be used instead of blue
169             when issuing commands.
170              
171             The command can be one of the following:
172              
173             =over 4
174              
175             =item on
176              
177             Turn on the named led.
178              
179             =item 1
180              
181             The same as 'on'.
182              
183             =item off
184              
185             Turn off the named led.
186              
187             =item 0
188              
189             The same as 'off'.
190              
191             =item flash
192              
193             Make the named led flash.
194              
195             =back
196              
197             =cut
198              
199             sub color_set
200             {
201             my $self = shift;
202             croak( "Odd number of parameters to color_set.\n" ) if scalar( @_ ) % 2;
203             my %args = @_;
204             my $onbits = 0;
205             my $offbits = 0;
206             my $flashon = 0;
207             my $flashoff = 0;
208              
209             foreach my $color (keys %args)
210             {
211             croak( "Unknown color '$color'\n" ) unless exists $colors{$color};
212              
213             my $cmd = $args{$color};
214             my $colorbit = 1 << $colors{$color};
215              
216             if('flash' eq $cmd)
217             {
218             $flashon |= $colorbit;
219             }
220             else
221             {
222             my $num = _onoff_to_num( $cmd );
223             croak( "Unknown color command '$cmd' for '$color'\n" ) unless defined $num;
224              
225             $flashoff |= $colorbit;
226             if($num)
227             {
228             $onbits |= $colorbit;
229             }
230             else
231             {
232             $offbits |= $colorbit;
233             }
234             }
235             }
236             $self->_port_set_reset( 1, $offbits, $onbits ) if $offbits or $onbits;
237             $self->_flash_mode( $flashon, $flashoff ) if $flashon or $flashoff;
238             }
239              
240              
241             =item set_prescalar
242              
243             Set the scaling value for the clock used in generating all frequencies.
244             Legal values are from 1 to 255. Power on default is 10. Higher numbers
245             means lower frequency.
246              
247             =cut
248              
249             sub set_prescalar
250             {
251             my ($self, $prescalar) = @_;
252             croak( "Invalid prescalar value, must be between 1 and 255.\n" )
253             if $prescalar < 1 or 255 < $prescalar;
254              
255             return $self->_delcom_write_command( 19, 0, $prescalar );
256             }
257              
258              
259             =item led_duty_cycle
260              
261             Set the duty cycle for a given led when it is flashing.
262              
263             =over 4
264              
265             =item color
266              
267             The name of the color to change: red, green, or blue.
268              
269             =item highdur
270              
271             The length of time the led is on in each cycle: 1 - 255.
272              
273             =item lowdur
274              
275             The length of time the led is off in each cycle: 1 - 255.
276              
277             =back
278              
279             =cut
280              
281             sub led_duty_cycle
282             {
283             my ($self, $color, $highdur, $lowdur) = @_;
284             croak( "Invalid color ($color).\n" ) unless exists $colors{$color};
285              
286             return $self->_load_duty_cycle( $colors{$color}, $lowdur, $highdur );
287             }
288              
289              
290             =item led_sync
291              
292             Synchronize the LEDs when flashing.
293              
294             The parameter list consists of a set of pairs of values. Each pair is
295             a color and a state. Any colors not listed will not be synchronized.
296              
297             The legal colors are: red, green, or blue
298              
299             The legal states are: on (1) and off (0).
300              
301             For example:
302              
303             $vsi->led_sync( red => on, green => off, blue => on );
304              
305             and
306              
307             $vsi->led_sync( red => 1, green => 0, blue => 1 );
308              
309             Have the same meaning, red and blue will come on at the same time
310             that green is off. Then, they will swap. (Depending on phase delay
311             and duty cycle, of course.)
312              
313             =cut
314              
315             sub led_sync
316             {
317             my $self= shift;
318             croak( "Odd number of parameters to led_sync.\n" ) if scalar( @_ ) % 2;
319             my %args = @_;
320              
321             my $enable = 0;
322             my $initial_state = 0;
323              
324             foreach my $color (keys %args)
325             {
326             croak( "Unknown color '$color'\n" ) unless exists $colors{$color};
327              
328             my $cmd = $args{$color};
329             my $colorbit = 1 << $colors{$color};
330             $enable |= $colorbit;
331              
332             my $num = _onoff_to_num( $cmd );
333             croak( "Unknown initial state '$cmd' for '$color'\n" )
334             unless defined $num;
335              
336             if($num)
337             {
338             $initial_state &= ~$colorbit;
339             }
340             else
341             {
342             $initial_state |= $colorbit;
343             }
344             }
345             return $self->_synch_clocks( $enable, $initial_state );
346             }
347              
348              
349             =item led_phase_delay
350              
351             Set the delay of the beginning of the cycle for the specified LED.
352              
353             =over 4
354              
355             =item color
356              
357             The color name of the LEDs to adjust: red, green, or blue.
358              
359             =item offset
360              
361             The offset of the beginning of the duty cycle. Legal values are
362             0 - 255. The units are 1.024ms times the prescalar value.
363              
364             =back
365              
366             =cut
367              
368             sub led_phase_delay
369             {
370             my ($self, $color, $offset) = @_;
371             croak( "Unknown color '$color'\n" ) unless exists $colors{$color};
372              
373             return $self->_load_phase_delay( $colors{$color}, $offset );
374             }
375              
376              
377             =item led_intensity
378              
379             Set the brightness of a particular color of LED.
380              
381             The parameter list consists of a set of pairs of values. Each pair is
382             a color and an intensity. Any colors not listed will not be changed.
383              
384             =over 4
385              
386             =item color
387              
388             The color name of the LEDs to adjust: red, green, or blue.
389              
390             =item intensity
391              
392             Brightness as a percentage. Default value is 80. Setting all LEDs
393             above 80 could potentially exceed the current limit of the USB port.
394              
395             =back
396              
397             =cut
398              
399             sub led_intensity
400             {
401             #my ($self, $color, $intensity) = @_;
402             my $self= shift;
403             croak( "Odd number of parameters to led_intensity.\n" ) if scalar( @_ ) % 2;
404             my %args = @_;
405              
406             foreach my $color (keys %args)
407             {
408             croak( "Unknown color '$color'\n" ) unless exists $colors{$color};
409             my $intensity = $args{$color};
410             $self->_light_intensity( $colors{$color}, $intensity );
411             }
412             }
413              
414              
415             =item set_event_count
416              
417             Enable or disable the button event counter.
418              
419             A value of 'on' or 1 enables the counter.
420             A value of 'off' or 0 disables it.
421              
422             =cut
423              
424             sub set_event_counter
425             {
426             my ($self, $on_off) = @_;
427              
428             my @enable;
429             my $num = _onoff_to_num( $on_off );
430             croak( "Unrecognized event_counter state '$on_off'\n" ) unless defined $num;
431              
432             if($num)
433             {
434             @enable = ( 0, 1 );
435             }
436             else
437             {
438             @enable = ( 1, 0 );
439             }
440              
441             return $self->_enable_event_counter( @enable );
442             }
443              
444              
445             =item buzzer_off
446              
447             Turn off the buzzer.
448              
449             =cut
450              
451             sub buzzer_off
452             {
453             my $self = shift;
454            
455             return $self->_buzzer_setup( 0, 0 );
456             }
457              
458              
459             =item buzzer_on
460              
461             Turn on the buzzer, setting its frequency and duty cycle.
462              
463             The parameter list consists of a set of pairs of values. Each pair is
464             optional, and a default value will be substituted if a parameter is missing.
465              
466             =over 4
467              
468             =item freq
469              
470             Frequency value in 256us increments. Legal values are from 1 to 255. Default is 10.
471              
472             =item repeat
473              
474             Number of cycles to repeat. Legal values are 1 - 254. Default is 3. There are also
475             two special values: 0 (full) and 255 (forever)
476              
477             A repeat value of 0 or 'full' causes the buzzer to run continuously at
478             a 100% duty cycle (ignoring the duty_on and duty_off values).
479              
480             A repeat value of 255 or 'forever' causes the buzzer to run with the
481             given frequency and duty cycle continuously.
482              
483             =item duty_on
484              
485             The on time portion of the duty cycle. Default is 3.
486              
487             =item duty_off
488              
489             The off time portion of the duty cycle. Default is 3.
490              
491             =back
492              
493             =cut
494              
495             sub buzzer_on
496             {
497             my $self = shift;
498             my %args = @_;
499             croak( "Odd number of parameters to buzzer_on.\n" ) if scalar( @_ ) % 2;
500             # my ($self, $freq, $repeat, $duty_on, $duty_off) = @_;
501             my $freq = $args{freq} || 10;
502             my $repeat = $args{repeat} || 3;
503             my $duty_on = $args{duty_on} || 3;
504             my $duty_off = $args{duty_off} || 3;
505            
506             $repeat = 0 if !defined $repeat or 'full' eq $repeat;
507             $repeat = 255 if defined $repeat and 'forever' eq $repeat;
508             $repeat &= 0xff;
509              
510             return $self->_buzzer_setup( 1, $freq, $repeat, $duty_on, $duty_off );
511             }
512              
513              
514             =item button_setup
515              
516             Configure the button modes of operation.
517              
518             The parameters to this method are pairs of values. The first item of
519             each pair is a mode string and the second specifies whether the mode
520             is 'on' (1) or 'off' (0). The defined modes are:
521              
522             =over 4
523              
524             =item clear
525              
526             Turn off the buzzer and all LEDs when the button is pressed.
527              
528             =item beep
529              
530             Generate an audible signal when the button is pressed.
531              
532             =back
533              
534             Either or both modes can be turned on or off without effecting the other.
535              
536             Although this method configures the modes. The button mode is not active
537             until the button event counter is enabled. For example, to turn on both
538             modes, use the following code:
539              
540             $vsi->button_setup( clear => 'on', beep => 'on' );
541             $vsi->set_event_counter( 'on' );
542              
543             Once the event counter is enabled, this method can be used to change the
544             button mode without re-enabling the counter.
545              
546             =cut
547              
548             sub button_setup
549             {
550             my $self = shift;
551             croak( "Odd number of parameters to button_setup.\n" ) if scalar( @_ ) % 2;
552             my %args = @_;
553            
554             my $enable = 0;
555             my $disable = 0;
556              
557             foreach my $key (keys %args)
558             {
559             my $num = _onoff_to_num( $args{$key} );
560             croak( "Invalid state for mode '$key': '$args{key}'\n" ) unless defined $num;
561            
562             if('clear' eq $key)
563             {
564             ($num ? $enable : $disable) |= 64;
565             }
566             elsif('beep' eq $key)
567             {
568             ($num ? $enable : $disable) |= 128;
569             }
570             else
571             {
572             croak( "Unrecognized button mode '$key'\n" );
573             }
574             }
575            
576             return $self->_button_setup( $enable, $disable );
577             }
578              
579              
580             =item read_ports
581              
582             Read ports 0 and 1 on the VSI and return the bytes as a two item list.
583              
584             =cut
585              
586             sub read_ports
587             {
588             my $self = shift;
589            
590             my $buffer = $self->_delcom_read_command( 0 );
591              
592             return unless defined $buffer;
593              
594             return unpack( "CC", $buffer );
595             }
596              
597              
598             =item read_button
599              
600             Read the current value of the button on the VSI. A value of 0 means
601             the button is being pushed. A value of 1 means the button is not
602             being pushed.
603              
604             =cut
605              
606             sub read_button
607             {
608             my $self = shift;
609              
610             return ($self->read_ports())[0] & 1;
611             }
612              
613              
614             =item read_buzzer
615              
616             Read the current value of the buzzer pin. Possible values are 0 and 1.
617              
618             =cut
619              
620             sub read_buzzer
621             {
622             my $self = shift;
623              
624             return (($self->read_ports())[1] & 8)>>3;
625             }
626              
627              
628             =item read_leds
629              
630             Read the current values of the LED pins. The result is returned as a
631             reference to a hash, containing the pin values. The keys to the hash are
632             the color names red, green, and blue. A value of 0 means the LEDs on that
633             color are on. A value of 1 means the LEDs of that color are off.
634              
635             =cut
636              
637             sub read_leds
638             {
639             my $self = shift;
640             my $leds = ($self->read_ports())[1];
641             return {
642             green => ($leds & 1),
643             red => ($leds & 2)>>1,
644             blue => ($leds & 4)>>2,
645             };
646             }
647              
648              
649             =item read_event_counter
650              
651             Read the current value of the button event counter. This method returns
652             the current value of the counter and resets the counter to 0.
653              
654             The event counter is a 4 byte value. If the event counter exceeds the
655             value that can be stored in 4 bytes, a special value of 'overflow' is
656             returned.
657              
658             =cut
659              
660             sub read_event_counter
661             {
662             my $self = shift;
663             my ($count, $overflow) = $self->_read_event_counter();
664              
665             return $overflow ? 'overflow' : $count;
666             }
667              
668              
669             =item read_system_variables
670              
671             Read the system variables. The results are decoded and returned as
672             a hash reference. The data stored in the hash reference is:
673              
674             =over 4
675              
676             =item buzzer_running
677              
678             True if the buzzer is currently running.
679              
680             =item counter_overflow
681              
682             True if the button event counter has overflowed.
683              
684             =item auto_clear
685              
686             True if the button is configured to clear when pressed.
687              
688             =item auto_confirm
689              
690             True if the button is configured to beep when pressed.
691              
692             =item prescalar
693              
694             The value of the closk generator pre-scalar.
695              
696             =item address
697              
698             The USB port address.
699              
700             =back
701              
702             =cut
703              
704             sub read_system_variables
705             {
706             my $self = shift;
707            
708             my @sysvars = $self->_read_system_variables();
709            
710             return unless @sysvars;
711              
712             return {
713             buzzer_running => ($sysvars[0] & 0b0001_0000) >> 4,
714             counter_overflow => ($sysvars[0] & 0b0010_0000) >> 5,
715             auto_clear => ($sysvars[0] & 0b0100_0000) >> 6,
716             auto_confirm => ($sysvars[0] & 0b1000_0000) >> 7,
717             prescalar => $sysvars[1],
718             address => $sysvars[2],
719             };
720             }
721              
722              
723             =item read_system_variables
724              
725             Read the formware information. The results are decoded and returned as
726             a hash reference. The data stored in the hash reference is:
727              
728             =over 4
729              
730             =item serial_number
731              
732             The 4-byte serial number.
733              
734             =item version
735              
736             The current firmware version.
737              
738             =item year
739              
740             The 2 digit year of the firmware date.
741              
742             =item month
743              
744             The month number of the firmware date.
745              
746             =item day
747              
748             The day number of the month of the firmware date.
749              
750             =back
751              
752             =cut
753              
754             sub read_firmware
755             {
756             my $self = shift;
757            
758             my @firmware = $self->_read_firmware();
759              
760             return unless @firmware;
761              
762             return {
763             serial_number => $firmware[0],
764             version => $firmware[1],
765             day => $firmware[2],
766             month => $firmware[3],
767             year => $firmware[4],
768             };
769             }
770              
771              
772              
773             =begin COMMENT
774              
775             sub color_set {
776             #my $self = shift;
777             #my $dev = $$self;
778             my $dev = ${(shift)};
779             my %args = @_;
780             foreach my $key (keys %args) {
781             my $color_name = $key;
782             my $color = $colors{$color_name};
783             print STDERR "bad color: $color_name in color_set\n" unless defined $color;
784             next unless defined $color;
785             my $cmd = $args{$key}; # should be on, off, flash
786             #dprint "in color_set, color is $color_name\n";
787            
788             if ($cmd eq "on"){
789             #dprint "turning on $color_name\n";
790             _color_on($dev,$color);
791             _color_flash($dev,$color_name,0);
792             } elsif ($cmd eq "off") {
793             #dprint "turning off $color_name\n";
794             _color_off($dev,$color);
795             _color_flash($dev,$color_name,0);
796             } elsif ($cmd eq "flash") {
797             #dprint "turning flashing on $color_name\ncolor is $color\n";
798             _color_flash($dev,$color_name,1);
799             }
800             }
801             }
802              
803             sub _color_on {
804             my $dev = shift;
805             my $color = shift;
806              
807             $dev->control_msg(
808             0xc8,
809             0x12,
810             (12 * 0x100) + 10,
811             (1 << $color), #MSB on
812             "",
813             0x08,
814             5000
815             );
816             }
817              
818             sub _color_off {
819             my $dev = shift;
820             my $color = shift;
821              
822             $dev->control_msg(
823             0xc8,
824             0x12,
825             (12 * 0x100) + 10,
826             ((1 << $color) * 0x100), #LSB off
827             "",
828             0x08,
829             5000
830             );
831             }
832              
833             sub _color_flash {
834             my $dev = shift;
835             my $color_name = shift;
836             my $color = 1 << $colors{$color_name};
837             #dprint "in _color_flash, color is $color\n";
838             my $onoff = shift;
839             my $lsb;
840             my $msb;
841              
842             if ($onoff) {
843             #dprint "setting flash off for $color\n";
844             $lsb=0;
845             $msb=$color;
846             }else{
847             #dprint "setting flash on for $color\n";
848             $lsb=$color;
849             $msb=0;
850             }
851             $dev->control_msg(
852             0x48,
853             0x12,
854             (10 + (20 * 256)), # 20, enable/disable flash mode
855             ($lsb + ($msb * 256)), # lsb disables, msb enables
856             0x0,
857             0x8,
858             5000);
859             }
860              
861             sub sync {
862             my $dev = ${(shift)};
863             my %args = @_;
864            
865             if (@_){ #set our delays if we have args
866             foreach my $key (keys %args) {
867             my $color_name = $key;
868             my $color = $colors{$color_name};
869             unless (defined $color) {
870             print STDERR "bad color: $color_name in sync\n" unless defined $color;
871             next;
872             }
873             my $delay = $args{$key};
874             my $minor = $color + 26;
875             dprint "setting value of $delay for $color_name\tusing minor of $minor\n";
876            
877             $dev->control_msg(
878             0x48,
879             0x12,
880             (10 + $minor * 0x100),
881             #0 + $delay * 0x100,
882             $delay + 0 * 0x100,
883             0,
884             8,
885             5000);
886             }
887              
888             }
889             $dev->control_msg( #no args? just sync
890             0x48,
891             0x12,
892             (10 + 25 * 0x100),
893             7 + 7 * 0x100,
894             0,
895             8,
896             5000);
897             }
898              
899              
900              
901              
902              
903             sync() if (defined $options{sync});
904             scaler($options{scaler}) if (defined $options{scaler});
905             buzzer($options{buzzer}) if (defined $options{buzzer});
906             clearconfirm($options{clear}) if (defined $options{clear});
907             clearconfirm($options{confirm}) if (defined $options{confirm});
908             vsiread($options{read}) if (defined $options{read});
909             cancel() if (defined $options{cancel});
910              
911             sub cleanup_options{
912             my $opts = shift;
913             foreach my $key (keys %$opts){
914             $$opts{$key} =~ s/,/ /g;
915             }
916             }
917              
918             sub led {
919             my $color = shift;
920             my $options = shift;
921             dprint "LED: $color: $options\n";
922             }
923              
924             sub led_onoff {
925             my $color = shift;
926             my $onoff = shift;
927             if ($onoff eq "on") {
928             dprint "turning $color on\n";
929             }else{
930             dprint "turning $color off\n";
931             }
932             }
933              
934             sub led_flash {
935             my $color = shift;
936             dprint "flashing $color\n";
937             }
938              
939             sub led_duty {
940             my $color = shift;
941             my ($dutyon, $dutyoff) = @_;
942             dprint "$color: dutyon: $dutyon\tdutyoff: $dutyoff\n";
943             }
944              
945             sub led_intensity {
946             my $color = shift;
947             my $intensity = shift;
948             dprint "$color: intensity: $intensity\n";
949             }
950              
951             sub led_syncoffset {
952             my $color = shift;
953             my $syncoffset = shift;
954             dprint "$color: syncoffset: $syncoffset\n";
955             }
956              
957             sub cancel {
958             dprint "cancelling all\n";
959             }
960              
961             sub scaler {
962             my $scaler = shift;
963             dprint "scaler: $scaler\n";
964             }
965              
966             sub buzzer {
967             my $options = shift;
968             dprint "buzzer options: $options\n";
969             }
970              
971             sub buzzer_default {
972             my $repeat = 3;
973             my $frequency = 5;
974             my $dutyon = 6;
975             my $dutyoff = 5;
976             buzzer ($repeat, $frequency, $dutyon, $dutyoff);
977             }
978             sub buzzer_manual {
979             my ($repeat, $frequency, $dutyon, $dutyoff) = @_;
980             dprint "buzzer repeat: $repeat\n";
981             dprint "buzzer frequency: $frequency\n";
982             dprint "buzzer dutyon: $dutyon\n";
983             dprint "buzzer dutyoff: $dutyoff\n";
984             }
985              
986             sub clearconfirm {
987             my $cc = shift;
988             my $onoff = shift;
989             my $ccbits;
990             $ccbits = 128 if $cc eq "confirm";
991             $ccbits = 64 if $cc eq "clear";
992             if ($onoff eq "on") {
993             dprint "$cc: on\n";
994             }else{
995             dprint "$cc: off\n";
996             }
997             }
998              
999             sub vsiread {
1000             my $options = shift;
1001             dprint "read options: $options\n";
1002             }
1003              
1004             =cut
1005              
1006             #
1007             # Utility method for writing to the ports
1008             #
1009             # port - port number: 0 or 1
1010             # byte - the value to write to that port.
1011             #
1012             sub _write_port
1013             {
1014             my ($self, $port, $byte) = @_;
1015             croak( "Invalid port, only 0 and 1 are allowed.\n" ) if $port < 0 or 1 < $port;
1016              
1017             dprintf( "_write_port( port:$port, byte:%02x )\n", $byte );
1018             return $self->_delcom_write_command( 1 + $port, 0, ($byte & 0xff) );
1019             }
1020              
1021             #
1022             # Utility method for writing to both ports
1023             #
1024             # port0val - value to write to port 0
1025             # port1val - value to write to port 1
1026             #
1027             sub _write_both_ports
1028             {
1029             my ($self, $port0val, $port1val) = @_;
1030            
1031             dprintf( "_write_both_ports( port0val:%02x, port1val%02x )\n", $port0val, $port1val );
1032             return $self->_delcom_write_command( 10, ($port1val & 0xff), ($port0val & 0xff) );
1033             }
1034              
1035              
1036             #
1037             # Utility method for (re)setting individual bits on a port.
1038             #
1039             # port - port number: 0 or 1
1040             # setbits - bitmask showing which bits to turn on
1041             # resetbits - bitmask showing which bits to turn off
1042             #
1043             # In case of conflict, reset overrides set.
1044             #
1045             sub _port_set_reset
1046             {
1047             my ($self, $port, $setbits, $resetbits) = @_;
1048             croak( "Invalid port, only 0 and 1 are allowed.\n" ) if $port < 0 or 1 < $port;
1049            
1050             dprintf( "_port_set_reset( port:$port, setbits:%08b, resetbits:%08b )\n", $setbits, $resetbits );
1051             return $self->_delcom_write_command( 11+$port, ($setbits & 0xff), ($resetbits & 0xff) );
1052             }
1053              
1054              
1055             #
1056             # Utility method for setting the flash modes for the leds
1057             #
1058             # enable - bitmask showing which LEDs to flash
1059             # disable - bitmask showing which LEDs to disable flashing
1060             #
1061             # In case of conflict, disable overrides enable.
1062             #
1063             sub _flash_mode
1064             {
1065             my ($self, $enable, $disable) = @_;
1066             $enable &= 0xf;
1067             $disable &= 0xf;
1068              
1069             dprintf( "_flash_mode( enable:%08b, disable:%08b )\n", $enable, $disable );
1070             return $self->_delcom_write_command( 20, $enable, $disable );
1071             }
1072              
1073              
1074             #
1075             # Utility method for loading the duty cycle on a flashing LED.
1076             #
1077             # colornum - the color number to set: 0, 1, or 2
1078             # highdur - period when the pin is high
1079             # lowdur - period when the pin is low
1080             #
1081             # Resolution of the period is 1.024 ms * pre-scalar value
1082             # Resolution of the duty cycle is 0.39 percent.
1083             #
1084             sub _load_duty_cycle
1085             {
1086             my ($self, $colornum, $highdur, $lowdur) = @_;
1087             croak( "Invalid color number ($colornum), must be 0, 1, or 2.\n" )
1088             if $colornum < 0 or 2 < $colornum;
1089              
1090             dprint( "_load_duty_cycle( colornum:$colornum, highdur:$highdur, lowdur:$lowdur )\n" );
1091             return $self->_delcom_write_command( 21+$colornum, $lowdur, $highdur );
1092             }
1093              
1094              
1095             #
1096             # Synchronize the clocks for the different led colors.
1097             #
1098             # enable - bitmask telling which colors to change.
1099             # initial_state - for the on bits in the bitmask, set the initial state
1100             # of the flash.
1101             #
1102             # This command also zeros the phase delay.
1103             #
1104             sub _synch_clocks
1105             {
1106             my ($self, $enable, $initial_state) = @_;
1107              
1108             dprintf( "_synch_clocks( enable:%08b, intial:%08b )\n", $enable, $initial_state );
1109             return $self->_delcom_write_command( 25, $initial_state, $enable );
1110             }
1111              
1112              
1113             #
1114             # Set the phase delay for a particular color
1115             #
1116             # colornum - the color number for the LEDs to change: 0, 1, or 2
1117             # offset - the delay until the beginning of the flash cycle. Legal
1118             # values are 0-255. Resolution is 1.024ms * pre-scalar value
1119             #
1120             sub _load_phase_delay
1121             {
1122             my ($self, $colornum, $offset) = @_;
1123             croak( "Invalid color number ($colornum), must be 0, 1, or 2.\n" )
1124             if $colornum < 0 or 2 < $colornum;
1125              
1126             dprint( "_load_phase_delay( colornum:$colornum, offset:$offset )\n" );
1127             return $self->_delcom_write_command( 26+$colornum, 0, $offset );
1128             }
1129              
1130              
1131             #
1132             # Set the intensity of a particular color of LEDs
1133             #
1134             # colornum - the color number for the LEDs to change: 0, 1, or 2
1135             # intensity - intensity: 0-100. Defaults to 80 at power up.
1136             #
1137             # Setting all LEDs higher than 80 may exceed the current limit of the
1138             # USB port if all LEDs are on at once.
1139             #
1140             sub _light_intensity
1141             {
1142             my ($self, $colornum, $intensity) = @_;
1143             croak( "Invalid color number ($colornum), must be 0, 1, or 2.\n" )
1144             if $colornum < 0 or 2 < $colornum;
1145             croak( "Invalid intensity ($colornum), must be between 0 and 100.\n" )
1146             if $intensity < 0 or 100 < $intensity;
1147              
1148             dprint( "_light_intensity( colornum:$colornum, intensity:$intensity )\n" );
1149             return $self->_delcom_write_command( 34, $intensity, $colornum );
1150             }
1151              
1152              
1153             #
1154             # Enable or disable the event counter
1155             #
1156             # disable - bitmask disabling pins to count events
1157             # enable - bitmask enabling pins to count events
1158             #
1159             # The button is the low-order bit.
1160             #
1161             sub _enable_event_counter
1162             {
1163             my ($self, $disable, $enable) = @_;
1164             $enable &= 0xff;
1165             $disable &= 0xff;
1166              
1167             dprintf( "_enable_event_counter( enable:%08b, disable:%08b )\n", $enable, $disable );
1168             return $self->_delcom_write_command( 38, $disable, $enable );
1169             }
1170              
1171              
1172             sub _buzzer_setup
1173             {
1174             my ($self, $on_off, $freq, $repeat, $duty_on, $duty_off) = @_;
1175             $on_off ||= 0;
1176             $freq ||= 0;
1177             $repeat ||= 0;
1178             $duty_on ||= 0;
1179             $duty_off ||= 0;
1180             my $pointer = pack("CCC", $repeat, $duty_on, $duty_off);
1181              
1182             dprintf( "_buzzer_setup( on_off:$on_off, freq:%02x, repeat:$repeat, dutyon:$duty_on, duty_off:$duty_off )\n", $freq );
1183             return $self->control_msg(
1184             0x48, # 0xc8 for reading, 0x48 for writing
1185             0x12,
1186             (10 | (70 << 8)),
1187             ($on_off | ($freq << 8)),
1188             $pointer,
1189             0x8,
1190             5000 );
1191             }
1192              
1193              
1194              
1195             sub _button_setup
1196             {
1197             my ($self, $enable, $disable) = @_;
1198             $enable &= 0xff;
1199             $disable &= 0xff;
1200              
1201             dprintf( "_button_setup( enable:%08b, disable:%08b )\n", $enable, $disable );
1202             return $self->_delcom_write_command( 72, $enable, $disable );
1203             }
1204              
1205              
1206             sub _read_event_counter
1207             {
1208             my $self = shift;
1209            
1210             my $buffer = $self->_delcom_read_command( 8 );
1211              
1212             return unless defined $buffer;
1213            
1214             return unpack( "VC", $buffer );
1215             }
1216              
1217              
1218             sub _read_system_variables
1219             {
1220             my $self = shift;
1221            
1222             my $buffer = $self->_delcom_read_command( 9 );
1223              
1224             return unless defined $buffer;
1225            
1226             return (unpack( "CCCCC", $buffer ))[0,1,4];
1227             }
1228              
1229              
1230             sub _read_firmware
1231             {
1232             my $self = shift;
1233            
1234             my $buffer = $self->_delcom_read_command( 10 );
1235              
1236             return unless defined $buffer;
1237            
1238             return unpack( "VCCCC", $buffer );
1239             }
1240              
1241             sub _delcom_write_command
1242             {
1243             my ($self, $cmd, $msb, $lsb) = @_;
1244             $cmd &= 0xff;
1245             $msb &= 0xff;
1246             $lsb &= 0xff;
1247              
1248             if(0 > $self->control_msg(
1249             0x48,
1250             0x12,
1251             (10 | ($cmd << 8)),
1252             ($lsb | ($msb << 8)),
1253             undef,
1254             0x8,
1255             5000))
1256             {
1257             croak( "USB access failed: $!\n" );
1258             }
1259             }
1260              
1261              
1262             sub _delcom_read_command
1263             {
1264             my ($self, $cmd) = @_;
1265             my $buffer = "\0"x8;
1266             my $retval = 0;
1267              
1268             if(0 > $self->control_msg(
1269             0xc8,
1270             0x12,
1271             (11 | ($cmd << 8)),
1272             0x00,
1273             $buffer,
1274             0x8,
1275             5000))
1276             {
1277             croak( "USB access failed: $!\n" );
1278             }
1279              
1280             return $buffer;
1281             }
1282             =back
1283              
1284             =head1 DIAGNOSTICS
1285              
1286             This is an explanation of the diagnostic and error messages this module
1287             can generate.
1288              
1289             =over 4
1290              
1291             =item couldn't get usb:{perror}
1292              
1293             Could not access the libusb library or the USB busses. The {perror} should
1294             be an OS-specific message that will shed further light on the problem.
1295              
1296             =item couldn't open device: {perror}
1297              
1298             The Delcom VSI USB device was found, but it could not be opened. The {perror}
1299             should be an OS-specific message that will shed further light on the problem.
1300              
1301             =item Odd number of parameters to color_set.
1302              
1303             The parameters to the color_set method must be pairs consisting of a color and
1304             a command.
1305              
1306             =item Unknown color '$color'
1307              
1308             The supplied color name was not correct. This may be caused by a misspelling,
1309             an incorrect color, or by parameters getting out of sequence.
1310              
1311             =item Unknown color command '$cmd' for '$color'
1312              
1313             The commands string supplied for the named color is unrecognized. This may be
1314             caused by a misspelling, actual bad command, or by parameters getting out of
1315             sequence.
1316              
1317             =item Invalid prescalar value, must be between 1 and 255.
1318              
1319             The number passed to set_prescalar was outside the legal range.
1320              
1321              
1322             =back
1323              
1324              
1325             =head1 DEPENDENCIES
1326              
1327             This module depends on the Carp and use Device::USB modules, as well as
1328             the strict and warnings pragmas.
1329              
1330             =head1 AUTHOR
1331              
1332             Paul Archer (paul at paularcher dot org)
1333             G. Wade Johnson (wade at anomaly dot org)
1334              
1335             Houston Perl Mongers Group
1336              
1337             =head1 BUGS
1338              
1339             Please report any bugs or feature requests to
1340             C, or through the web interface at
1341             L.
1342             I will be notified, and then you'll automatically be notified of progress on
1343             your bug as I make changes.
1344              
1345             =head1 ACKNOWLEDGEMENTS
1346              
1347             Thanks go to various members of the Houston Perl Mongers group for input
1348             on the module. But thanks mostly go to Paul Archer who proposed the project
1349             and helped with the development.
1350              
1351             =head1 COPYRIGHT & LICENSE
1352              
1353             Copyright 2006, 2014 Houston Perl Mongers
1354              
1355             Device::Delcom::VSI is released under the GNU Public License (GPL).
1356              
1357             =cut
1358              
1359             1;
1360             __END__