View unanswered posts | View active topics It is currently Wed Jul 30, 2014 3:43 am






Reply to topic  [ 5 posts ] 
Advanced Joystick Driver 
Author Message
Novice

Joined: Fri Oct 24, 2008 8:58 am
Posts: 87
Post Advanced Joystick Driver
I've noticed alot of issues on the forum recently have been related to proper joystick handling.
There is alot of desirable features left out of the default driver, so I've packaged them all together into an advanced driver.

Some of the features include:
1. More convenient naming scheme for easier coding.
ie. joy1.left.x instead of joystick.joy1_x1
2. Tracks the change in state of a button in order to perform an action
only once when the button is pressed or released.
ie. if(joy1Press(1)) ...
3. Tracks the state of the hat and provides an enumeration for the different
positions. Also indicates a change in state of the hat.
4. Provides for remapping joystick axis through a table lookup.
This is used to remove the deadband and to perform non-linear scaling.
5. Provides a boolean switch like action for changes in joystick axis. This
enables performing an action when an axis leaves from center or returns to center
ie. if(joy1.left.x_sw == kAxisToZero) ...
6. Provides a logical separation between physical joysticks and driver actions.
This allows either joystick to act as driver 1 or driver 2.
7. Most features can be enabled or disabled and no code will be generated for
disabled features.

To use the new driver you have to call initJoystick() and you have to call updateJoystick() periodically.
Code:
#include "AdvJoystickDriver.c"

task main()
{
  initJoystick();

  while(true)
  {
    updateJoystick();

   /* now you can use the joy1 and joy2 variables */

   // motor[motorA] = joy1.left.y / 10;
   // motor[motorB] = joy1.right.y / 10;

  /* do things based on a button press */
  if (joy1.btn_down & joyBtn1)
     PlaySound(soundBeepBeep);

  /* do more complex button actions like using triggers for shift */
  if (joy1Hold(7) && joy1Press(2))
     PlaySound(soundException);

  /* using the top hat */
  if (joy1.hat == kHatLeft && joy1.hat_press)
    PlaySound(soundLowBuzzShort);

  /* using joystick motion as a switch */
  if (joy1.right.y_sw == kAxisFromZero)
    PlaySound(soundUpwardTones);
  if (joy1.right.y_sw == kAxisToZero)
    PlaySound(soundDownwardTones);

  /* change to driver 1 by pressing btn 9 change to driver 2 by pressing btn 10 */
  if (bDriverSwitched)
    PlaySound(soundBlip);

  }
}


Since I've tried numerous times to upload files and it never works (I've tried IE, Firefox, Chrome) I'm going to have to include the source in a code block.

Code:
/************************************************************************
 * AdvJoystickDriver.c
 *
 * This driver provides additional functionality to the default joystick
 * handling routines provided by RobotC.
 *
 * Some of the features include:
 *    1. More convenient naming scheme for easier coding.
 *       ie. joy1.left.x instead of joystick.joy1_x1
 *    2. Tracks the change in state of a button in order to perform an action
 *       only once when the button is pressed or released.
 *       ie. if(joy1Press(1)) ...
 *    3. Tracks the state of the hat and provides an enumeration for the different
 *       positions. Also indicates a change in state of the hat.
 *    4. Provides for remapping joystick axis through a table lookup.
 *       This is used to remove the deadband and to perform non-linear scaling.
 *    5. Provides a boolean switch like action for changes in joystick axis. This
 *       enables performing an action when an axis leaves from center or returns to center
 *       ie. if(joy1.left.x_sw == kAxisToZero) ...
 *    6. Provides a logical separation between physical joysticks and driver actions.
 *       This allows either joystick to act as driver 1 or driver 2.
 *    7. Most features can be enabled or disabled and no code will be generated for
 *       disabled features.
 *
 *  Revision 1.0 - Jan 2009
 *  Author: C. George
 */


#if !defined(int16)
#define int16 int
#endif

#if !defined(int32)
#define int32 long
#endif

#define ADV_JOYSTICK_DRIVER_C

/************************************************************************
 * Enable or Disable features of this advanced joystick driver
 */

#define ENABLE_AXIS_REMAP         1      // This enables non-linear mapping from lookup table
#define ENABLE_AXIS_SWITCH_CHECK  1     // This enables checking for joystick axis motion
#define ENABLE_PLAYER_SWAP        1     // This enables player swapping



/************************************************************************
 * Macros to make button processing code easier to read
 */

#if ENABLE_AXIS_SWITCH_CHECK
typedef enum
{
   kAxisToZero   = -1,      // Set when a joystick axis has just returned to zero
   kAxisNoSwitch = 0,      // Set when there is has been no switch
   kAxisFromZero = 1      // Set when a joystick axis has just left zero
} TAxisSwitch;
#endif

