File Coverage

blib/lib/Device/MindWave.pm
Criterion Covered Total %
statement 132 147 89.8
branch 43 48 89.5
condition 6 6 100.0
subroutine 19 19 100.0
pod 8 8 100.0
total 208 228 91.2


line stmt bran cond sub pod time code
1             package Device::MindWave;
2              
3 4     4   91863 use strict;
  4         10  
  4         140  
4 4     4   21 use warnings;
  4         14  
  4         106  
5              
6 4     4   5292 use Device::SerialPort;
  4         152732  
  4         292  
7 4         232 use Device::MindWave::Utils qw(checksum
8 4     4   2774 packet_isa);
  4         40  
9 4     4   2240 use Device::MindWave::Packet::Parser;
  4         11  
  4         5887  
10              
11             our $VERSION = '0.02';
12             our $_NO_SLEEP = 0;
13              
14             sub new
15             {
16 4     4 1 40 my $class = shift;
17 4         16 my %args = @_;
18              
19 4         9 my $port;
20 4 100       19 if (exists $args{'fh'}) {
    50          
21 3         10 $port = $args{'fh'};
22             } elsif (exists $args{'port'}) {
23 0         0 $port = Device::SerialPort->new($args{'port'});
24 0 0       0 if (not $port) {
25 0         0 die "Cannot open ".($args{'port'}).": $!";
26             }
27 0         0 $port->baudrate(115200);
28 0         0 $port->user_msg(0);
29 0         0 $port->parity("even");
30 0         0 $port->databits(8);
31 0         0 $port->stopbits(1);
32 0         0 $port->handshake("none");
33 0         0 $port->read_const_time(1000);
34 0         0 $port->read_char_time(5);
35 0         0 $port->write_settings();
36             } else {
37 1         11 die "Either 'fh' or 'port' must be provided.";
38             }
39              
40 3         35 my $self = { port => $port,
41             is_fh => (exists $args{'fh'}),
42             parser => Device::MindWave::Packet::Parser->new() };
43 3         7 bless $self, $class;
44 3         11 return $self;
45             }
46              
47             sub _sleep
48             {
49 64 100   64   132 if ($_NO_SLEEP) {
50 61         183 return 1;
51             }
52 3         3000933 sleep($_[0]);
53 3         57 return 1;
54             }
55              
56             sub _read
57             {
58 2460     2460   3032 my ($self, $len) = @_;
59              
60 2460         2494 my $buf;
61             my $bytes;
62 2460 50       6337 if ($self->{'is_fh'}) {
63 2460         6367 $bytes = $self->{'port'}->read($buf, $len);
64             } else {
65 0         0 $buf = $self->{'port'}->read($len);
66 0         0 $bytes = length $buf;
67             }
68              
69 2460 100       5340 if ($len != (length $buf)) {
70 1         13 die "Received too few characters on read ($bytes instead of $len).";
71             }
72              
73 2459         5224 return $buf;
74             }
75              
76             sub _write
77             {
78 16     16   31 my ($self, $data) = @_;
79              
80 16 50       51 if ($self->{'is_fh'}) {
81 16         85 $self->{'port'}->write($data, (length $data), 0);
82             } else {
83 0         0 $self->{'port'}->write($data);
84             }
85              
86 16         36 return 1;
87             }
88              
89             sub _write_bytes
90             {
91 16     16   27 my ($self, $bytes) = @_;
92              
93 16         30 my $data = join '', map { chr($_) } @{$bytes};
  28         275  
  16         41  
94 16         64 return $self->_write($data);
95             }
96              
97             sub _to_headset_id_bytes
98             {
99 6     6   18 my ($upper, $lower) = @_;
100              
101 6 100       22 if ($upper > 255) {
102 5         7 $lower = $upper & 0xFF;
103 5         10 $upper = ($upper >> 8) & 0xFF;
104             }
105              
106 6         14 return ($upper, $lower);
107             }
108              
109             sub _wait_for_standby
110             {
111 11     11   16 my ($self) = @_;
112              
113 11         21 my $tries = 15;
114 11         38 while ($tries--) {
115 25         62 my $packet = $self->read_packet();
116 25 100       137 if (packet_isa($packet, 'Dongle::StandbyMode')) {
117 10         25 return 1;
118             }
119 15         40 _sleep(1);
120             }
121              
122 1         20 die "Timed out waiting for standby packet (15s).";
123             }
124              
125             sub connect_nb
126             {
127 6     6 1 13 my ($self, $upper, $lower) = @_;
128              
129 6         18 ($upper, $lower) = _to_headset_id_bytes($upper, $lower);
130 6         24 $self->_write_bytes([ 0xC0, $upper, $lower ]);
131              
132 6         13 return 1;
133             }
134              
135             sub connect
136             {
137 7     7 1 48 my ($self, @args) = @_;
138              
139 7         26 $self->_wait_for_standby();
140 6         24 $self->connect_nb(@args);
141              
142 6         8 my $tries = 15;
143 6         17 while ($tries--) {
144 22         55 my $packet = $self->read_packet();
145 22 100       80 if (packet_isa($packet, 'Dongle::HeadsetFound')) {
    100          
    100          
146 3         20 return 1;
147             } elsif (packet_isa($packet, 'Dongle::HeadsetNotFound')) {
148 1         12 die "Headset not found.";
149             } elsif (packet_isa($packet, 'Dongle::RequestDenied')) {
150 1         8 die "Request denied by dongle.";
151             }
152 17         48 _sleep(1);
153             }
154              
155 1         9 die "Unable to connect to headset.";
156             }
157              
158             sub auto_connect_nb
159             {
160 4     4 1 6 my ($self) = @_;
161              
162 4         13 $self->_write_bytes([ 0xC2 ]);
163              
164 4         9 return 1;
165             }
166              
167             sub auto_connect
168             {
169 4     4 1 19 my ($self) = @_;
170              
171 4         13 $self->_wait_for_standby();
172 4         14 $self->auto_connect_nb();
173              
174 4         6 my $tries = 15;
175 4         10 while ($tries--) {
176 19         39 my $packet = $self->read_packet();
177 19 100       75 if (packet_isa($packet, 'Dongle::HeadsetFound')) {
    100          
    100          
178 1         8 return 1;
179             } elsif (packet_isa($packet, 'Dongle::HeadsetNotFound')) {
180 1         10 die "No headset was found.";
181             } elsif (packet_isa($packet, 'Dongle::RequestDenied')) {
182 1         10 die "Request denied by dongle.";
183             }
184 16         47 _sleep(1);
185             }
186              
187 1         20 die "Unable to connect to any headset.";
188             }
189              
190             sub disconnect_nb
191             {
192 6     6 1 11 my ($self) = @_;
193              
194 6         30 $self->_write_bytes([ 0xC1 ]);
195              
196 6         14 return 1;
197             }
198              
199             sub disconnect
200             {
201 6     6 1 29 my ($self) = @_;
202              
203 6         23 $self->disconnect_nb();
204              
205 6         9 my $tries = 15;
206 6         13 my $got_error = 0;
207 6         26 while ($tries--) {
208             # Allow one error during packet read, since there will
209             # occasionally be a packet length mismatch problem.
210 22         27 my $packet = eval { $self->read_packet() };
  22         50  
211 22 100       81 if (my $error = $@) {
212 2 100       8 if ($got_error == 1) {
213 1         7 die $error;
214             } else {
215 1         2 $got_error = 1;
216             }
217             }
218             # Flush the remaining ThinkGear packets.
219 21 100       61 if (packet_isa($packet, 'ThinkGear')) {
220 1         2 $tries++;
221 1         23 next;
222             }
223 20 100 100     61 if (packet_isa($packet, 'Dongle::HeadsetDisconnected')
    100          
224             or packet_isa($packet, 'Dongle::StandbyMode')) {
225             # Occasionally, no HeadsetDisconnected packet will be
226             # returned, hence the check for standby mode.
227 3         116 return 1;
228             } elsif (packet_isa($packet, 'Dongle::RequestDenied')) {
229 1         1282 die "Request denied by dongle.";
230             }
231 16         48 _sleep(1);
232             }
233              
234 1         19 die "Unable to disconnect from headset.";
235             }
236              
237             sub read_packet
238             {
239 95     95 1 432 my ($self) = @_;
240              
241 95         100 my $tries = 1001;
242 95         109 my $prev_byte = 0;
243 95         207 while (--$tries) {
244 2188         2451 my $length = 0;
245 2188         5476 my $byte = $self->_read(1);
246 2187 100 100     6149 if (((ord $prev_byte) == 0xAA) and ((ord $byte) == 0xAA)) {
247 92         136 last;
248             } else {
249 2095         4561 $prev_byte = $byte;
250             }
251             }
252              
253 94 100       179 if ($tries == 0) {
254 2         37 die "Unable to find synchronisation bytes (read 1000 bytes).";
255             }
256              
257 92         174 my $len = ord $self->_read(1);
258 92 100       274 if ($len > 169) {
259 2         16 die "Length byte has invalid value ($len): expected 0-169.";
260             }
261              
262 90         174 my $data = $self->_read($len);
263 90         281 my @bytes = map { ord $_ } split //, $data;
  285         502  
264              
265 90         247 my $checksum = ord $self->_read(1);
266 90         479 my $checksum_actual = checksum(\@bytes);
267              
268 90 100       196 if ($checksum != $checksum_actual) {
269 1         6 goto &read_packet;
270             }
271              
272 89         418 return $self->{'parser'}->parse(\@bytes);
273             }
274              
275             1;
276              
277             __END__