Arduino gamepad control via USB host shield- no PC required-

Anything to do with programing the Arduino Platform.
User avatar
olegodo
Posts: 222
Joined: Aug 30th, 2013, 9:47 am
Location: Bergen, Norway

Re: Arduino gamepad control via USB host shield- no PC requi

Post by olegodo »

Thanks for the offer! I really appreciate it, but I am focusing on Moki's solution now. All components have arrived and waiting to get connected :)
But when you get it working please share what you got so others can use it. If it's ok for you ofc.
I think the control and software part is the most difficult for most of us
a_shorething
Posts: 289
Joined: Sep 10th, 2013, 5:26 pm
Location: New Jersey Shore

Re: Arduino gamepad control via USB host shield- no PC requi

Post by a_shorething »

olegodo wrote:Thanks for the offer! I really appreciate it, but I am focusing on Moki's solution now. All components have arrived and waiting to get connected :)
But when you get it working please share what you got so others can use it. If it's ok for you ofc.
I think the control and software part is the most difficult for most of us
Will do.
a_shorething
Posts: 289
Joined: Sep 10th, 2013, 5:26 pm
Location: New Jersey Shore

Re: Arduino gamepad control via USB host shield- no PC requi

Post by a_shorething »

OK, so I got the USB host shield wired up and working with an XBOX 360 controller.

I bought the shield from Sparkfun and it seems that their version of the USB host shield requires a bit of a 'hack'. You need a jumper from D7<>RST pin on the shield for it to work.

Once I found that little trick it worked fine.

I did notice that the way they have the code it kind of hangs a bit for the 'force feedback' message and also I need to try to get it to work with the wireless dongle. I only got it working with one gamepad on the first attempt and the wireless one never linked up. I didn't try for long though so I'm sure it's a minor issue (Different sketches are used: XBOXUSB and XBOXRECV)

So it looks like this will work once I merge in the code I had from my previous setup. I'm not 100% sure which way I will go yet, but it's really cool to have this option.

I'll post a sketch once I iron out the rough spots in the code and I'll leave comments for people to find where they should add stuff and how to add it.
a_shorething
Posts: 289
Joined: Sep 10th, 2013, 5:26 pm
Location: New Jersey Shore

Re: Arduino gamepad control via USB host shield- no PC requi

Post by a_shorething »

OK, as promised, I'm posting some code that actually works here. It's version .01 or something like that because it's just a test and my goal was twofold:
1. Get it working to verify that I could read codes directly from an XBOX controller via an Arduino and send it somewhere.
2. Get some motors turning based on that input so that I know it's reacting in real time without any lag or hiccups.

The default code that they send for the XBOXUSB from the shield 2.0 repository didn't work for me. I think they set themselves a task of being able to read any of the codes that come in, but the way it was set up (with delays and stuff) meant that it wasn't practical for realtime control of anything, especially if you set up lots of buttons (and BTW- you can use combo buttons too, like a control or shift key). They also have another one that uses up to 4 wireless XBOX controllers and the XBOX USB Dongle, but I couldn't get that one working on my setup yet, so this is the wired version. Once I figure out the wireless setup I'll post code for that.

So to get it to work for me I had to do a few things. First on the hardware side:

1. Since I got the Sparkfun USB shield, I had to jumper from RST>D7. That stinks and for that reason I don't recommend this shield, but it's what I had to do.
2. For my test environment I'm using the Adafruit motor controller board which is not going to be my final configuration. Just ignore the actual syntax if you're planning to set up another kind of motor control signal and substitute your own. I plan to use PWM control, possibly through an Adafruit i2c board and when I get that coded I'll update this thread. Until then, this is an example that works, so if you want to test with this exactly code, you'll need the Adafruit board.
3. Power: The USB shield will require you to power the Arduino from additional power, not just the USB cable. Running the XBOX controller takes more than the USB can provide. You'll also need power for the motor controller.

On the software side, I downloaded the latest version of USB 2.0 from Oleg here:http://www.circuitsathome.com/products- ... or-arduino THANKS OLEG!

Then for testing purposes, I commented everything out and uncommented just want I wanted to test which was the left hat X and Y inputs (left joystick on the Xbox controller). When mapping this stuff there is usually a 'dead zone' where the controller joystick may not return to 0,0 every time so you need to account for it. It looks like the default code uses 7500 (out of a range in each axis of -32767 to 32767). I played with it and found that 500 was plenty for me. I set up a variable called DeadZone that you can change if you like. The code will ignore anything from -(DeadZone) to +(DeadZone) that comes in from the controller.

