/[RFID]/3m-810.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /3m-810.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (show annotations)
Fri Oct 3 12:31:58 2008 UTC (15 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 10458 byte(s)
decode read response
1 #!/usr/bin/perl
2
3 use Device::SerialPort qw (:STAT);
4 use strict;
5 use warnings;
6
7 use Data::Dump qw/dump/;
8 use Carp qw/confess/;
9
10 my $debug = 0;
11
12 my $response = {
13 'd500090400110a0500027250' => 'version?',
14 'd60007fe00000500c97b' => 'no tag in range',
15
16 'd6000ffe00000501e00401003123aa26941a' => 'tag #1',
17 'd6000ffe00000501e0040100017c0c388e2b' => 'rfid card',
18 'd6000ffe00000501e00401003123aa2875d4' => 'tag red-stripe',
19
20 'd60017fe00000502e00401003123aa26e0040100017c0c38cadb' => 'tag #1 + card',
21 'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',
22 };
23
24 =head1 NAME
25
26 3m-810 - support for 3M 810 RFID reader
27
28 =head1 SYNOPSIS
29
30 3m-810.pl [DEVICE [BAUD [DATA [PARITY [STOP [FLOW]]]]]]
31
32 =head1 DESCRIPTION
33
34 Communicate with 3M 810 RFID reader and document it's protocol
35
36 =head1 SEE ALSO
37
38 L<Device::SerialPort(3)>
39
40 L<perl(1)>
41
42 L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
43
44 =head1 AUTHOR
45
46 Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
47
48 =head1 COPYRIGHT AND LICENSE
49
50 This program is free software; you may redistribute it and/or modify
51 it under the same terms ans Perl itself.
52
53 =cut
54
55 # your serial port.
56 my ($device,$baudrate,$databits,$parity,$stopbits,$handshake)=@ARGV;
57 $device ||= "/dev/ttyUSB0";
58 $baudrate ||= "19200";
59 $databits ||= "8";
60 $parity ||= "none";
61 $stopbits ||= "1";
62 $handshake ||= "none";
63
64 my $port=new Device::SerialPort($device) || die "new($device): $!\n";
65 $handshake=$port->handshake($handshake);
66 $baudrate=$port->baudrate($baudrate);
67 $databits=$port->databits($databits);
68 $parity=$port->parity($parity);
69 $stopbits=$port->stopbits($stopbits);
70
71 print "## using $device $baudrate $databits $parity $stopbits\n";
72
73 # Just in case: reset our timing and buffers
74 $port->lookclear();
75 $port->read_const_time(100);
76 $port->read_char_time(5);
77
78 # Turn on parity checking:
79 #$port->stty_inpck(1);
80 #$port->stty_istrip(1);
81
82 # initial hand-shake with device
83
84 cmd( 'D5 00 05 04 00 11 8C66', 'hw version?',
85 'D5 00 09 04 00 11 0A 05 00 02 7250', 'hw 10.5.0.2', sub {
86 print "hardware version ", join('.', unpack('CCCC', skip_assert(3) )), "\n";
87 });
88
89 cmd( 'D6 00 0C 13 04 01 00 02 00 03 00 04 00 AAF2','stats?',
90 'D6 00 0C 13 00 02 01 01 03 02 02 03 00 E778','FIXME: unimplemented', sub { assert() } );
91
92 # start scanning for tags
93
94 cmd( 'D6 00 05 FE 00 05 FA40', "XXX scan $_",
95 'D6 00 07 FE 00 00 05 00 C97B', 'no tag', sub {
96 dispatch(
97 'D6 00 0F FE 00 00 05 ',# 01 E00401003123AA26 941A # seen, serial length: 8
98 sub {
99 my $rest = shift || die "no rest?";
100 my $nr = ord( substr( $rest, 0, 1 ) );
101 my $tags = substr( $rest, 1 );
102
103 my $tl = length( $tags );
104 die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
105
106 my @tags;
107 push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
108 warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
109 print "seen $nr tags: ", join(',', @tags ) , "\n";
110
111 # XXX read first tag
112 read_tag( @tags );
113
114 }
115 ) }
116
117 ) foreach ( 1 .. 100 );
118
119 my $read_cached;
120
121 sub read_tag {
122 my ( $tag ) = @_;
123
124 print "read_tag $tag\n";
125 return if $read_cached->{ $tag }++;
126
127 cmd( "D6 00 0D 02 $tag 00 03 1CC4", 'read $tag offset: 0 blocks: 3',
128 "D6 00 0F FE 00 00 05 01 $tag 941A", "$tag ready?", sub {
129 dispatch( "D6 00 1F 02 00 $tag ", sub { # 03 00 00 04 11 00 01 01 00 31 32 33 34 02 00 35 36 37 38 531F\n";
130 my $rest = shift || die "no rest?";
131 warn "## DATA ", dump( $rest ) if $debug;
132 my $blocks = ord(substr($rest,0,1));
133 my @data;
134 foreach my $nr ( 0 .. $blocks - 1 ) {
135 my $block = substr( $rest, 1 + $nr * 6, 6 );
136 warn "## block ",as_hex( $block ) if $debug;
137 my $ord = unpack('v',substr( $block, 0, 2 ));
138 die "got block $ord, expected block $nr from ",dump( $block ) if $ord != $nr;
139 my $data = substr( $block, 2 );
140 die "data payload should be 4 bytes" if length($data) != 4;
141 warn sprintf "## tag %08s %02d %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
142 $data[ $ord ] = $data;
143 }
144 $read_cached->{ $tag } = join('', @data);
145 print "DATA $tag ",dump( $read_cached->{ $tag } ), "\n";
146 })
147 });
148
149 # D6 00 1F 02 00 $tag 03 00 00 04 11 00 01 01 00 30 30 30 30 02 00 30 30 30 30 E5F4
150 if (0) {
151 cmd( "D6 00 0D 02 $tag 03 04 3970", 'read offset: 3 blocks: 4' );
152
153 # D6 00 25 02 00 $tag 04 03 00 30 30 00 00 04 00 00 00 00 00
154 # $tag 05 00 00 00 00 00 06 00 00 00 00 00 B9BA
155 warn "?? D6 00 25 02 00 $tag 04 03 00 39 30 31 32 04 00 ....\n";
156 }
157 warn "?? D6 00 0F FE 00 00 05 01 $tag 941A ##### ready?\n";
158
159 }
160
161 for ( 1 .. 3 ) {
162
163 # ++-->type 00-0a
164 # D6 00 2A 04 E00401003123AA26 00 07 00 04 11 00 01 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1C D4
165 # D6 00 2A 04 E0 04 01 00 31 23 AA 26 00 07 00 04 11 00 06 32 32 32 32 32 32 32 32 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 32B7
166 # D6 00 2A 04 E0 04 01 00 31 23 AA 26 00 07 00 04 11 00 02 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 00 00 00 00 00 00 00 00 42 1F
167
168 cmd(' D6 00 2A 04 E00401003123AA26 00 07 00 04 11 00 01 30 30 30 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8843', "write offset 0, block: 7 -- 0000000000 $_" );
169 warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
170
171 }
172 warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
173
174 cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
175
176 cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
177 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
178 cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
179 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
180
181 cmd('D6 00 26 04 E00401003123AA26 00 06 00 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 A98B', 'blank offset: 0 blocks: 6',
182 'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
183
184 undef $port;
185 print "Port closed\n";
186
187 sub writechunk
188 {
189 my $str=shift;
190 my $count = $port->write($str);
191 print "#> ", as_hex( $str ), "\t[$count]" if $debug;
192 }
193
194 sub as_hex {
195 my @out;
196 foreach my $str ( @_ ) {
197 my $hex = unpack( 'H*', $str );
198 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
199 $hex =~ s/\s+$//;
200 push @out, $hex;
201 }
202 return join(' | ', @out);
203 }
204
205 sub read_bytes {
206 my ( $len, $desc ) = @_;
207 my $data = '';
208 while ( length( $data ) < $len ) {
209 my ( $c, $b ) = $port->read(1);
210 #warn "## got $c bytes: ", as_hex($b), "\n";
211 $data .= $b;
212 }
213 $desc ||= '?';
214 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
215 return $data;
216 }
217
218 our $assert;
219
220 # my $rest = skip_assert( 3 );
221 sub skip_assert {
222 assert( 0, shift );
223 }
224
225 sub assert {
226 my ( $from, $to ) = @_;
227
228 $from ||= 0;
229 $to = length( $assert->{expect} ) if ! defined $to;
230
231 my $p = substr( $assert->{payload}, $from, $to );
232 my $e = substr( $assert->{expect}, $from, $to );
233 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
234
235 # return the rest
236 return substr( $assert->{payload}, $to );
237 }
238
239 our $dispatch;
240 sub dispatch {
241 my ( $pattern, $coderef ) = @_;
242 my $patt = substr( str2bytes($pattern), 3 ); # just payload
243 my $l = length($patt);
244 my $p = substr( $assert->{payload}, 0, $l );
245 warn "## dispatch pattern $pattern [$l] ",dump( $patt, $p ) if $debug;
246
247 if ( $assert->{payload} eq $assert->{expect} ) {
248 warn "## no dispatch, payload expected" if $debug;
249 } elsif ( $p eq $patt ) {
250 # if matched call with rest of payload
251 $coderef->( substr( $assert->{payload}, $l ) );
252 } else {
253 warn "## dispatch ignored" if $debug;
254 }
255 }
256
257 use Digest::CRC;
258
259 sub crcccitt {
260 my $bytes = shift;
261 my $crc = Digest::CRC->new(
262 # midified CCITT to xor with 0xffff instead of 0x0000
263 width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
264 ) or die $!;
265 $crc->add( $bytes );
266 pack('n', $crc->digest);
267 }
268
269 # my $checksum = checksum( $bytes );
270 # my $checksum = checksum( $bytes, $original_checksum );
271 sub checksum {
272 my ( $bytes, $checksum ) = @_;
273
274 my $xor = crcccitt( substr($bytes,1) ); # skip D6
275 warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
276
277 my $len = ord(substr($bytes,2,1));
278 my $len_real = length($bytes) - 1;
279
280 if ( $len_real != $len ) {
281 print "length wrong: $len_real != $len\n";
282 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,4);
283 }
284
285 if ( defined $checksum && $xor ne $checksum ) {
286 print "checksum doesn't match: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n";
287 return $bytes . $xor;
288 }
289 return $bytes . $checksum;
290 }
291
292 sub readchunk {
293 my ( $parser ) = @_;
294
295 sleep 1; # FIXME remove
296
297 # read header of packet
298 my $header = read_bytes( 2, 'header' );
299 my $length = read_bytes( 1, 'length' );
300 my $len = ord($length);
301 my $data = read_bytes( $len, 'data' );
302
303 my $payload = substr( $data, 0, -2 );
304 my $payload_len = length($data);
305 warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
306
307 my $checksum = substr( $data, -2, 2 );
308 checksum( $header . $length . $payload, $checksum );
309
310 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n";
311
312 $assert->{len} = $len;
313 $assert->{payload} = $payload;
314
315 $parser->( $len, $payload ) if $parser && ref($parser) eq 'CODE';
316
317 return $data;
318 }
319
320 sub str2bytes {
321 my $str = shift || confess "no str?";
322 my $b = $str;
323 $b =~ s/\s+//g;
324 $b =~ s/(..)/\\x$1/g;
325 $b = "\"$b\"";
326 my $bytes = eval $b;
327 die $@ if $@;
328 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
329 return $bytes;
330 }
331
332 sub cmd {
333 my ( $cmd, $cmd_desc, $expect, $expect_desc, $coderef ) = @_;
334 my $bytes = str2bytes( $cmd );
335
336 # fix checksum if needed
337 $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
338
339 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n";
340 $assert->{send} = $cmd;
341 writechunk( $bytes );
342
343 if ( $expect ) {
344 warn "?? $expect", $expect_desc ? "\t## $expect_desc" : '', "\n";
345 $assert->{expect} = substr(str2bytes($expect), 3, -2); # just expected payload
346 readchunk( $coderef );
347 }
348 }
349

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26