MHeironimus / ArduinoJoystickLibrary

An Arduino library that adds one or more joysticks to the list of HID devices an Arduino Leonardo or Arduino Micro can support.
GNU Lesser General Public License v3.0
2.06k stars 403 forks source link

USB device not recognized when pedals connected #247

Open MarquisedeMonsas opened 1 year ago

MarquisedeMonsas commented 1 year ago

Description of Issue

Hello. I have been trying to build a HOTAS setup with this library, but I am stuck on wierd issue. I have three devices, a stick with Arduino micro, a throttle with Arduino pro mini and pedals with Arduino pro mini. The pedals and throttle are connected to the Arduino micro by I2C and my code recognises what devices are connected by quering the I2C bus and sets the USB joystics accordingly. I have also set up internal serial debugging, that can be accessed by pressing two buttons on the stick when plugin it in the computer. Everything works great at the debug mode and all axis and buttons are working as they should, but if I connect the stick, with pedals connected, to my computer I get USB device not recognized error. If I connect the joysticks to my computer in debug mode and then flash the code to the Arduino micro all three joysticks show up in control panel, but nothing on the throttle is responsive, but the pedals work. If I connect just the throttle to the stick it works as it should. Also if I go to the debug mode and flash the code with only pedals connected, everything works in the control panel, but if I disconnect and reconnect the joystick, I get the USB device not recognized error again. I am really lost with this one. I am thiking it has to be some kind of software issue, because everything is working fine in serial debug mode.

Things I have tried

Adding delay before and/or after the I2C query. Forcing the detected devices to full HOTAS and skipping the I2C query completely. I was thinking that the query would take too long and windows would time out the USB recognition. Using the unmodified library. I modified the library so that it does not create the joystics that are not found in the I2C query. Either option does not change the behaviour. I used a digispark board (AtTiny88) for the pedals. Same behaviour. I have tried different USB ports and different computers. USB 2 and USB 3 in laptop and desktop, no change in behaviour.

Technical Details

Arduino micro, 2 x Arduino pro mini IDE = PlatformIO on VSCode OS = Windows 10 (19043.1889) HP ZBook 15 G2 and a desktop computer. Joystick library version 2.1.0 and 2.1.1

main.cpp

#include <Arduino.h>
#include <Joystick.h>
#include "inputs.h"
#include "config.h"
#include "i2c.h"

#define STICK     0
#define THR       1
#define PED       2

#define JOYSTICK_COUNT 3

int devices = 0;
uint8_t serialDebug = 0;
i2c coms;
inputs deviceInputs;
//ID, Type, Buttons, Hats, includeXAxis, includeYAxis, includeZAxis, includeRxAxis, includeRyAxis, includeRzAxis,includeRudder, includeThrottle, includeAccelerator, includeBrake, includeSteering
Joystick_ Joystick[JOYSTICK_COUNT] = {
  Joystick_(0x03, JOYSTICK_TYPE_JOYSTICK,  4, 1,  true, true, false, false, false, false, false, false, false, false, false),
  Joystick_(0x04, JOYSTICK_TYPE_JOYSTICK,  10, 0,  true, true,  true,  false, false, false, false, true, false, false, false),
  Joystick_(0x05, JOYSTICK_TYPE_JOYSTICK, 0, 0,   true, true, false, false, false, false, true, false, false, false, false)
};

void getData(uint8_t devices);

