#!/usr/bin/perl
#
# Read R3 historical data from Chaney Instruments acurite weather station 01036
# dump as hex string to stdout
#
# Andrew Daviel March 2015
#
# some code from U451.write.pl (USB Micro interface) by myself
# some code from acurite.py Matthew Wall
# some constants from /usr/include/usb.h
#
$VENDOR = 0x24c0 ; 
$PRODUCT = 0x0003 ;

use Device::USB;
my $usb = Device::USB->new();
my $dev = $usb->find_device( $VENDOR, $PRODUCT );
unless ($dev) { exit ; }
#&ptime ; printf "Device: %04X:%04X\n", $dev->idVendor(), $dev->idProduct();

$dev->open() or exit ;
# 01036 doesn't support these standard requests
#print "Manufactured by ", $dev->manufacturer(), " Product: ", $dev->product(), "\n";
#$serial = $dev->serial_number; 
#print "Serial $serial\n" ;

use Time::HiRes qw( gettimeofday usleep )  ;
my $cfg = $dev->config()->[0];
our $ifnum = 0 ;

my $ep = $cfg->interfaces->[0]->[0]->endpoints->[0]->bEndpointAddress ;

my $driver = $dev->get_driver_np($ifnum);
if ($driver) {
# Linux normally binds this kind of device as a human interface device
  warn "unbinding interface $ifnum from kernel driver \"$driver\"\n";
  if ($dev->detach_kernel_driver_np($ifnum) < 0) {
    print "# " ; $ptime ; print " unbinding FAILED\n";
    exit ;
  }
}
my $ret = $dev->claim_interface($ifnum);
if ($ret < 0) {
  print "# " ; $ptime ; print " claim_interface failed ($ret): $!\n";
  exit ;
}

#&resetep($ep) ; # don't seem to need this, just setup
#&clear_halt($ep) ;
&setup ; 
while (1) {
  &ptime ; $buffer = &read_R3 ;
  @data = unpack("C*",$buffer) ;
  foreach $d (@data) {
    printf ("%2.2x ",$d) ;
  }
  print "\n" ;
}

&clean ;

exit ;

sub clean {
  $dev->release_interface($ifnum);
}

sub read {
  my $msgtype = shift ;
  my $nbytes = shift ;
  my $reqtype = 0xa1 ; # USB_RECIP_INTERFACE + USB_TYPE_CLASS + USB_ENDPOINT_IN
  my $request = 1 ; # USB_REQ_CLEAR_FEATURE
  my $value = 0x0100 + $msgtype ;
  my $cmdlen = 1 ;
  my $timeout = 5000 ;
  my $buffer = 0 x 64; # pre-allocate buffer - libusb is not perl!
  my $ret = $dev->control_msg($reqtype,$request,$value,$ifnum,$buffer,$nbytes, $timeout );
  if ($ret < 0) {
    print "Read failed; $!\n" ;
    &clean ;
    exit ;
  }

  if ($ret != $nbytes) {
    print "Expected $nbytes bytes got $ret\n" ;
  }
  return $buffer ;
}

sub setup {
  my $reqtype = 0x21 ; # USB_RECIP_INTERFACE + USB_TYPE_CLASS + USB_ENDPOINT_OUT
  my $buffer = pack("C*", (0x01, 0xff, 0, 0, 0, 0, 0, 0 )) ;
  my $timeout = 1000 ;
  my $request = 9 ; # USB_REQ_SET_CONFIGURATION
  my $nbytes = 2 ;
  my $value = 0x201 ;
  my $ret = $dev->control_msg($reqtype,$request,$value,$ifnum,$buffer,$nbytes, $timeout ); 
  if ($ret < 0) {
    print "setup failed; $!\n" ;
    &clean ;
    exit ;
  }
}
sub resetep {
  my $ep = shift ;
  my $ret = $dev->resetep($ep) ;
  if ($ret < 0) {
    print "resetep failed; $!\n" ;
    return  ;
  }
}
sub clear_halt {
  my $ep = shift ;
  my $ret = $dev->clear_halt($ep) ;
  if ($ret < 0) {
    print "clear_halt failed; $!\n" ;
    return  ;
  }
}


sub read_R1 {
  return &read(1,10) ;
}
sub read_R2 {
  return &read(2,25) ;
}
sub read_R3 {
  return &read(3,33) ;
}

sub ptime {
# this was for matching records with Wireshark packets by time-of-day
  my  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  my ($t, $ut) = gettimeofday;
  #printf("%2.2d:%2.2d:%2.2d.%d ",$hour,$min,$sec,$ut)
  printf("%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d ",$year+1900,$mon+1,$mday,$hour,$min,$sec) ;
}