Finally, I played with the options of setting and resetting and the one that seemed to work best was to read three different states for each Axis. One positive, one in the DeadZone (reset) and one negative. There are lots of ways to do this, but it worked well for me in test version 1.

In this one, I am setting leftmotor and rightmotor to the same values, but the final code for an ROV should read the input, and send the value out to each motor (or control, or command or whatever) individually for the input configuration that's being sent. *Don't forget the way a program like this works. While it's in the loop it will continue to scroll from top to bottom and the state of the gamepad will change very quickly. You need to deal with 'triggers' and also 'reset' states after the trigger in your code.

NOTE: You will also notice that I'm using the 'MAP' function in my code. This is a very useful one-line function you can use to pass a range of numbers scaled or 'mapped' to another set. So in this case my code takes the range from the beginning of the DeadZone to the maximum number in each axis and maps it to the range that my motor controller is expecting.

j=(map(Xbox.getAnalogHat(LeftHatY),DeadZone+1,32767,1,255));
So in this case, I'm setting the integer j equal to a number that is converted from the DeadZone+1 (501 in this case) to 32767 and it's converted into a number between 1 and 255 (which is the range the Adafruit motor controller is expecting).
Your ranges may differ, so you can just put in your min and max in place of the 1 and 255 to get it to work.

Disclaimer: This code is all open source, blah blah blah, please share and update if you like, credit Oleg if you use it or pass it on, I didn't add much so don't worry about mentioning me and use at your own risk, YMMV. :)


Code: Select all

/* 
This is a test sketch for the Adafruit assembled Motor Shield for Arduino v2
It won't work with v1.x motor shields! Only for the v2's with built in PWM
control

For use with the Adafruit Motor Shield v2 
---->	http://www.adafruit.com/products/1438

This sketch creates a fun motor party on your desk *whiirrr*
Connect a unipolar/bipolar stepper to M3/M4
Connect a DC motor to M1
Connect a hobby servo to SERVO1
*/

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <Servo.h> 

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
// And connect a DC motor to port M1
Adafruit_DCMotor *rightMotor = AFMS.getMotor(1);
Adafruit_DCMotor *leftMotor = AFMS.getMotor(2);

// We'll also test out the built in Arduino Servo library
Servo servo1;



/*
 Example sketch for the Xbox 360 USB library - developed by Kristian Lauszus
 For more information visit my blog: http://blog.tkjelectronics.dk/ or
 send me an e-mail:  kristianl@tkjelectronics.com
 */



#include <XBOXUSB.h>
// Satisfy IDE, which only needs to see the include statment in the ino.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

USB Usb;
XBOXUSB Xbox(&Usb);