void setup() {
  // put your setup code here, to run once:
  pinMode(TRIGGER, INPUT_PULLUP);
  pinMode(BTN0, INPUT_PULLUP);
  pinMode(BTN1, INPUT_PULLUP);
  pinMode(BTN2, INPUT_PULLUP);
  //delay(10);
  if(digitalRead(BTN1) == 0 && digitalRead(BTN2) == 0)
  {
    delay(10000);
    if(digitalRead(BTN1) == 0 && digitalRead(BTN2) == 0)
    {
      serialDebug = 1;
    }
  }
  if(serialDebug == 1)
  {
    Serial.begin(115200);
    delay(2000);
    Serial.print("Serial debug enabled.\n");
    delay(2000);
  }
  coms.init();
  //delay(300);
  if(serialDebug == 1)
    {
    Serial.print("Detecting I2C devices.\n");
    }
  //I2C query start
  devices = coms.detectDevices();
  //devices = 1;
  if(devices < 0)
  {
    if(serialDebug == 1)
    {
    Serial.print("I2C Error: ");
    Serial.print(devices);
    Serial.print("\n");
    delay(2000);
    }
    devices = 0;
  }
  if(serialDebug == 1)
  {
    Serial.print("Devices: ");
    Serial.print(devices);
    Serial.print("\n");
    delay(2000);
  }
  //Full HOTAS
  if(devices == 1)
  {
    //Stick
    Joystick[STICK].setXAxisRange(-127, 127);
    Joystick[STICK].setYAxisRange(-127, 127);
    //Throttle
    Joystick[THR].setXAxisRange(-127, 127);
    Joystick[THR].setYAxisRange(-127, 127);
    Joystick[THR].setZAxisRange(-127, 127);
    Joystick[THR].setThrottleRange(-127, 127);
    //Pedals
    Joystick[PED].setXAxisRange(-127, 127);
    Joystick[PED].setYAxisRange(-127, 127);
    Joystick[PED].setRudderRange(-127, 127);
    if(serialDebug == 0)
    {
      Joystick[STICK].begin();
      Joystick[THR].begin();
      Joystick[PED].begin();
    }
  }
  //Throttle only
  else if(devices == 2)
  {
    //Stick
    Joystick[STICK].setXAxisRange(-127, 127);
    Joystick[STICK].setYAxisRange(-127, 127);
    //Throttle
    Joystick[THR].setXAxisRange(-127, 127);
    Joystick[THR].setYAxisRange(-127, 127);
    Joystick[THR].setZAxisRange(-127, 127);
    Joystick[THR].setThrottleRange(-127, 127);
    if(serialDebug == 0)
    {
      Joystick[STICK].begin();
      Joystick[THR].begin();
    }
  }
  //Pedals only
  else if(devices == 3)
  {
    //Stick
    Joystick[STICK].setXAxisRange(-127, 127);
    Joystick[STICK].setYAxisRange(-127, 127);
    //Pedals
    Joystick[PED].setXAxisRange(-127, 127);
    Joystick[PED].setYAxisRange(-127, 127);
    Joystick[PED].setRudderRange(-127, 127);
    if(serialDebug == 0)
    {
      Joystick[STICK].begin();
      Joystick[PED].begin();
    }
  }
  //Stick only
  else
  {
    //Stick
    Joystick[STICK].setXAxisRange(-127, 127);
    Joystick[STICK].setYAxisRange(-127, 127);
    if(serialDebug == 0)
    {
      Joystick[STICK].begin();
    }
  }

}

void loop() {
  // put your main code here, to run repeatedly:
  getData(devices);
  if(serialDebug == 1)
  {
    uint8_t val = 0;
    Serial.print("Analogs: \n");
    Serial.print("Stick X: ");
    Serial.print(deviceInputs.getStick_X());
    Serial.print(" Stick Y: ");
    Serial.print(deviceInputs.getStick_Y());
    Serial.print(" Hat: ");
    Serial.print(deviceInputs.getHat());
    Serial.print(" Mouse X: ");
    Serial.print(deviceInputs.getMouse_X());
    Serial.print(" Mouse Y: ");
    Serial.print(deviceInputs.getMouse_Y());
    Serial.print(" Brake R: ");
    Serial.print(deviceInputs.getBrake_R());
    Serial.print(" Brake L: ");
    Serial.print(deviceInputs.getBrake_L());
    Serial.print(" Rudder: ");
    Serial.print(deviceInputs.getRudder());
    Serial.print(" Throttle: ");
    Serial.print(deviceInputs.getThrottle());
    Serial.print(" Encoder: ");
    Serial.print(deviceInputs.getEncoder());
    Serial.print("\n");
    Serial.print("Buttons: ");
    for (uint8_t i = 0; i < 8; i++)
      {
        val = (deviceInputs.getButtons1() >> i) & 0x01;
        Serial.print(val);
        Serial.print(",");
      }
    for (uint8_t i = 0; i < 8; i++)
      {
        val = (deviceInputs.getButtons2() >> i) & 0x01;
        Serial.print(val);
        Serial.print(",");
      }
    Serial.print("\n");
    delay(500);
  }

}

