File Coverage

blib/lib/AmbientOrb/Serial.pm
Criterion Covered Total %
statement 17 33 51.5
branch 3 8 37.5
condition n/a
subroutine 6 8 75.0
pod n/a
total 26 49 53.0


line stmt bran cond sub pod time code
1             package AmbientOrb::Serial;
2            
3            
4            
5             =head1 NAME
6            
7             AmbientOrb::Serial - Perl module for interfacing with your Orb via serial port.
8            
9             =head1 VERSION
10            
11             Version 0.02
12            
13             =cut
14            
15             our $VERSION = '0.02';
16            
17             =head1 SYNOPSIS
18            
19             This module allows you to do communicate with your ambient orb via serial port.
20             Please see the reference manual at L
21             if you want to delve a little deeper. The ambient orb home page can be found
22             at L.
23            
24             Tested only on a Win32 system, but it should work fine for a non-Windows host; just
25             pass the constructor the /dev path of the port.
26            
27             use AmbientOrb::Serial;
28            
29             my $orb = AmbientOrb::Serial->new( { port_name => COM1 } );
30             $orb->connect() or die "unable to connect to orb!";
31             $orb->color( ORB_RED ); #turn it red
32             $orb->pulse( ORB_RED, ORB_SLOW ); #pulse it slow
33             $orb->pulse( ORB_GREEN, ORB_FAST ); #pulse it fast
34             ...
35            
36             =cut
37            
38             =head1 EXPORT
39            
40             By default the constants for colors and animations are exported.
41            
42             Constants are exported for the different colors and animations. Note that I'm mucking around directly with the symbol
43             table and exporting these constants to main::ORB_RED, for example. I know. I'm bad. I'm sorry.
44            
45             For example:
46            
47             use AmbientOrb::Serial;
48             print ORB_RED; #prints 'RED'
49            
50             =cut
51            
52            
53            
54            
55             =head1 AUTHOR
56            
57             Lyle Hayhurst, C<< >>
58            
59             =head1 BUGS
60            
61             Please report any bugs or feature requests to
62             C, or through the web interface at
63             L.
64             I will be notified, and then you'll automatically be notified of progress on
65             your bug as I make changes.
66            
67             =head1 TODO
68            
69             =over 4
70            
71             =item * Need to add support for manual setting of RGB
72            
73             =item * Need to add support for getting orb diagnostics.
74            
75             =item * Probably need to have the thing pull out of serial mode when the port is disconnected.
76            
77             =item * And further on, create AmbientOrb::Web that supports the same feature set, except via the web interface.
78            
79             =back
80            
81             =head1 SUPPORT
82            
83             You can find documentation for this module with the perldoc command.
84            
85             perldoc AmbientOrb::Serial
86            
87             You can also look for information at:
88            
89             =over 4
90            
91             =item * AnnoCPAN: Annotated CPAN documentation
92            
93             L
94            
95             =item * CPAN Ratings
96            
97             L
98            
99             =item * RT: CPAN's request tracker
100            
101             L
102            
103             =item * Search CPAN
104            
105             L
106            
107             =back
108            
109             =head1 ACKNOWLEDGEMENTS
110            
111             =head1 COPYRIGHT & LICENSE
112            
113             Copyright 2006 Lyle Hayhurst, all rights reserved.
114            
115             This program is free software; you can redistribute it and/or modify it
116             under the same terms as Perl itself.
117            
118             =cut
119            
120 1     1   20075 use warnings;
  1         2  
  1         36  
121 1     1   1276 use integer;
  1         12  
  1         7  
122 1     1   30 use Carp;
  1         8  
  1         112  
123 1     1   5 use vars qw(%color_map %animation_map $OS_win);
  1         2  
  1         347  
