I am trying to take the reading from a Thrustmaster USB Joystick (only the x and y axis motion, no other inputs like buttons,etc) and mapping it to two stepper motors(one of the for the x motion, the other for the y motion), so that the position of the Joystick corresponds to some angular velocity at which the motors turn.
I am currently using a Usb Host Shield 2.0 on an Arduino Uno, and I am using the UBS_Host_Shield_2.0 library. I am trying to modify the USBHIDJoystick example in this library to accomplish my goals. I have modified these to remove the code pertaining to button readings, and have added a section where I included the AccelStepper library in order to drive the motors, and this code is posted below.
I am trying to get the motor to turn continuously at the constant speed the constant joystick position corresponds to. I have noticed both through the serial monitor and through the performance of the motors themselves that if I hold the joystick in some constant position, only the first reading in that position gets printed to the monitor, and the motor is actuated for a single pulse, even if I am continuing to hold the joystick position. These readings are then followed by readings which are identical to the readings I get from an un-actuated joystick. This response makes me believe that somewhere in the UBS_Host_Shield_2.0 library, there is a section of code where the previous usb signal is compared to the current one, and if it is not different, it starts the entire loop again to inspect the usb signal reading, without forwarding and mapping the signal to a value to drive the motors.
USBHIDJoystick.ino
#include <usbhid.h>
#include <hiduniversal.h>
#include <usbhub.h>
// Satisfy IDE, which only needs to see the include statment in the ino.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
#include "hidjoystickrptparser.h"
USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
JoystickEvents JoyEvents;
JoystickReportParser Joy(&JoyEvents);
void setup() {
Serial.begin(115200);
#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
Serial.println("Start");
if (Usb.Init() == -1)
Serial.println("OSC did not start.");
Serial.println("");
delay(200);
if (!Hid.SetReportParser(0, &Joy))
ErrorMessage<uint8_t > (PSTR("SetReportParser"), 1);
}
void loop() {
Usb.Task();
}
hidjoystickrptparser.cpp
#include "hidjoystickrptparser.h"
#include <AccelStepper.h>
AccelStepper FirstMotor(1, 3, 2);
AccelStepper SecondMotor(1, 6, 7);
JoystickReportParser::JoystickReportParser(JoystickEvents *evt) :
joyEvents(evt) {
for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)
oldPad[i] = 0xD;
}
void JoystickReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
bool match = true;
// Checking if there are changes in report since the method was last called
for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)
if (buf[i] != oldPad[i]) {
match = false;
break;
}
// Calling Game Pad event handler
if (!match && joyEvents) {
joyEvents->OnGamePadChanged((const GamePadEventData*)buf);
for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i];
}
}
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) {
FirstMotor.setMaxSpeed(5000); // STEPS PER SECOND
SecondMotor.setMaxSpeed(5000); // STEPS PER SECOND
uint8_t X1 = evt->X;
uint8_t Y1 = evt->Y;
int speed_A;
int speed_B;
// A JOYSTICK ---------------------------------
if((X1 >= 128)&&(X1 <= 251)){
speed_A = map(X1, 251, 128, -5, -500);
FirstMotor.setSpeed(speed_A);
}
if((X1 >= 4)&&(X1 <= 127)){
speed_A = map(X1, 4, 127, 5, 500);
FirstMotor.setSpeed(speed_A);
}
if((X1 > 251)||(X1 < 4)){
speed_A = 0;
FirstMotor.setSpeed(speed_A);
}
// B JOYSTICK ---------------------------------
if((Y1 >= 128)&&(Y1 <= 251)){
speed_B = map(Y1, 251, 128, 5, 500);
SecondMotor.setSpeed(speed_B);
}
if((Y1 >= 4)&&(Y1 <= 127)){
speed_B = map(Y1, 4, 127, -5, -500);
SecondMotor.setSpeed(speed_B);
}
if((Y1 > 251)||(Y1 < 4)){
speed_B = 0;
SecondMotor.setSpeed(speed_B);
}
FirstMotor.runSpeed();
SecondMotor.runSpeed();
}
hidjoystickrptparser.h
#if !defined(__HIDJOYSTICKRPTPARSER_H__)
#define __HIDJOYSTICKRPTPARSER_H__
#include <usbhid.h>
struct GamePadEventData {
uint8_t X, Y;
};
class JoystickEvents {
public:
virtual void OnGamePadChanged(const GamePadEventData *evt);
};
#define RPT_GEMEPAD_LEN 2
class JoystickReportParser : public HIDReportParser {
JoystickEvents *joyEvents;
uint8_t oldPad[RPT_GEMEPAD_LEN];
public:
JoystickReportParser(JoystickEvents *evt);
virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};
#endif // __HIDJOYSTICKRPTPARSER_H__
I am having difficulties determining the exact location of this theoretical section of code however (I am a mechanical engineer and this is my first arduino project), but my attempts at debugging has led me to a section of code in the Usb.cpp file in the UBS_Host_Shield_2.0 library. In the "void USB::Task(Void)" section of the Usb.cpp file, there is a loop that goes as follows:
for(uint8_t i = 0; i < USB_NUMDEVICES; i++)
if(devConfig[i])
rcode = devConfig[i]->Poll();
As an additional bit of information, I have noticed that if I am either not actuating the joystick, or am holding it in a constant position, the rcode reading from this line is '4', and if I have moved the joystick, that first reading will cause rcode to read '0', after which the serial monitor continues to spit out '4''s.
I believe that the 'Poll()' the above line is referring to is found in 'usbhub.cpp' of the same library, in a section of code that starts with 'uint8_t USBhub::Poll(){'. Serial prints have confirmed that the code is going through this section regardless of whether the joystick is actuated or un-actuated, but I have reached a dead end because the line "if(!bPollEnable) return 0;" occurs regardless of whether the joystick is actuated or un-actuated. In this same section of code, rcode is set to 0, with no conditional statements, so this leads me to believe that there is an intermediate section of code between the "rcode = devConfig -> Poll();" and the "uint8_t USBhub::Poll(){" (As I see nowhere in "uint8_t USBhub::Poll(){" where rcode is set to 4.). I was wondering if someone might clarify where, when the code is run, the code gets sent to directly following the line "rcode = devConfig*->Poll();"?*