/[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 6 - (show annotations)
Sun Sep 28 18:19:37 2008 UTC (15 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 8559 byte(s)
better output
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 =head1 AUTHOR
43
44 Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
45
46 =head1 COPYRIGHT AND LICENSE
47
48 This program is free software; you may redistribute it and/or modify
49 it under the same terms ans Perl itself.
50
51 =cut
52
53 # your serial port.
54 my ($device,$baudrate,$databits,$parity,$stopbits,$handshake)=@ARGV;
55 $device ||= "/dev/ttyUSB0";
56 $baudrate ||= "19200";
57 $databits ||= "8";
58 $parity ||= "none";
59 $stopbits ||= "1";
60 $handshake ||= "none";
61
62 my $port=new Device::SerialPort($device) || die "new($device): $!\n";
63 $handshake=$port->handshake($handshake);
64 $baudrate=$port->baudrate($baudrate);
65 $databits=$port->databits($databits);
66 $parity=$port->parity($parity);
67 $stopbits=$port->stopbits($stopbits);
68
69 print "## using $device $baudrate $databits $parity $stopbits\n";
70
71 # Just in case: reset our timing and buffers
72 $port->lookclear();
73 $port->read_const_time(100);
74 $port->read_char_time(5);
75
76 # Turn on parity checking:
77 #$port->stty_inpck(1);
78 #$port->stty_istrip(1);
79
80 # initial hand-shake with device
81
82 cmd( 'D5 00 05 04 00 11 8C66', 'hw version?',
83 'D5 00 09 04 00 11 0A 05 00 02 7250', 'hw 10.5.0.2', sub {
84 print "hardware version ", join('.', unpack('CCCC', skip_assert(3) )), "\n";
85 });
86
87 cmd( 'D6 00 0C 13 04 01 00 02 00 03 00 04 00 AAF2','stats?',
88 'D6 00 0C 13 00 02 01 01 03 02 02 03 00 E778','FIXME: unimplemented', sub { assert() } );
89
90 # start scanning for tags
91
92 cmd( 'D6 00 05 FE 00 05 FA40', "XXX scan $_",
93 'D6 00 07 FE 00 00 05 00 C97B', 'no tag', sub {
94 dispatch(
95 'D6 00 0F FE 00 00 05 ',# 01 E00401003123AA26 941A # seen, serial length: 8
96 sub {
97 my $rest = shift || die "no rest?";
98 my $nr = ord( substr( $rest, 0, 1 ) );
99 my $tags = substr( $rest, 1 );
100
101 my $tl = length( $tags );
102 die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
103
104 my @tags;
105 push @tags, substr($tags, $_ * 8, 8) foreach ( 0 .. $nr - 1 );
106 warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags );
107 print "seen $nr tags: ", join(',', map { unpack('H16', $_) } @tags ) , "\n";
108 }
109 ) }
110
111 ) foreach ( 1 .. 100 );
112
113 cmd( 'D6 00 0D 02 E00401003123AA26 00 03 1CC4', 'read offset: 0 blocks: 3' );
114
115 # D6 00 1F 02 00 E00401003123AA26 03 00 00 04 11 00 01 01 00 30 30 30 30 02 00 30 30 30 30 E5F4
116 warn "D6 00 1F 02 00 E00401003123AA26 03 00 00 04 11 00 01 01 00 31 32 33 34 02 00 35 36 37 38 531F\n";
117
118 cmd( 'D6 00 0D 02 E00401003123AA26 03 04 3970', 'read offset: 3 blocks: 4' );
119
120 # D6 00 25 02 00 E00401003123AA26 04 03 00 30 30 00 00 04 00 00 00 00 00
121 # 05 00 00 00 00 00 06 00 00 00 00 00 B9BA
122 warn "D6 00 25 02 00 E00401003123AA26 04 03 00 39 30 31 32 04 00 33 34 35 36
123 05 00 00 00 00 00 06 00 00 00 00 00 524B\n";
124 warn "D6 00 0F FE 00 00 05 01 E00401003123AA26 941A ##### ready?\n";
125
126 for ( 1 .. 3 ) {
127
128 # ++-->type 00-0a
129 # 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
130 # 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
131 # 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
132
133 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 $_" );
134 warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
135
136 }
137 warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
138
139 cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
140
141 cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
142 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
143 cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
144 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
145
146 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',
147 'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
148
149 undef $port;
150 print "Port closed\n";
151
152 sub writechunk
153 {
154 my $str=shift;
155 my $count = $port->write($str);
156 print "#> ", as_hex( $str ), "\t[$count]\n";
157 }
158
159 sub as_hex {
160 my @out;
161 foreach my $str ( @_ ) {
162 my $hex = unpack( 'H*', $str );
163 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
164 push @out, $hex;
165 }
166 return join(' ', @out);
167 }
168
169 sub read_bytes {
170 my ( $len, $desc ) = @_;
171 my $data = '';
172 while ( length( $data ) < $len ) {
173 my ( $c, $b ) = $port->read(1);
174 #warn "## got $c bytes: ", as_hex($b), "\n";
175 $data .= $b;
176 }
177 $desc ||= '?';
178 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
179 return $data;
180 }
181
182 our $assert;
183
184 # my $rest = skip_assert( 3 );
185 sub skip_assert {
186 assert( 0, shift );
187 }
188
189 sub assert {
190 my ( $from, $to ) = @_;
191
192 $from ||= 0;
193 $to = length( $assert->{expect} ) if ! defined $to;
194
195 my $p = substr( $assert->{payload}, $from, $to );
196 my $e = substr( $assert->{expect}, $from, $to );
197 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
198
199 # return the rest
200 return substr( $assert->{payload}, $to );
201 }
202
203 our $dispatch;
204 sub dispatch {
205 my ( $pattern, $coderef ) = @_;
206 my $patt = substr( str2bytes($pattern), 3 ); # just payload
207 my $l = length($patt);
208 my $p = substr( $assert->{payload}, 0, $l );
209 warn "## dispatch pattern $pattern [$l] ",dump( $patt, $p ) if $debug;
210
211 if ( $assert->{payload} eq $assert->{expect} ) {
212 warn "## no dispatch, payload expected" if $debug;
213 } elsif ( $p eq $patt ) {
214 # if matched call with rest of payload
215 $coderef->( substr( $assert->{payload}, $l ) );
216 } else {
217 warn "## dispatch ignored" if $debug;
218 }
219 }
220
221 sub readchunk {
222 my ( $parser ) = @_;
223
224 sleep 1; # FIXME remove
225
226 # read header of packet
227 my $header = read_bytes( 2, 'header' );
228 my $length = read_bytes( 1, 'length' );
229 my $len = ord($length);
230 my $data = read_bytes( $len, 'data' );
231 my ( $cmd ) = unpack('C', $data );
232
233 my $payload = substr( $data, 0, -2 );
234 my $payload_len = length($data);
235 warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
236 my $checksum = substr( $data, -2, 2 );
237 # FIXME check checksum
238
239 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), "checksum: ", as_hex( $checksum ),"\n";
240
241 $assert->{len} = $len;
242 $assert->{payload} = $payload;
243 $assert->{checksum} = $checksum;
244
245 $parser->( $len, $payload, $checksum ) if $parser && ref($parser) eq 'CODE';
246
247 return $data;
248 }
249
250 sub str2bytes {
251 my $str = shift || confess "no str?";
252 my $b = $str;
253 $b =~ s/\s+(\S\S)(\S\S)+\s*/ $1 $2/; # fix checksum
254 $b =~ s/\s+$//;
255 $b =~ s/\s+/\\x/g;
256 $b = '"\x' . $b . '"';
257 my $bytes = eval $b;
258 die $@ if $@;
259 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
260 return $bytes;
261 }
262
263 sub cmd {
264 my ( $cmd, $cmd_desc, $expect, $expect_desc, $coderef ) = @_;
265 my $bytes = str2bytes( $cmd );
266
267 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n";
268 $assert->{send} = $cmd;
269 writechunk( $bytes );
270
271 if ( $expect ) {
272 warn "?? $expect", $expect_desc ? "\t## $expect_desc" : '', "\n";
273 $assert->{expect} = substr(str2bytes($expect), 3, -2); # just expected payload
274 readchunk( $coderef );
275 }
276 }
277

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26