void getData(uint8_t devices)
{
  deviceInputs.getInputData(devices);
  //Full HOTAS
  if(devices == 1)
  {
    //Stick
    Joystick[STICK].setYAxis(deviceInputs.getStick_Y());
    Joystick[STICK].setXAxis(deviceInputs.getStick_X());
    Joystick[STICK].setHatSwitch(0, deviceInputs.getHat());
    Joystick[STICK].setButton(0, (deviceInputs.getButtons2() >> 2) & 0x01);
    Joystick[STICK].setButton(1, (deviceInputs.getButtons2() >> 3) & 0x01);
    Joystick[STICK].setButton(2, (deviceInputs.getButtons2() >> 4) & 0x01);
    Joystick[STICK].setButton(3, (deviceInputs.getButtons2() >> 5) & 0x01);
    //Throttle
    Joystick[THR].setXAxis(deviceInputs.getMouse_X());
    Joystick[THR].setYAxis(deviceInputs.getMouse_Y());
    Joystick[THR].setZAxis(deviceInputs.getEncoder());
    Joystick[THR].setThrottle(deviceInputs.getThrottle());
    for (uint8_t i = 0; i < 8; i++)
    {
      Joystick[THR].setButton(i, (deviceInputs.getButtons1() >> i) & 0x01);
    }
    for (uint8_t i = 0; i < 3; i++)
    {
      Joystick[THR].setButton((i + 8), (deviceInputs.getButtons2() >> i) & 0x01);
    }
    //Pedals
    Joystick[PED].setRxAxis(deviceInputs.getBrake_R());
    Joystick[PED].setRyAxis(deviceInputs.getBrake_L());
    Joystick[PED].setRudder(deviceInputs.getRudder());
  }
  //Throttle only
  else if(devices  == 2)
  {
    //Stick
    Joystick[STICK].setYAxis(deviceInputs.getStick_Y());
    Joystick[STICK].setXAxis(deviceInputs.getStick_X());
    Joystick[STICK].setHatSwitch(0, deviceInputs.getHat());
    Joystick[STICK].setButton(0, (deviceInputs.getButtons2() >> 2) & 0x01);
    Joystick[STICK].setButton(1, (deviceInputs.getButtons2() >> 3) & 0x01);
    Joystick[STICK].setButton(2, (deviceInputs.getButtons2() >> 4) & 0x01);
    Joystick[STICK].setButton(3, (deviceInputs.getButtons2() >> 5) & 0x01);
    //Throttle
    Joystick[THR].setXAxis(deviceInputs.getMouse_X());
    Joystick[THR].setYAxis(deviceInputs.getMouse_Y());
    Joystick[THR].setZAxis(deviceInputs.getEncoder());
    Joystick[THR].setThrottle(deviceInputs.getThrottle());
    for (uint8_t i = 0; i < 8; i++)
    {
      Joystick[THR].setButton(i, (deviceInputs.getButtons1() >> i) & 0x01);
    }
    for (uint8_t i = 0; i < 3; i++)
    {
      Joystick[THR].setButton((i + 8), (deviceInputs.getButtons2() >> i) & 0x01);
    }
  }
  //Pedals only
  else if(devices == 3)
  {
    //Stick
    Joystick[STICK].setYAxis(deviceInputs.getStick_Y());
    Joystick[STICK].setXAxis(deviceInputs.getStick_X());
    Joystick[STICK].setHatSwitch(0, deviceInputs.getHat());
    Joystick[STICK].setButton(0, (deviceInputs.getButtons2() >> 2) & 0x01);
    Joystick[STICK].setButton(1, (deviceInputs.getButtons2() >> 3) & 0x01);
    Joystick[STICK].setButton(2, (deviceInputs.getButtons2() >> 4) & 0x01);
    Joystick[STICK].setButton(3, (deviceInputs.getButtons2() >> 5) & 0x01);
    //Pedals
    Joystick[PED].setRxAxis(deviceInputs.getBrake_R());
    Joystick[PED].setRyAxis(deviceInputs.getBrake_L());
    Joystick[PED].setRudder(deviceInputs.getRudder());
  }
  else
  {
    //Stick
    Joystick[STICK].setYAxis(deviceInputs.getStick_Y());
    Joystick[STICK].setXAxis(deviceInputs.getStick_X());
    Joystick[STICK].setHatSwitch(0, deviceInputs.getHat());
    Joystick[STICK].setButton(0, (deviceInputs.getButtons2() >> 2) & 0x01);
    Joystick[STICK].setButton(1, (deviceInputs.getButtons2() >> 3) & 0x01);
    Joystick[STICK].setButton(2, (deviceInputs.getButtons2() >> 4) & 0x01);
    Joystick[STICK].setButton(3, (deviceInputs.getButtons2() >> 5) & 0x01);
  }

  //delay(10);
}