void setup() {
  Serial.begin(115200);
    AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz

  // Attach a servo to pin #10
  //servo1.attach(10);
   

#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif

  if (Usb.Init() == -1) {
    Serial.print(F("\r\nXBOX USB Init did not start"));
    while (1); //halt
  }
  Serial.print(F("\r\nXBOX USB Library Started"));
    
  
}
void loop() {
  Usb.Task();
    int DeadZone=500;  
  if (Xbox.Xbox360Connected) {
    /*
    if (Xbox.getButtonPress(L2) || Xbox.getButtonPress(R2)) {
      Serial.print("L2: ");
      Serial.print(Xbox.getButtonPress(L2));
      Serial.print("\tR2: ");
      Serial.println(Xbox.getButtonPress(R2));
      Xbox.setRumbleOn(Xbox.getButtonPress(L2), Xbox.getButtonPress(R2));
    } else
      Xbox.setRumbleOff();
      */
      // Xbox.setRumbleOn(0, 0);
      /*if (Xbox.getAnalogHat(RightHatX) == 0){
        servo1.write(90);
      }
      if (Xbox.getAnalogHat(RightHatX)>0){
        servo1.write(map(Xbox.getAnalogHat(RightHatX), 1, 32767, 91, 180));
      }
      if (Xbox.getAnalogHat(RightHatX)<0){
        servo1.write(map(Xbox.getAnalogHat(RightHatX), -32767,-1, 0, 89));
      }*/
      
      
    // if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {
      //if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {
        //leftMotor->run(FORWARD);
        // leftMotor->setSpeed(i);  
        // rightMotor->run(FORWARD);
        // rightMotor->setSpeed(i);  
          
          //}
       // left hat is for motors, right hat is for steering/servo for now
        int j;
        if ((Xbox.getAnalogHat(LeftHatY) <= DeadZone) && (Xbox.getAnalogHat(LeftHatY) >= (DeadZone*-1)))
        {
         
          
          leftMotor->run(RELEASE);
         rightMotor->run(RELEASE);
        }

        if (Xbox.getAnalogHat(LeftHatY) > DeadZone)
        {
          // map(Xbox.getAnalogHat(RightHatX), -32767, 32767, 0, 180)
          j=(map(Xbox.getAnalogHat(LeftHatY),DeadZone+1,32767,1,255));
          leftMotor->setSpeed(j);  
          rightMotor->setSpeed(j);  
          leftMotor->run(BACKWARD);
          rightMotor->run(BACKWARD);
          //leftMotor->run(RELEASE);
          Serial.print(F("LeftHatY also : "));
          Serial.print(j);
          Serial.print("\t");
        }
        
          
        if (Xbox.getAnalogHat(LeftHatY) < (-1*DeadZone))
        {
          // map(Xbox.getAnalogHat(RightHatX), -32767, 32767, 0, 180)
          // turn negative into positive, but reverse motor direction
          int k=(Xbox.getAnalogHat(LeftHatY)*(-1));
          
          j=(map(k,DeadZone+1,32767,1,255));
          leftMotor->setSpeed(j);  
          leftMotor->run(FORWARD);
          rightMotor->setSpeed(j);  
          rightMotor->run(FORWARD);

          //leftMotor->run(RELEASE);
          Serial.print(F("LeftHatY also : "));
          Serial.print(j);
          Serial.print("\t");
        }
        // right hat is for steering*********************
        int r;
        if ((Xbox.getAnalogHat(RightHatX) <= DeadZone) && (Xbox.getAnalogHat(RightHatX) >= (DeadZone*-1)))
        {

           servo1.write(90);
          
        }

        if (Xbox.getAnalogHat(RightHatY) > DeadZone)
        {
          // map(Xbox.getAnalogHat(RightHatX), -32767, 32767, 0, 180)
          servo1.write(map(Xbox.getAnalogHat(RightHatX), DeadZone+1,32767,91,255));
          
          
        }
        
          
        if (Xbox.getAnalogHat(RightHatX) < (-1*DeadZone))
        {
          int k=(Xbox.getAnalogHat(RightHatX)*(-1));
          
          j=(map(k,DeadZone+1,32767,1,89));
          servo1.write(j);
        }
        // ************end of right hat code*************************
        
      //}
      /*if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) {
        Serial.print(F("LeftHatY: "));
        Serial.print(Xbox.getAnalogHat(LeftHatY));
        Serial.print("\t");
      }*/
      
      /*if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) {
        Serial.print(F("RightHatX: "));
        Serial.print(Xbox.getAnalogHat(RightHatX));
        Serial.print("\t");
        servo1.write(map(Xbox.getAnalogHat(RightHatX), -32767, 32767, 0, 180));
      }*/
      /*
      if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {
        Serial.print(F("RightHatY: "));
        Serial.print(Xbox.getAnalogHat(RightHatY));
      }
      // Serial.println();
    // }

    if (Xbox.getButtonClick(UP)) {
      Xbox.setLedOn(LED1);
      Serial.println(F("Up"));
    }
    if (Xbox.getButtonClick(DOWN)) {
      Xbox.setLedOn(LED4);
      Serial.println(F("Down"));
    }
    if (Xbox.getButtonClick(LEFT)) {
      Xbox.setLedOn(LED3);
      Serial.println(F("Left"));
    }
    if (Xbox.getButtonClick(RIGHT)) {
      Xbox.setLedOn(LED2);
      Serial.println(F("Right"));
    }

    if (Xbox.getButtonClick(START)) {
      Xbox.setLedMode(ALTERNATING);
      Serial.println(F("Start"));
    }
    if (Xbox.getButtonClick(BACK)) {
      Xbox.setLedBlink(ALL);
      Serial.println(F("Back"));
    }
    if (Xbox.getButtonClick(L3))
      Serial.println(F("L3"));
    if (Xbox.getButtonClick(R3))
      Serial.println(F("R3"));

    if (Xbox.getButtonClick(L1))
      Serial.println(F("L1: Left Shoulder"));
    
    if (Xbox.getButtonClick(R1))
      Serial.println(F("R1: Right Shoulder"));
    if (Xbox.getButtonClick(XBOX)) {
      Xbox.setLedMode(ROTATING);
      Serial.println(F("Xbox"));
    }

    if (Xbox.getButtonClick(A))
      Serial.println(F("A"));
    if (Xbox.getButtonClick(B))
      Serial.println(F("B"));
    if (Xbox.getButtonClick(X))
      Serial.println(F("X"));
    if (Xbox.getButtonClick(Y))
      Serial.println(F("Y"));
      */
  }
  delay(1);
}
Post Reply