124            
125             sub export_constants
126             {
127 0     0     my ( $hash, $caller ) = @_;
128 0           foreach my $name ( keys %$hash )
129             {
130 0           my $value = $hash->{$name};
131 0     0     *{$name} = sub () { $value };
  0            
  0            
132 0           push @{$caller.'::EXPORT'}, $name;
  0            
133             }
134             }
135            
136             BEGIN {
137            
138             #determine the operating system
139 1 50   1   7 $OS_win = ($^O eq "MSWin32") ? 1 : 0;
140 1 50       12 if ($OS_win) {
141 0         0 eval "use Win32::SerialPort qw( :STAT 0.19 )";
142 0 0       0 die "$@\n" if ($@);
143             }
144             else {
145 1     1   75 eval "use Device::SerialPort";
  1         476  
  0            
  0            
146 1 50       73 die "$@\n" if ($@);
147             }
148            
149             #build the color and animation maps
150 0         0 %color_map = ( ORB_RED => 0,
151             ORB_ORANGE => 3,
152             ORB_YELLOW => 6,
153             ORB_GREEN => 12,
154             ORB_AQUA => 16,
155             ORB_CYAN => 18,
156             ORB_BLUE => 24,
157             ORB_VIOLET => 27,
158             ORB_PURPLE => 28,
159             ORB_MAGENTA => 30,
160             ORB_WHITE => 36 );
161            
162 0         0 %animation_map = ( ORB_NONE => 0,
163             ORB_VERY_SLOW => 1,
164             ORB_SLOW => 2,
165             ORB_MEDIUM_SLOW => 3,
166             ORB_MEDIUM => 4,
167             ORB_MEDIUM_FAST => 5,
168             ORB_FAST => 6,
169             ORB_VERY_FAST => 7,
170             ORB_CRESCENDO => 8,
171             ORB_HEARTBEAT => 9 );
172            
173 0         0 export_constants( \%color_map, caller );
174 0         0 export_constants( \%animation_map, caller );
175             }
176            
177             use strict;
178             use base qw(Class::Accessor);
179             AmbientOrb::Serial->mk_accessors( qw/serial_port port_name/ );
180            
181             =head1 FUNCTIONS
182            
183             #public methods
184            
185             =head2 connect
186             The connect method will attempt to establish a serial port connection with the orb.
187            
188             Note that, as per the spec, the first thing it does is transmit a GT message to the orb.
189             This will tell it to ignore wireless input and use the serial port input instead.
190            
191             If all goes well, it returns a 1, else a 0.
192            
193             =cut
194            
195             sub connect {
196             my ( $self ) = @_;
197             my $port = create_serial_port( $self->port_name );
198             $self->serial_port( $port );
199            
200             #tell the orb to ignore the pager data
201             my $result = $self->send( pack("a3", "~GT" ) );
202             if ( not $result =~ "G+" ) {
203             return 0;
204             }
205             return 1;
206             }
207            
208             =head2 color
209             The color method instructs the orb to change its color.
210            
211             It takes a single argument -- the color to turn it.
212            
213             I'm actually lying here -- it can take an optional third argument, the pulse frequency.
214             But if you want to pulse the orb you might as well use the pulse() function, if only
215             for code readability.
216            
217             =cut
218            
219             sub color
220             {
221             my ( $self, $color, $anim ) = @_;
222             $anim ||= 0;
223            
224             my $message = $self->color_to_ascii( $color, $anim );
225            
226             my $result = $self->send( $message );
227             if ( not $result =~ "A+" )
228             {
229             return 0;
230             }
231             return 1;
232            
233             }
234            
235             =head2 pulse
236             The pulse method instructs the orb to change its color and pulse.
237            
238             It takes a two arguments -- the color to turn to, and the pulse frequency.
239            
240             =cut
241            
242             sub pulse
243             {
244             my ( $self, $color, $anim ) = @_;
245             return $self->color( $color, $anim );
246             }
247            
248             #private methods
249            
250             sub create_serial_port
251             {
252             my ( $port_name ) = @_;
253             my $serial_port;
254            
255             if ( $OS_win )
256             {
257             $serial_port = Win32::SerialPort->new( $port_name );
258             }
259             else
260             {
261             $serial_port = Device::SerialPort->new( $port_name );
262             }
263            
264             croak "unable to connect to serial port $port_name: $^E"
265             unless $serial_port;
266            
267             #as per the specification
268             $serial_port->baudrate(19200);
269             $serial_port->databits(8);
270             $serial_port->stopbits(1);
271             $serial_port->parity("none");
272             $serial_port->handshake("none");
273             return $serial_port;
274             }
275            
276            
277            
278             sub send {
279             my ( $self, $message ) = @_;
280             $self->serial_port->write( $message );
281             my $result;
282            
283             #the docs say that you have to poll a lot to get the
284             #correct result back. there is no doubt a better way
285             #to do this, but 1000 seems to be a nice magic number
286             for ( 1 .. 1000 )
287             {
288             $result = $self->serial_port->input;
289             if ( $result =~ /\w+/ )
290             {
291             last;
292             }
293             }
294             return $result;
295             }
296            
297             sub color_to_ascii
298             {
299             my ( $self, $color, $anim ) = @_;
300            
301             my $colorval = $color_map{$color};
302            
303             croak "unknown color $colorval!" unless defined $colorval;
304            
305             $anim = $animation_map{$anim} if defined $anim;
306             $anim ||= 0;
307            
308             my $firstByte = ( ($colorval + ( 37 * $anim)) / 94 ) + 32;
309             my $secondByte = ( ($colorval + ( 37 * $anim)) % 94 ) + 32 ;
310            
311             $secondByte = sprintf("%c", $secondByte);
312             $firstByte = sprintf("%c", $firstByte );
313             my $packme = "~A" . $firstByte . $secondByte;
314             my $message = pack("a4", $packme);
315            
316             return $message;
317             }
318            
319             sub DESTROY
320             {
321             my ( $self ) = @_;
322             if ( defined $self->serial_port )
323             {
324             $self->serial_port->close() || warn "unable to close serial port!\n";
325             undef $self->serial_port;
326             }
327             }
328            
329             1; # End of AmbientOrb::Serial