inputs.cpp

#include "inputs.h"

i2c comms;
inputs::inputs(void)
{
    Stick_X = 0;
    Stick_Y = 0;
    Mouse_X = 0;
    Mouse_Y = 0;
    Throttle = 0;
    Rudder = 0;
    Brake_R = 0;
    Brake_L = 0;
    Encoder = 0;
    //HAT
    HAT = -1;
    //Buttons
    Buttons[0] = 0;
    Buttons[1] = 0;
}
int inputs::resolveHat(int HatY, int HatX)
{
        //Right
        if(HatX > 50 && HatY < 50 && HatY > -40)
        {
            return 90;
        }
        //Left
        else if(HatX < -50 && HatY < 50 && HatY > -40)
        {
            return 270;
        }
        //Up
        else if(HatY > 50 && HatX < 50 && HatX > -50)
        {
            return 0;
        }
        //Down
        else if(HatY < -40 && HatX < 50 && HatX > -50)
        {
            return 180;
        }
        //Up & Right
        else if(HatY > 50 && HatX > 50)
        {
            return 45;
        }
        //Down & Right
        else if(HatY < -40 && HatX > 50)
        {
            return 135;
        }
        //Down & Left
        else if(HatY < -40 && HatX < -50)
        {
            return 225;
        }
        //Up & Left
        else if(HatY > 50 && HatX < -50)
        {
            return 315;
        }
        //Center
        else
        {
            return -1;
        }
}
void inputs::getInputData(uint8_t devices)
{
    uint8_t throttleData[6] = {0,0,0,0,0,0};
    uint8_t pedalsData[3] = {0,0,0};
    //Full HOTAS
    if(devices == 1)
    {
        //Get Throttle
        comms.getDeviceData(THROTTLE, 6, throttleData);
        Throttle = map(throttleData[0], 0, 255, -127, 127);
        Mouse_X = map(throttleData[1], 0, 255, -127, 127);
        Mouse_Y = map(throttleData[2], 0, 255, -127, 127);
        Encoder = map(throttleData[3], 0, 255, -127, 127);
        //Buttons
        Buttons[0] = throttleData[4];
        Buttons[1] = throttleData[5];
        //Get Pedals
        comms.getDeviceData(PEDALS, 3, pedalsData);
        Rudder = map(pedalsData[0], 0, 255, -127, 127);
        Brake_R = map(pedalsData[1], 0, 255, -127, 127);
        Brake_L = map(pedalsData[2], 0, 255, -127, 127);
    }
    //Throttle only
    else if(devices == 2)
    {
        comms.getDeviceData(THROTTLE, 6, throttleData);
        Throttle = map(throttleData[0], 0, 255, -127, 127);
        Mouse_X = map(throttleData[1], 0, 255, -127, 127);
        Mouse_Y = map(throttleData[2], 0, 255, -127, 127);
        Encoder = map(throttleData[3], 0, 255, -127, 127);
        //Buttons
        Buttons[0] = throttleData[4];
        Buttons[1] = throttleData[5];
    }
    //Pedals only
    else if(devices == 3)
    {
        comms.getDeviceData(PEDALS, 3, pedalsData);
        Rudder = map(pedalsData[0], 0, 255, -127, 127);
        Brake_R = map(pedalsData[1], 0, 255, -127, 127);
        Brake_L = map(pedalsData[2], 0, 255, -127, 127);
    }
    Stick_X = map(analogRead(STICK_X),0,1024, 127, -127);
    Stick_Y = map(analogRead(STICK_Y),0,1024, 127, -127);
    HAT = resolveHat(map(analogRead(HAT_Y),0,1024,-127,127),map(analogRead(HAT_X),0,1024,-127,127));
    if(digitalRead(TRIGGER))
    {
        Buttons[1] &= ~(1<<2); //Clear bit 0  
    }
    else
    {
        Buttons[1] |= (1<<2); //Set bit 1
    }
    if(digitalRead(BTN0))
    {
        Buttons[1] &= ~(1<<3); //Clear bit 0 
    }
    else
    {
        Buttons[1] |= (1<<3); //Set bit 1
    }

    if(digitalRead(BTN1))
    {
        Buttons[1] &= ~(1<<4); //Clear bit 0 
    }
    else
    {
        Buttons[1] |= (1<<4); //Set bit 1
    }
    if(digitalRead(BTN2))
    {
        Buttons[1] &= ~(1<<5); //Clear bit 0
    }
    else
    {
        Buttons[1] |= (1<<5); //Set bit 1

    }
}

