Joystick controlled servos under Windows 7 in perl

Instructions;

Plug in your XBox360 wireless remote controller (or some other joystick)

Connect your Mini Maestro to COM5 (or adjust COM5 in the code below)

Wiggling the joysticks will control servos 1, 2, and 3.

Press “Y” to start recording your wiggles.
Press “B” to stop recording
Press “A” to play it back
Press “X” to exit.

If anyone knows how to talk to the Maestro using DLLs (eg: USB instead of serial commands), PLEASE let me know!!!


#!/usr/bin/perl

use strict;
use Win32::API;
use Win32::SerialPort;
# use Time::HiRes;	# Do not use this - it crashes the serialport code

my $VERSION = 0.1;

=head1 NAME

XBox_Pololu.pl - demo controlling servos via joystick

=head1 Installing

	I'm using ActivePerl from ActiveState.

	Put this:-
	http://cpansearch.perl.org/src/BBIRTH/Win32-SerialPort-0.22/lib/Win32API/CommPort.pm
	into C:\Perl64\site\lib\Win32API

	And put this:-
	http://cpansearch.perl.org/src/BBIRTH/Win32-SerialPort-0.22/lib/Win32/SerialPort.pm
	into C:\Perl64\site\lib\Win3

=head1 SYNOPSIS

    XBox_Pololu.pl [joynumber]

=head1 DESCRIPTION

This B<XBox_Pololu.pl> demo wiggles pololu servos

=head1 OPTIONS

=over

=item B<none>

this demo has no options

=back

=head1 README

Needs Win32::API and Win32::SerialPort installed
Assumes COM5 for the servo controller

=head1 PREREQUISITES

This script requires C<Win32::API 0.20>.

=pod OSNAMES

MSWin32

=pod SCRIPT CATEGORIES

Win32

=head1 AUTHOR

Written by Chris Drake.

=head1 COPYRIGHT

Copyright © 2011 The Chris Drake Team.

This is Free Software; this software is licensed under the GPL version 2, as published by the Free Software Foundation.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

=cut


# Set up the servo controller
my $port = Win32::SerialPort->new("COM5") || die $!;

$port->baudrate(9600);
$port->parity("none");
$port->handshake("none");
$port->databits(8);
$port->stopbits(1);
$port->read_char_time(0);
$port->read_const_time(1);


# Define the structure we need to get all this info (might already be defined in Win32::API - I didn't look)
  typedef Win32::API::Struct JOYINFOEX => (
    'LONG', 'dwSize',		# size of structure        
    'LONG', 'dwFlags',		# flags to indicate what to return        
    'LONG', 'dwXpos',		# x position        
    'LONG', 'dwYpos',		# y position        
    'LONG', 'dwZpos',		# z position        
    'LONG', 'dwRpos',		# rudder/4th axis position        
    'LONG', 'dwUpos',		# 5th axis position        
    'LONG', 'dwVpos',		# 6th axis position        
    'LONG', 'dwButtons',	# button states        
    'LONG', 'dwButtonNumber',	# current button number pressed        
    'LONG', 'dwPOV',		# point of view state        
    'LONG', 'dwReserved1',	# reserved for communication between winmm driver        
    'LONG', 'dwReserved2',	# reserved for future expansion
  );

my $joyinfoex=Win32::API::Struct->new( 'JOYINFOEX' ); # Register the structure

# Windows wants us to fill in some parts of the structure before we use it:
$joyinfoex->{dwSize}=Win32::API::Struct::sizeof($joyinfoex);
$joyinfoex->{dwFlags}= 0x01 |  0x02 |  0x04 |  0x08 |  0x10 |  0x20 |  0x40 |  0x80; # JOY_RETURNX JOY_RETURNY JOY_RETURNZ JOY_RETURNR JOY_RETURNU JOY_RETURNV JOY_RETURNPOV JOY_RETURNBUTTONS

# "import" the call we need to get the joystick data.
my $joyGetPosEx=Win32::API->new('WinMM', 'int joyGetPosEx(int a, JOYINFOEX *p)');

my $endloop=0;
my $recording;
my @cmds;

my @inputs=qw(dwXpos dwYpos dwRpos); my %state;

print "Use sticks to control servos\n";
print "Y = start recording\n";
print "B = stop recording\n";
print "A = Playback\n";
print "X = Exit\n\n";


while(!$endloop) {
  my $ret=$joyGetPosEx->Call($ARGV[0] || 0,$joyinfoex);
  die "Joystick problem - rc=$ret" if($ret);
  &Joy($joyinfoex);

  $endloop=($joyinfoex->{'dwButtons'}&4); #  + $joyinfoex->{'dwButtonNumber'};
  if($joyinfoex->{'dwButtons'}&8) {		# Y button
    print "Recording begun\n" unless($recording);
    $recording=1;
    @cmds=(); # Clear old one
  }
  if($joyinfoex->{'dwButtons'}&2) {		# B button
    print "Recording ended. Got " . $#cmds . " commands \n" if($recording);
    $recording=0; 
  }
  if($joyinfoex->{'dwButtons'}&1) {		# A button
    $recording=0;
    print "Playing back recording...\n";
    my $t=$cmds[0]; $t=$$t[0];	# Start moment.
    foreach my $step (@cmds) {
      my $delay=($$step[0]-$t);
      print "D:$delay\t";
      if($delay>0) { select(undef, undef, undef, $delay); }
      &Joy($$step[1]);
      $t=$$step[0];
    }
  }
} # loop;

$port->close;
print "Ending on button-press\n";
exit(0);

sub Joy {
  my($joyinfoex)=@_;
  for(my $i=0; $i<=$#inputs; $i++) {
    if($state{$inputs[$i]} ne $joyinfoex->{$inputs[$i]}) {	# joystick pos is changed
      my $pos=int($joyinfoex->{$inputs[$i]} / 256); $pos=254 if($pos>254);
      my $pack = pack "CCC", 255,$i, $pos;
      my ($user,$system,$cuser,$csystem) = times;
      print "($user,$system,$cuser,$csystem) Sending:" . unpack("H*", $pack) . "\n";

      $port->write($pack);
      $state{$inputs[$i]} = $joyinfoex->{$inputs[$i]};

      if($recording) {
        push @cmds,[$user,&ObjtoH($joyinfoex)];
      }

    } # change
  } # $i
} # Joy

sub ObjtoH {
  my($joyinfoex)=@_;
  my %h;
  foreach my $i (@inputs) {$h{$i}=$joyinfoex->{$i}}
  return \%h;
}

# The end.

Have Fun!!!

Hello.

Very cool, thanks for sharing! I am moving your thread to the Servo Controllers forum where I think Maestro users searching for something like this are more likely to find it, but I’m leaving a shadow topic to redirect from the “Share Your Projects” forum.

- Ben