typedef struct
{
   int16 x;            // current value of joystick
   int16 y;

#if ENABLE_AXIS_SWITCH_CHECK
   TAxisSwitch x_sw;       // convert joystick movement to switch type action
   TAxisSwitch y_sw;
#endif

} TStick;

typedef enum
{
  kHatCentered = 0,
  kHatUp = 1,
  kHatUpRight = 2,
  kHatRight = 3,
  kHatDownRight = 4,
  kHatDown = 5,
  kHatDownLeft = 6,
  kHatLeft = 7,
  kHatUpLeft = 8
} THatPosition;

typedef struct
{
   TStick  left;         // x,y values for the left analog thumbstick
   TStick  right;         // x,y values for the right analog thumbstick
   THatPosition   hat;       // enumeration for tophat position
   bool    hat_press;      // set true when a tophat is pressed
   int16    btn;         // stores the state of the buttons
   int16    btn_down;      // set true when a button is pressed
   int16    btn_up;         // set true when a button is releaseed
} TAdvJoystick;

TAdvJoystick joy1;         // Joystick information for Driver 1
TAdvJoystick joy2;         // Joystick information for Driver 2

#if ENABLE_PLAYER_SWAP
bool bSwitchDrivers;      // Set when Drive 1 = Joystick 2 and Driver 2 = Joystick 1
bool bSwitchingDrivers;     // Set while the Driver Swap buttons are depressed
bool bDriverSwitched;      // Set when bSwitchingDrivers goes from True to False

TAdvJoystick joyA;         // temporary variables to make driver swapping logic easier
TAdvJoystick joyB;
#endif

/************************************************************************
 * Macros to make button processing code easier to read
 */

#define joy1Hold(bnum)        (joy1.btn           & (1 << (bnum - 1)))
#define joy1Press(bnum)       (joy1.btn_down      & (1 << (bnum - 1)))
#define joy1Release(bnum)     (joy1.btn_up        & (1 << (bnum - 1)))
#define joy2Hold(bnum)        (joy2.btn           & (1 << (bnum - 1)))
#define joy2Press(bnum)       (joy2.btn_down      & (1 << (bnum - 1)))
#define joy2Release(bnum)     (joy2.btn_up        & (1 << (bnum - 1)))

#define joyBtn1  (1 << 0)
#define joyBtn2  (1 << 1)
#define joyBtn3  (1 << 2)
#define joyBtn4  (1 << 3)
#define joyBtn5  (1 << 4)
#define joyBtn6  (1 << 5)
#define joyBtn7  (1 << 6)
#define joyBtn8  (1 << 7)
#define joyBtn9  (1 << 8)
#define joyBtn10 (1 << 9)
#define joyBtn11 (1 << 10)               // NOTE: Buttons 11 & 12 aren't being sent by the FMS
#define joyBtn12 (1 << 11)

#define joyBtn(bnum) (1 << (bnum - 1))


// Joystick driver included here so the AdvJoystick variables show up first in the debug
// variable window
#if !defined(JOYSTICKDRIVER_C)
#include "JoystickDriver.c"
#endif

TJoystick joystickPrev;            // Temporary variable to track changes in joystick state

/************************************************************************
 * Include the axis remapping code if required.
 */

#if ENABLE_AXIS_REMAP

// This lookup table converts joystick values to power % based on a
// log-log function.

int axis_map[] = {
       2,    4,    6,    8,   10,   12,   14,   16,   18,   20,
      22,   25,   27,   29,   32,   34,   37,   39,   42,   44,
      47,   50,   52,   55,   58,   61,   64,   67,   70,   73,
      77,   80,   83,   87,   90,   94,   97,  101,  105,  109,
     113,  117,  121,  125,  129,  134,  138,  143,  147,  152,
     157,  162,  167,  172,  177,  183,  188,  194,  200,  205,
     212,  218,  224,  231,  237,  244,  251,  258,  265,  273,
     280,  288,  296,  304,  313,  321,  330,  339,  348,  358,
     368,  378,  388,  398,  409,  420,  432,  443,  455,  468,
     480,  493,  507,  520,  534,  549,  564,  579,  595,  611,
     628,  645,  662,  680,  699,  718,  738,  759,  779,  801,
     823,  846,  870,  894,  920,  946,  972,  1000,  1000};

// Applies the lookup table for a given value.
int remapAxis(int value)
{
    int t=1;               // since the mapping function is symmetric
                        // we only store the positive half.
    if (value < 0)            // check here if the input value is negative
    {
       value *= -1;         // take the absolute value
        t=-1;               // remember to invert the result
        value += 1;            // correction to allow 100% power
    }
    else
    {
       value += 1;            // correction to allow 100% power since
    }

    if (value < 11)            // deadband removal
        return 0;

    value -= 11;            // deadband values not included in table

    t *= axis_map[value];      // lookup new value and correct for negative input
    return t;
}