inputs.h

#ifndef INPUTS_H_
#define INPUTS_H_
#include <Arduino.h>
#include "i2c.h"

/*
Button bit order
//Throttle
buttons[0] bit 0 hatbtn0
buttons[0] bit 1 hatbtn1
buttons[0] bit 2 pushbtn0
buttons[0] bit 3 pushbtn1
buttons[0] bit 4 pushbtn2
buttons[0] bit 5 pushbtn3
buttons[0] bit 6 Switch0
buttons[0] bit 7 Switch1
buttons[1] bit 0 Switch2
buttons[1] bit 1 Switch3
//Stick
buttons[1] bit 2 Trigger
buttons[1] bit 3 Stickbtn0
buttons[1] bit 4 Stickbtn1
buttons[1] bit 5 Stickbtn2
*/
class inputs
{
private:
    //Analogs
    int Stick_X;
    int Stick_Y;
    int Mouse_X;
    int Mouse_Y;
    int Throttle;
    int Rudder;
    int Brake_R;
    int Brake_L;
    int Encoder;
    //HAT
    int HAT;
    //Buttons
    uint8_t Buttons[2];
    //Functions
    int resolveHat(int HatY, int HatX);
public:
    inputs();
    //Functions
    void getInputData(uint8_t devices);
    int getStick_X(void)
    {
        return Stick_X;
    };
    int getStick_Y(void)
    {
        return Stick_Y;
    };
    int getMouse_X(void)
    {
        return Mouse_X;
    };
    int getMouse_Y(void)
    {
        return Mouse_Y;
    };
    int getThrottle(void)
    {
        return Throttle;
    };
    int getRudder(void)
    {
        return Rudder;
    };
    int getBrake_R(void)
    {
        return Brake_R;
    };
    int getBrake_L(void)
    {
        return Brake_L;
    };
    int getEncoder(void)
    {
        return Encoder;
    };
    int getHat(void)
    {
        return HAT;
    };
    uint8_t getButtons1(void)
    {
        return Buttons[0];
    }
    uint8_t getButtons2(void)
    {
        return Buttons[1];
    }

};

#endif

i2c.cpp

#include "i2c.h"

void i2c::init(void)
{
    Wire.begin();
}

