usb-uirt-config.pl

#!/usr/bin/perl
#
# Copyright (C) 2007 Kenneth L. Root. <https://the-b.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
use strict;
use warnings;
use Device::SerialPort;
use Getopt::Long;
use Pod::Usage;
use constant TIMEOUT_DEFAULT => 10;
use constant TRANSMITTING => 0x20;
use constant CMDOK => 0x21;
use constant CSERROR => 0x80;
use constant TOERROR => 0x81;
use constant CMDERROR => 0x82;
use constant ACTIONS => ['Pulse pin (positive)',
'Set pin',
'Clear pin',
'Pulse pin (negative)',
];
my %config = (
'port' => '/dev/ttyUSB0',
'slot' => 0
);
my $port;
my $slots = -1;
sub openPort {
my($portName) = @_;
my $port = new Device::SerialPort($portName)
|| die "Can't open $portName: $!
";
$port->read_char_time(0);
$port->read_const_time(1000);
return $port;
}
sub checksum {
my(@bytes) = @_;
my $cksum = 0;
map { $cksum += $_ } @bytes;
return 0x100 - ($cksum & 0xFF);
}
sub printCommand {
my ($prefix, @cmd) = @_;
print "$prefix: ";
map { printf "0x%02x ", $_ } @cmd;
print "
";
}
sub sendCommand {
my @cmd = @_;
push @cmd, checksum(@cmd);
printCommand("send", @cmd) if $config{'debug'};
$port->write(pack('C*', @cmd));
}
sub readCommand {
my($numBytes) = @_;
my $timeout = TIMEOUT_DEFAULT;
my $buffer;
my $chars = 0;
while ($timeout > 0) {
my ($count, $saw) = $port->read(255);
if ($count > 0) {
$chars += $count;
$buffer .= $saw;
if ($chars >= $numBytes) {
my @cmd = map { ord } unpack("(Z)*", $buffer);
printCommand("recv", @cmd) if $config{'debug'};
return @cmd;
}
} else {
$timeout--;
}
}
if ($timeout == 0) {
die "Couldn't communicate with USB-UIRT device after ". TIMEOUT_DEFAULT ." seconds.
";
}
}
sub getVersion {
my $printing = shift;
sendCommand(0x23);
my @res = readCommand(7);
return if not $printing;
print "Firmware information:
";
printf " Firmware version = %d.%d
", $res[1], $res[0];
printf " Protocol compatability version = %d.%d
", $res[3], $res[2];
# Y3K bug
printf " Firmware date (DMY) = %02d/%02d/%02d
", $res[4], $res[5], $res[6] + 2000;
}
sub setUIRMode {
sendCommand(0x20);
my @res = readCommand(1);
die "Cannot set UIR mode" if ($res[0] != CMDOK);
}
sub getGpioCaps {
my $printing = shift;
sendCommand(0x30, 0x01);
my @res = readCommand(6);
die "Failed to get GPIO caps" if (checksum(@res[0..$#res-1]) != $res[$#res]);
$slots = $res[0];
return if not $printing;
print "GPIO capabilities:
";
printf " Number of slots = %d
", $slots;
foreach my $slot (0..$slots-1) {
printf " Port %c bitmask: %08b
", (ord("A") + $slot), $res[1+$slot];;
}
}
sub getGpioCfg {
my ($slot) = @_;
getGpioCaps if ($slots == -1);
die "No such programmable slot: $slot" if ($slot > $slots);
sendCommand(0x31, 0x02, $slot);
my @res = readCommand(9);
die "Failed to get GPIO config for slot $slot"
if (checksum(@res[0..$#res-1]) != $res[$#res]);
my $action = ($res[6] & 0xC0) >> 6;
my $port = ($res[6] & 0x18) >> 3;
my $pin = $res[6] & 0x03;
my $duration = $res[7] * 5;
print "GPIO configuration for slot $slot
";
printCommand(" IR Code", @res[0..5]);
print " Pin action: " . ACTIONS->[$action] . "
";
print " Port: ". chr(ord("A") + $port) .".". $pin . "
";
print " Duration: ". $duration ." ms
";
}
sub setWakeUpCode {
my ($slot, @irCode) = @_;
# bit 7,6: 11 (Pulse Pin (negative))
# bit 5: reserved
# bit 4,3: 00 (Port A)
# bit 2,1,0: 011 (Pin 3)
my $action = 0xC3;
my $duration = 100 / 5; # Duration is in 5mS increments.
sendCommand(0x32, 0x0A, $slot, @irCode, $action, $duration);
my @res = readCommand(1);
if ($res[0] == CMDOK) {
print "Successfully set Wake-Up code in slot $slot.
";
} else {
print "Error setting Wake-Up code.
";
}
}
sub getConfiguration {
sendCommand(0x38, 0x01);
my @res = readCommand(3);
print "Configuration:
";
print " LED blink on IR TX: " . ((($res[0] & 0x01) == 0x01) ? "yes" : "no") ."
";
print " LED blink on IR RX: " . ((($res[0] & 0x02) == 0x02) ? "yes" : "no") ."
";
}
sub compareCodes {
my ($first, $second) = @_;
no warnings;
return 0 unless @$first == @$second;
for (my $i = 0; $i < @$first; $i++) {
return 0 if ($first->[$i] ne $second->[$i]);
}
return 1;
}
GetOptions(%config,
'help|?',
'debug!',
'port=s',
'version!',
'get-configuration!',
'gpio-capabilities|gpiocaps|gpio-caps!',
'slot-configuration!',
'wakeup!',
'slot=i',
'get-code!',
'code=s',
);
if (exists $config{'help'}) {
pod2usage(1);
}
$port = openPort($config{'port'});
print "Opened port ". $config{'port'} ."
" if ($config{'debug'});
getVersion($config{'version'});
exit(0) if $config{'version'};
if (exists $config{'gpio-capabilities'}) {
getGpioCaps(1);
exit(0);
}
if (exists $config{'get-configuration'}) {
getConfiguration;
exit(0);
}
if (exists $config{'slot-configuration'}) {
getGpioCfg($config{'slot'});
exit(0);
}
if (exists $config{'get-code'}) {
setUIRMode;
print "Press the button on the remote you want to see the UIR code for...
";
my @res = readCommand(6);
printCommand("IR Code", @res);
exit(0);
}
if ($config{'wakeup'}) {
if (not exists $config{'code'}) {
setUIRMode;
print "Press key on remote that you want to be the Wake-Up key...
";
my @code1 = readCommand(6);
my $tries = 6;
my $identical = 0;
while ($tries-- > 0 && !$identical) {
print "Press key on remote that you want to be the Wake-Up key again...
";
my @code2 = readCommand(6);
$identical = compareCodes(@code1, @code2);
if (not $identical) {
@code1 = @code2;
print "Codes are not identical. Please press the button again...
";
}
}
if ($tries == 0) {
print "Couldn't get two identical codes! Exiting...
";
exit(1);
}
$config{'code'} = @code1;
}
setWakeUpCode($config{'slot'}, @{$config{'code'}});
getGpioCfg($config{'slot'});
exit(0);
}
$port->close
|| die "Failed to close ". $config{'port'} .": $!
";
print "No actions specified!
";
pod2usage(1);
exit(1);
__END__
=head1 NAME
usb-uirt-config - Configuration program for the USB-UIRT
=head1 SYNOPSIS
usb-uirt-config [options]
=head1 OPTIONS
=over 8
=item B<--port>
Serial port to which USB-UIRT is connected.
=item B<--debug>
Enable debugging.
=item B<--version>
Output USB-UIRT version.
=item B<--get-configuration>
Prints out the USB-UIRT's current configuration.
=item B<--gpio-capabilities>
Get GPIO capabilities of USB-UIRT.
=item B<--slot-configuration>
Get GPIO configuration for a single slot.
=item B<--get-code>
Reads and prints an incoming IR code.
=item B<--wakeup>
Configure wake-up on GPIO slot
=item B<--slot>
Specify GPIO slot to configure.
=item B<--code>
IR code to use (not yet implemented).
=back
=head1 DESCRIPTION
Configures the USB-UIRT device.
=cut

Copyright © Kenny Root. All rights reserved.