#endif


/************************************************************************
 * initJoystick()
 *
 * Call this function once at the beginning of a program to initialize
 * internal variables.
 */

void initJoystick()
{
   // Initialize joystick structures to prevent false button press at startup
   memcpy(joystick, joystickCopy, sizeof(joystick));
   memcpy(joystickPrev, joystick, sizeof(joystick));

#if ENABLE_PLAYER_SWAP
   bSwitchDrivers    = false;
   bSwitchingDrivers = false;
   bDriverSwitched   = false;
#endif

#if ENABLE_AXIS_SWITCH_CHECK
   // Initialize joystick axis structures to prevent false axis motion at startup
   joyA.left.x = 0;
   joyA.left.y = 0;
   joyA.right.x = 0;
   joyA.right.y = 0;

   joyB.left.x = 0;
   joyB.left.y = 0;
   joyB.right.x = 0;
   joyB.right.y = 0;
#endif

}

/************************************************************************
 * processAnalogStick()
 *
 * This private function performs all processing for the x and y axis of an analog
 * thumbstick.
 */

void processAnalogStick(TStick& stick, int joy_x, int joy_y )
{
   int tx, ty;

   tx = stick.x;
   ty = stick.y;

#if ENABLE_AXIS_REMAP
   stick.x = remapAxis(joy_x);
   stick.y = remapAxis(joy_y);
#else
   stick.x = joy_x;
   stick.y = joy_y;
#endif

#if ENABLE_AXIS_SWITCH_CHECK
   stick.x_sw = kAxisNoSwitch;
   if (tx != 0 && stick.x == 0)
   {
      stick.x_sw = kAxisToZero;
   }
   else if (stick.x != 0 && tx == 0)
   {
      stick.x_sw = kAxisFromZero;
   }

   stick.y_sw = kAxisNoSwitch;
   if (ty != 0 && stick.y == 0)
   {
      stick.y_sw = kAxisToZero;
   }
   else if (stick.y != 0 && ty == 0)
   {
      stick.y_sw = kAxisFromZero;
   }
#endif

}


/************************************************************************
 * updateJoystick()
 *
 * Periodically call this function to update the joy1 and joy2 variables from
 * the current joystick values.
 *
 */

void updateJoystick()
{
   // Get the new joystick values
   memcpy(joystick, joystickCopy, sizeof(joystick));


#if ENABLE_PLAYER_SWAP
   // Button Handling Routines
   joyA.btn        = joystick.joy1_Buttons;
   joyA.btn_down   = (joystick.joy1_Buttons ^ joystickPrev.joy1_Buttons) & joystick.joy1_Buttons;
   joyA.btn_up     = (joystick.joy1_Buttons ^ joystickPrev.joy1_Buttons) & (~joystick.joy1_Buttons);

   joyA.hat       = (THatPosition)(joystick.joy1_TopHat+1);
   joyA.hat_press = (joystickPrev.joy1_TopHat != joystick.joy1_TopHat && joystick.joy1_TopHat != -1);

   joyB.btn        = joystick.joy2_Buttons;
   joyB.btn_down = (joystick.joy2_Buttons ^ joystickPrev.joy2_Buttons) & joystick.joy2_Buttons;
   joyB.btn_up   = (joystick.joy2_Buttons ^ joystickPrev.joy2_Buttons) & (~joystick.joy2_Buttons);

   joyB.hat       = (THatPosition)(joystick.joy1_TopHat+1);
   joyB.hat_press = (joystickPrev.joy2_TopHat != joystick.joy2_TopHat && joystick.joy2_TopHat != -1);

   // Process the Analog Thumbsticks
   processAnalogStick(joyA.left,  joystick.joy1_x1, joystick.joy1_y1);
   processAnalogStick(joyA.right, joystick.joy1_x2, joystick.joy1_y2);
   processAnalogStick(joyB.left,  joystick.joy2_x1, joystick.joy2_y1);
   processAnalogStick(joyB.right, joystick.joy2_x2, joystick.joy2_y2);

   // Check for a player swap:
   //    Pressing Button 9 makes that joystick player 1
   //    Pressing Button 10 makes that joystick player 2

   bDriverSwitched = false;

   // Check if the joystick to player mapping should be normal
   if( (joystick.joy1_Buttons & joyBtn9) || (joystick.joy2_Buttons & joyBtn10) )
   {
      bSwitchDrivers = false;
      bSwitchingDrivers = true;
   }
   else if( (joystick.joy1_Buttons & joyBtn10) || (joystick.joy2_Buttons & joyBtn9) )
   {
      bSwitchDrivers = true;
      bSwitchingDrivers = true;
   }
   else
   {
      if (bSwitchingDrivers)
      {
         bSwitchingDrivers = false;
         bDriverSwitched = true;
      }
   }

   // Switch players if required
   if (bSwitchDrivers)
   {
      memcpy(joy1, joyB, sizeof(joy1));
      memcpy(joy2, joyA, sizeof(joy2));
   }
   else
   {
      memcpy(joy1, joyA, sizeof(joy1));
      memcpy(joy2, joyB, sizeof(joy2));
   }

#else
   joy1.btn        = joystick.joy1_Buttons;
   joy1.btn_down   = (joystick.joy1_Buttons ^ joystickPrev.joy1_Buttons) & joystick.joy1_Buttons;
   joy1.btn_up     = (joystick.joy1_Buttons ^ joystickPrev.joy1_Buttons) & (~joystick.joy1_Buttons);

   joy1.hat       = (THatPosition)(joystick.joy1_TopHat + 1);
   joy1.hat_press = (joystickPrev.joy1_TopHat != joystick.joy1_TopHat && joystick.joy1_TopHat != -1);

   joy2.btn        = joystick.joy2_Buttons;
   joy2.btn_down = (joystick.joy2_Buttons ^ joystickPrev.joy2_Buttons) & joystick.joy2_Buttons;
   joy2.btn_up   = (joystick.joy2_Buttons ^ joystickPrev.joy2_Buttons) & (~joystick.joy2_Buttons);

   joy2.hat       = (THatPosition)(joystick.joy2_TopHat + 1);
   joy2.hat_press = (joystickPrev.joy2_TopHat != joystick.joy2_TopHat && joystick.joy2_TopHat != -1);

   // Process the Analog Thumbsticks
   processAnalogStick(joy1.left, joystick.joy1_x1, joystick.joy1_y1);
   processAnalogStick(joy1.right, joystick.joy1_x2, joystick.joy1_y2);
   processAnalogStick(joy2.left, joystick.joy2_x1, joystick.joy2_y1);
   processAnalogStick(joy2.right, joystick.joy2_x2, joystick.joy2_y2);
#endif

   // Save the current settings as the previous settings
   memcpy(joystickPrev, joystick, sizeof(joystick));
}



