Thank you, I am less worried about my sollution's safety now. The interface is (sortof) described at CashCode's website. PDF only. There are 4 manuals, each manages to describe part of the protocol. Some of them claim that the device supports 600 baud, others that it senses 1200 or 9600. My unit only works at 600. I don't have any specs on the "serial" to RS232 converter, as that would require spending an extra $500 on a "developers kit" from Happ Controls. I used the timings described in CashCode's MAN-CCS.pdf. Basically, they have a line labelled SEND, which they want me to play with when they throw their INTERUPT line. The interupt is tied to one of the handshake lines, but is irrelevant to me, as if SEND is in the correct state for the bill validator to be active and send a message, then I can just read the incoming data, and know by that when to respond, even if I turn off handshaking in my software. I used an RS232 breakout box to toggle lines until I found the SEND. I might not have found it correctly; I found I could also toggle one of the handshake lines from the validator side, and it would partially respond, but only with RD could I get it to give me the full documented behavior. And I couldn't think of another way to toggle it in software than tying it to RTS. (the other potential source, DTR, is the ACCEPT ENABLE line from the docs)
I agree it makes no sense, but with the following code the whole thing works. (without the hardware hack, you put a bill in and it just stays in ESCROW position)
use strict;
require 5.006;
use warnings;
use Device::SerialPort;
use Time::HiRes qw/ usleep /;
my $port = shift @ARGV;
$port ||= '/dev/validator' if -e '/dev/validator';
$port ||= '/dev/ttyS1' if -e '/dev/ttyS1';
$port ||= '/dev/ttyS0' if -e '/dev/ttyS0';
# validator commands
my ( %v_bills, %v_status );
$v_status{pack('H*','89')} = 'VEND';
$v_status{pack('H*','8a')} = 'RETURNED';
$v_status{pack('H*','8b')} = 'REJECT';
$v_status{pack('H*','8c')} = 'FAILURE';
$v_status{pack('H*','8d')} = 'STACKER FULL';
$v_status{pack('H*','8e')} = 'LRC REMOVED';
$v_status{pack('H*','8f')} = 'LRC ATTACHED';
$v_bills{pack('H*','81')} = 1; # $1 inserted
$v_bills{pack('H*','82')} = 2;
$v_bills{pack('H*','83')} = 5;
$v_bills{pack('H*','84')} = 10;
$v_bills{pack('H*','85')} = 20;
# what about model 1100, when it gets a $50 or $100? Not in the chart
+:(
$PortObj->baudrate( $baud );
$PortObj->parity("none");
$PortObj->databits(8);
$PortObj->stopbits(1);
$PortObj->datatype('raw');
$PortObj->write_settings();
print STDERR "$0 ($$) started at ",scalar localtime(), "\n";
# always send an ack to start, in case there is an event in the valida
+tor's queue, or a half processed event
ack();
my $last = time;
while (1) {
my ($count, $data) = $PortObj->read( 1024 );
if ( $count and defined $data ) {
foreach my $byte ( split //, $data ) {
if ( exists $v_bills{$byte} ) {
printf "saw a \$%s dollar bill\n", $v_bills{$byte};
if ( $enabled ) {
accept_bill();
}
else {
reject_bill();
}
}
elsif ( exists $v_status{$byte} ) {
printf "saw status event %s\n", $v_status{$byte};
ack();
}
else {
my $hex = unpack "H*", $byte;
my $bin = unpack( "B*", $byte );
$bin =~ s/(.{8})/$1 /g; # seperate the bytes please
printf "UNKNOWN: hex: %s bin: %s\n", $hex, $bin;
}
}
}
else {
pause( 50_000 ); # because the $PortObj->read is nonblocking, we d
+on't want to loop too tight. so pause 50ms if a read fails.
}
}
sub ack {
$PortObj->rts_active( 0 );
pause( $rts_timing );
$PortObj->rts_active( 1 );
}
sub accept_bill {
ack();
}
sub reject_bill {
$PortObj->rts_active( 0 );
$PortObj->dtr_active( 0 );
pause( $rts_timing );
ack();
}
sub pause {
my $time = shift || 500_000; # micro seconds
usleep( $time );
return 1;
}
Of course, the useful version does more than just print to the screen that money was inserted ;)
-- Snazzy tagline here
| [reply] [d/l] |