int i2c::detectDevices(void)
{
  int8_t error;
  uint8_t Throttle = 0;
  uint8_t Pedals = 0;
  //uint8_t connectedDevices[3] = {0,0,0};
  //bool deviceFound = false;
  //Find Throttle
  #ifdef DEBUG_SERIAL
  Serial.print("Quering device 0x");
  Serial.print(THROTTLE);
  Serial.print("\n");
  #endif
  Wire.beginTransmission(THROTTLE);
  error = Wire.endTransmission();
  if (error == 0)
    {
      #ifdef DEBUG_SERIAL
      Serial.print("I2C device found at address 0x");
      Serial.print(THROTTLE);
      Serial.print("\n");
      #endif
      Throttle = 1;
    }
  else if (error == 4)
    {
      #ifdef DEBUG_SERIAL
      Serial.print("Unknown error at address 0x");
      Serial.print(THROTTLE);
      Serial.print("\n");
      #endif
      return -1;
    }
  else
  {
    #ifdef DEBUG_SERIAL
    Serial.print("No device found at address 0x");
    Serial.print(THROTTLE);
    Serial.print("\n");
    #endif
  }
  //Find pedals
  #ifdef DEBUG_SERIAL
  Serial.print("Quering device 0x");
  Serial.print(PEDALS);
  Serial.print("\n");
  #endif
  Wire.beginTransmission(PEDALS);
  error = Wire.endTransmission();
  if (error == 0)
    {
      #ifdef DEBUG_SERIAL
      Serial.print("I2C device found at address 0x");
      Serial.print(PEDALS);
      Serial.print("\n");
      #endif
      Pedals = 1;
    }
  else if (error == 4)
    {
      #ifdef DEBUG_SERIAL
      Serial.print("Unknown error at address 0x");
      Serial.print(PEDALS);
      Serial.print("\n");
      #endif
      return -2;
    }
  else
  {
    #ifdef DEBUG_SERIAL
    Serial.print("No device found at address 0x");
    Serial.print(PEDALS);
    Serial.print("\n");
    #endif
  }
  if(Throttle == 1 && Pedals == 1)
  {
    #ifdef DEBUG_SERIAL
    Serial.print("Set Full HOTAS \n");
    #endif
    return 1;
  }
  else if(Throttle == 1 && Pedals == 0)
  {
    #ifdef DEBUG_SERIAL
    Serial.print("Set Throttle only \n");
    #endif
    return 2;
  }
  else if(Throttle == 0 && Pedals == 1)
  {
    #ifdef DEBUG_SERIAL
    Serial.print("Set Pedals only \n");
    #endif
    return 3;
  }
  else
  {
    return 0;
  }
}

int i2c::getDeviceData(int address, int len, uint8_t *data)
{
    int c = 0;
    Wire.requestFrom(address, len);
    while (Wire.available()) 
    { // peripheral may send less than requested
        data[c] = Wire.read(); // receive a byte as character
        c++;
    }
    return c;
}

i2c.h

#ifndef I2C_H_
#define I2C_H_

#include <Arduino.h>
#include <Wire.h>
#include "config.h"

class i2c
{

public:
    void init(void);
    int detectDevices(void);
    int getDeviceData(int address, int len, uint8_t *data);
};

#endif

main.cpp of the pedals

#include <Arduino.h>
#include <Wire.h>

#define RUDDER      A3
#define PEDALRIGHT  A2
#define PEDALLEFT   A1

#define DEVICEADDR  0x3b

uint8_t inputValues[3] = {0,0,0};

void getInputValues(void)
{
  //Rudder
  inputValues[0] = map(analogRead(RUDDER),0,1024,0,255);
  //Right pedal
  inputValues[1] = map(analogRead(PEDALRIGHT),0,1024,0,255);
  //Left pedal
  inputValues[2] = map(analogRead(PEDALLEFT),0,1024,0,255);
}
void requestHandler(void)
{
  Wire.write(inputValues, 3);
}

void setup() {
  pinMode(RUDDER, INPUT);
  pinMode(PEDALRIGHT, INPUT);
  pinMode(PEDALLEFT, INPUT);
  Wire.begin(DEVICEADDR);                // join i2c bus
  Wire.onRequest(requestHandler); // register event
}

void loop() {
  getInputValues();
}

Additional context

I did not add the Arduino pro mini code for the throttle, because this post is way too long already and the code is besically the same as the pedals code. It just polls constantly the ADC's and button pins and responds on the I2C request interrupt by the master (Arduino micro). I can add the code if some one sees it necessary, but because everything works fine with the throttle by it self and in the serial debug mode, I don't think the code for the throttle is relevant for this issue.

Also I don't think it's wiring or hardware issue eihter, because everything works in serial debug mode just fine and consistently. I also have tried different hardware for the pedals with no luck. I am also ruling out power issue, because the bedals don't work with the stick and they have only three 10k potentiometers as the throttle has also three 10k pontentiometers, 4 LED's 10 swithces and a rotary encoder, so it should draw way more power than the pedals and it works with the stick. I am also using UGreen 2A USB cables for the cables between the devices and the shielded USB signal lines for the I2C wires.

I don't know anymore what to try and to do to get this working. Please help me.