Enjoy.


Last edited by chadgeorge on Fri Feb 13, 2009 9:30 pm, edited 1 time in total.



Sat Feb 07, 2009 5:48 pm
Profile
Expert
User avatar

Joined: Tue Oct 14, 2008 7:16 pm
Posts: 171
Location: Investigating an unidentified ship sighted in Sector 31428
Post Re: Advanced Joystick Driver
Some of your reorganization, espicially the use of stuff like "joy1.left.x" makes you sound like a .NET programmer. :programmer: :bigthumb:

_________________
Captain, Head programmer, School of the Arts, Silverbots Robtics Team #2890
Code:
using namespace System;
using namespace Genius;
using namespace Personality;
public ref class Nerd : Geek, IAnserable
{
    Geek::Type brainMode = Geek::Type::Programmer;
}


Sun Feb 08, 2009 4:19 am
Profile
Novice

Joined: Fri Oct 24, 2008 8:58 am
Posts: 87
Post Re: Advanced Joystick Driver
Although I have done alot of .NET programming, lately I prefer python. Mostly the left/right syntax is because I could never remember where joy1_x1 was :)

Also with RobotC's particular flavor of "C" (ie. lack of pointer support, prodigous use of global variables, no call stack, no recursion, etc)
I've found that structures and pass-by-reference functions are the best way available to organize code and data.


Sun Feb 08, 2009 1:44 pm
Profile
Site Admin
Site Admin

Joined: Wed Jan 24, 2007 10:42 am
Posts: 601
Post Re: Advanced Joystick Driver
FYI, this is great improvement over the existing driver for testing... but during software inspection they may be looking for the JoystickDriver.c file that is posted on the FTCTraining website, depending upon how knowledgeable the software inspector is.

They may or may not look for it, but if they do and you're using this... you may not pass software inspection.

_________________
Timothy Friez
ROBOTC Developer - SW Engineer
tfriez@robotc.net


Fri Feb 13, 2009 5:19 pm
Profile
Novice

Joined: Fri Oct 24, 2008 8:58 am
Posts: 87
Post Re: Advanced Joystick Driver
My code does use the official joystick driver.

The include is actually buried towards the middle of the file so that all of my variables show up first in the global variables list.

Also in "official game" code I include it more obviously in the top level file again, just to make software inspectors who might not know any better sleep better at night :)


Fri Feb 13, 2009 9:29 pm
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 5 posts ] 

Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron



Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.