USB/Bluetooth PS4 controller not connecting with USB Host Shield 2.0

I’m trying to get a 3rd party PS4 controller to connect to my arduino
over bluetooth with the USB host shield and a bluetooth dongle.

When I run the example code from the library with the hardware
configured for bluetooth (bluetooth dongle plugged in and trying to
pair with the share+ps4 button) it won’t connect. When I connect it to
the usb port with a microusb port, I believe I am able to connect to
the controller, but nothing prints in the serial monitor when I push
any buttons. I ran the qc test code in the library and that came back
with the usb shield working as intended.

I added this line of code to see if the controller was even connecting over usb.

if (PS4.connected()) {
Serial.print(F(“r\nConnected”));
}

and it does indeed print connected if I plug it up and stops printing
if I unplug it. I just don’t ever get any other of the example commands
to print if I press any buttons.

I would really appreciate help!

You have a lot of moving pieces in your project. I would suggest testing them separately to figure out if one piece isn’t working.
You can test the Bluetooth dongle and PS4 controller with a PC to check if they work.
Then test the USB host shield with another device like a USB flash drive or wired USB keyboard.
Difficult to suggest anything else without seeing your code.

I did remove the bluetooth dongle from my testing so I could reduce variables like you suggested. I wasn’t able to get any device to work with the usb host shield, but the quality control example code didn’t return any errors. I am able to at least get power from the usb port because the controller indicates that its charging when I plug it up to the host shield. When I connect the ps4 controller over usb, I think I am able to connect with the controller because I add this if loop to see if it even connects and it does indeed print connected to the serial monitor albeit inconsistently. When the code does print, if I connect the controller it prints, and if I unplug the controller while running the code, the message doesn’t print. If I reconnect the controller it starts printing again.

if (PS4.connected()) {
  Serial.print("\r\nConnected")
}

Here is the code for the QC test.

/* USB Host Shield 2.0 board quality control routine */
/* To see the output set your terminal speed to 115200 */
/* for GPIO test to pass you need to connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, etc. */
/* otherwise press any key after getting GPIO error to complete the test */
/**/
#include <usbhub.h>

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library
#endif
#include <SPI.h> // Hack to use the SPI library

/* variables */
uint8_t rcode;
uint8_t usbstate;
uint8_t laststate;
//uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
USB_DEVICE_DESCRIPTOR buf;

/* objects */
USB Usb;
//USBHub hub(&Usb);

void setup() {
        laststate = 0;
        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
        E_Notify(PSTR("\r\nCircuits At Home 2011"), 0x80);
        E_Notify(PSTR("\r\nUSB Host Shield Quality Control Routine"), 0x80);
        /* SPI quick test - check revision register */
        E_Notify(PSTR("\r\nReading REVISION register... Die revision "), 0x80);
        Usb.Init(); // Initializes SPI, we don't care about the return value here
        {
                uint8_t tmpbyte = Usb.regRd(rREVISION);
                switch(tmpbyte) {
                        case( 0x01): //rev.01
                                E_Notify(PSTR("01"), 0x80);
                                break;
                        case( 0x12): //rev.02
                                E_Notify(PSTR("02"), 0x80);
                                break;
                        case( 0x13): //rev.03
                                E_Notify(PSTR("03"), 0x80);
                                break;
                        default:
                                E_Notify(PSTR("invalid. Value returned: "), 0x80);
                                print_hex(tmpbyte, 8);
                                halt55();
                                break;
                }//switch( tmpbyte...
        }//check revision register
        /* SPI long test */
        {
                E_Notify(PSTR("\r\nSPI long test. Transfers 1MB of data. Each dot is 64K"), 0x80);
                uint8_t sample_wr = 0;
                uint8_t sample_rd = 0;
                uint8_t gpinpol_copy = Usb.regRd(rGPINPOL);
                for(uint8_t i = 0; i < 16; i++) {
#ifdef ESP8266
                        yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
                        for(uint16_t j = 0; j < 65535; j++) {
                                Usb.regWr(rGPINPOL, sample_wr);
                                sample_rd = Usb.regRd(rGPINPOL);
                                if(sample_rd != sample_wr) {
                                        E_Notify(PSTR("\r\nTest failed.  "), 0x80);
                                        E_Notify(PSTR("Value written: "), 0x80);
                                        print_hex(sample_wr, 8);
                                        E_Notify(PSTR(" read: "), 0x80);
                                        print_hex(sample_rd, 8);
                                        halt55();
                                }//if( sample_rd != sample_wr..
                                sample_wr++;
                        }//for( uint16_t j...
                        E_Notify(PSTR("."), 0x80);
                }//for( uint8_t i...
                Usb.regWr(rGPINPOL, gpinpol_copy);
                E_Notify(PSTR(" SPI long test passed"), 0x80);
        }//SPI long test
        /* GPIO test */
        /* in order to simplify board layout, GPIN pins on text fixture are connected to GPOUT */
        /* in reverse order, i.e, GPIN0 is connected to GPOUT7, GPIN1 to GPOUT6, etc. */
        {
                uint8_t tmpbyte;
                E_Notify(PSTR("\r\nGPIO test. Connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, and so on"), 0x80);
                for(uint8_t sample_gpio = 0; sample_gpio < 255; sample_gpio++) {
#ifdef ESP8266
                        yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
                        Usb.gpioWr(sample_gpio);
                        tmpbyte = Usb.gpioRd();
                        /* bit reversing code copied vetbatim from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious */
                        tmpbyte = ((tmpbyte * 0x0802LU & 0x22110LU) | (tmpbyte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
                        if(sample_gpio != tmpbyte) {
                                E_Notify(PSTR("\r\nTest failed. Value written: "), 0x80);
                                print_hex(sample_gpio, 8);
                                E_Notify(PSTR(" Value read: "), 0x80);
                                print_hex(tmpbyte, 8);
                                E_Notify(PSTR(" "), 0x80);
                                press_any_key();
                                break;
                        }//if( sample_gpio != tmpbyte...
                }//for( uint8_t sample_gpio...
                E_Notify(PSTR("\r\nGPIO test passed."), 0x80);
        }//GPIO test
        /* PLL test. Stops/starts MAX3421E oscillator several times */
        {
                E_Notify(PSTR("\r\nPLL test. 100 chip resets will be performed"), 0x80);
                /* check current state of the oscillator */
                if(!(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ)) { //wrong state - should be on
                        E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80);
                        press_any_key();
                }
                /* Restart oscillator */
                E_Notify(PSTR("\r\nResetting oscillator\r\n"), 0x80);
                for(uint16_t i = 0; i < 100; i++) {
#ifdef ESP8266
                        yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
                        E_Notify(PSTR("\rReset number "), 0x80);
                        Serial.print(i, DEC);
                        Usb.regWr(rUSBCTL, bmCHIPRES); //reset
                        if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) { //wrong state - should be off
                                E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80);
                                halt55();
                        }
                        Usb.regWr(rUSBCTL, 0x00); //release from reset
                        uint16_t j = 0;
                        for(j = 1; j < 65535; j++) { //tracking off to on time
                                if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) {
                                        E_Notify(PSTR(" Time to stabilize - "), 0x80);
                                        Serial.print(j, DEC);
                                        E_Notify(PSTR(" cycles\r\n"), 0x80);
                                        break;
                                }
                        }//for( uint16_t j = 0; j < 65535; j++
                        if(j == 0) {
                                E_Notify(PSTR("PLL failed to stabilize"), 0x80);
                                press_any_key();
                        }
                }//for( uint8_t i = 0; i < 255; i++

        }//PLL test
        /* initializing USB stack */
        if(Usb.Init() == -1) {
                E_Notify(PSTR("\r\nOSCOKIRQ failed to assert"), 0x80);
                halt55();
        }
        E_Notify(PSTR("\r\nChecking USB device communication.\r\n"), 0x80);
}

void loop() {
        delay(200);
        Usb.Task();
        usbstate = Usb.getUsbTaskState();
        if(usbstate != laststate) {
                laststate = usbstate;
                /**/
                switch(usbstate) {
                        case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE):
                                E_Notify(PSTR("\r\nWaiting for device..."), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_RESET_DEVICE):
                                E_Notify(PSTR("\r\nDevice connected. Resetting..."), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_WAIT_SOF):
                                E_Notify(PSTR("\r\nReset complete. Waiting for the first SOF..."), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE):
                                E_Notify(PSTR("\r\nSOF generation started. Enumerating device..."), 0x80);
                                break;
                        case( USB_STATE_ADDRESSING):
                                E_Notify(PSTR("\r\nSetting device address..."), 0x80);
                                break;
                        case( USB_STATE_RUNNING):
                                E_Notify(PSTR("\r\nGetting device descriptor"), 0x80);
                                rcode = Usb.getDevDescr(1, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*) & buf);

                                if(rcode) {
                                        E_Notify(PSTR("\r\nError reading device descriptor. Error code "), 0x80);
                                        print_hex(rcode, 8);
                                } else {
                                        /**/
                                        E_Notify(PSTR("\r\nDescriptor Length:\t"), 0x80);
                                        print_hex(buf.bLength, 8);
                                        E_Notify(PSTR("\r\nDescriptor type:\t"), 0x80);
                                        print_hex(buf.bDescriptorType, 8);
                                        E_Notify(PSTR("\r\nUSB version:\t\t"), 0x80);
                                        print_hex(buf.bcdUSB, 16);
                                        E_Notify(PSTR("\r\nDevice class:\t\t"), 0x80);
                                        print_hex(buf.bDeviceClass, 8);
                                        E_Notify(PSTR("\r\nDevice Subclass:\t"), 0x80);
                                        print_hex(buf.bDeviceSubClass, 8);
                                        E_Notify(PSTR("\r\nDevice Protocol:\t"), 0x80);
                                        print_hex(buf.bDeviceProtocol, 8);
                                        E_Notify(PSTR("\r\nMax.packet size:\t"), 0x80);
                                        print_hex(buf.bMaxPacketSize0, 8);
                                        E_Notify(PSTR("\r\nVendor  ID:\t\t"), 0x80);
                                        print_hex(buf.idVendor, 16);
                                        E_Notify(PSTR("\r\nProduct ID:\t\t"), 0x80);
                                        print_hex(buf.idProduct, 16);
                                        E_Notify(PSTR("\r\nRevision ID:\t\t"), 0x80);
                                        print_hex(buf.bcdDevice, 16);
                                        E_Notify(PSTR("\r\nMfg.string index:\t"), 0x80);
                                        print_hex(buf.iManufacturer, 8);
                                        E_Notify(PSTR("\r\nProd.string index:\t"), 0x80);
                                        print_hex(buf.iProduct, 8);
                                        E_Notify(PSTR("\r\nSerial number index:\t"), 0x80);
                                        print_hex(buf.iSerialNumber, 8);
                                        E_Notify(PSTR("\r\nNumber of conf.:\t"), 0x80);
                                        print_hex(buf.bNumConfigurations, 8);
                                        /**/
                                        E_Notify(PSTR("\r\n\nAll tests passed. Press RESET to restart test"), 0x80);
                                        while(1) {
#ifdef ESP8266
                                                yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
                                        }
                                }
                                break;
                        case( USB_STATE_ERROR):
                                E_Notify(PSTR("\r\nUSB state machine reached error state"), 0x80);
                                break;

                        default:
                                break;
                }//switch( usbstate...
        }
}//loop()...

/* constantly transmits 0x55 via SPI to aid probing */
void halt55() {

        E_Notify(PSTR("\r\nUnrecoverable error - test halted!!"), 0x80);
        E_Notify(PSTR("\r\n0x55 pattern is transmitted via SPI"), 0x80);
        E_Notify(PSTR("\r\nPress RESET to restart test"), 0x80);

        while(1) {
                Usb.regWr(0x55, 0x55);
#ifdef ESP8266
                yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
        }
}

/* prints hex numbers with leading zeroes */
void print_hex(int v, int num_places) {
        int mask = 0, n, num_nibbles, digit;

        for(n = 1; n <= num_places; n++) {
                mask = (mask << 1) | 0x0001;
        }
        v = v & mask; // truncate v to specified number of places

        num_nibbles = num_places / 4;
        if((num_places % 4) != 0) {
                ++num_nibbles;
        }
        do {
                digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f;
                Serial.print(digit, HEX);
        } while(--num_nibbles);
}

/* prints "Press any key" and returns when key is pressed */
void press_any_key() {
        E_Notify(PSTR("\r\nPress any key to continue..."), 0x80);
        while(Serial.available() <= 0) { // wait for input
#ifdef ESP8266
                yield(); // needed in order to reset the watchdog timer on the ESP8266
#endif
        }
        Serial.read(); //empty input buffer
        return;
}

Here is the code for the USB PS4 controller.

/*
 Example sketch for the PS4 USB library - developed by Kristian Lauszus
 For more information visit my blog: http://blog.tkjelectronics.dk/ or
 send me an e-mail:  [email protected]
 */

#include <PS4USB.h>

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

USB Usb;
PS4USB PS4(&Usb);

bool printAngle, printTouch;
uint8_t oldL2Value, oldR2Value;

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
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); // Halt
  }
  Serial.print(F("\r\nPS4 USB Library Started"));
}

void loop() {
  Usb.Task();

  if (PS4.connected()) {
    if (PS4.getAnalogHat(LeftHatX) > 137 || PS4.getAnalogHat(LeftHatX) < 117 || PS4.getAnalogHat(LeftHatY) > 137 || PS4.getAnalogHat(LeftHatY) < 117 || PS4.getAnalogHat(RightHatX) > 137 || PS4.getAnalogHat(RightHatX) < 117 || PS4.getAnalogHat(RightHatY) > 137 || PS4.getAnalogHat(RightHatY) < 117) {
      Serial.print(F("\r\nLeftHatX: "));
      Serial.print(PS4.getAnalogHat(LeftHatX));
      Serial.print(F("\tLeftHatY: "));
      Serial.print(PS4.getAnalogHat(LeftHatY));
      Serial.print(F("\tRightHatX: "));
      Serial.print(PS4.getAnalogHat(RightHatX));
      Serial.print(F("\tRightHatY: "));
      Serial.print(PS4.getAnalogHat(RightHatY));
    }

    if (PS4.getAnalogButton(L2) || PS4.getAnalogButton(R2)) { // These are the only analog buttons on the PS4 controller
      Serial.print(F("\r\nL2: "));
      Serial.print(PS4.getAnalogButton(L2));
      Serial.print(F("\tR2: "));
      Serial.print(PS4.getAnalogButton(R2));
    }
    if (PS4.getAnalogButton(L2) != oldL2Value || PS4.getAnalogButton(R2) != oldR2Value) // Only write value if it's different
      PS4.setRumbleOn(PS4.getAnalogButton(L2), PS4.getAnalogButton(R2));
    oldL2Value = PS4.getAnalogButton(L2);
    oldR2Value = PS4.getAnalogButton(R2);

    if (PS4.getButtonClick(PS))
      Serial.print(F("\r\nPS"));
    if (PS4.getButtonClick(TRIANGLE)) {
      Serial.print(F("\r\nTriangle"));
      PS4.setRumbleOn(RumbleLow);
    }
    if (PS4.getButtonClick(CIRCLE)) {
      Serial.print(F("\r\nCircle"));
      PS4.setRumbleOn(RumbleHigh);
    }
    if (PS4.getButtonClick(CROSS)) {
      Serial.print(F("\r\nCross"));
      PS4.setLedFlash(10, 10); // Set it to blink rapidly
    }
    if (PS4.getButtonClick(SQUARE)) {
      Serial.print(F("\r\nSquare"));
      PS4.setLedFlash(0, 0); // Turn off blinking
    }

    if (PS4.getButtonClick(UP)) {
      Serial.print(F("\r\nUp"));
      PS4.setLed(Red);
    } if (PS4.getButtonClick(RIGHT)) {
      Serial.print(F("\r\nRight"));
      PS4.setLed(Blue);
    } if (PS4.getButtonClick(DOWN)) {
      Serial.print(F("\r\nDown"));
      PS4.setLed(Yellow);
    } if (PS4.getButtonClick(LEFT)) {
      Serial.print(F("\r\nLeft"));
      PS4.setLed(Green);
    }

    if (PS4.getButtonClick(L1))
      Serial.print(F("\r\nL1"));
    if (PS4.getButtonClick(L3))
      Serial.print(F("\r\nL3"));
    if (PS4.getButtonClick(R1))
      Serial.print(F("\r\nR1"));
    if (PS4.getButtonClick(R3))
      Serial.print(F("\r\nR3"));

    if (PS4.getButtonClick(SHARE))
      Serial.print(F("\r\nShare"));
    if (PS4.getButtonClick(OPTIONS)) {
      Serial.print(F("\r\nOptions"));
      printAngle = !printAngle;
    }
    if (PS4.getButtonClick(TOUCHPAD)) {
      Serial.print(F("\r\nTouchpad"));
      printTouch = !printTouch;
    }

    if (printAngle) { // Print angle calculated using the accelerometer only
      Serial.print(F("\r\nPitch: "));
      Serial.print(PS4.getAngle(Pitch));
      Serial.print(F("\tRoll: "));
      Serial.print(PS4.getAngle(Roll));
    }

    if (printTouch) { // Print the x, y coordinates of the touchpad
      if (PS4.isTouching(0) || PS4.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad
        Serial.print(F("\r\n"));
      for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers
        if (PS4.isTouching(i)) { // Print the position of the finger if it is touching the touchpad
          Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": "));
          Serial.print(PS4.getX(i));
          Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": "));
          Serial.print(PS4.getY(i));
          Serial.print(F("\t"));
        }
      }
    }
  }
}

  • Does the PS4 controller work connected to a PC? over USB and Bluetooth?
  • The QC code looks like it’s testing the interface between the arduino and shield, so something might still be broken on the USB side of the shield?
  • This doesn’t answer your Q - but I am curious why you aren’t using an ESP32 which has built-in USB?
  • Are you planning to be at the space this weekend? I can bring my ESP32 for a quick test.

I didn’t try connecting the ps4 controller to my PC over bluetooth, but it does work when I plug it up via USB. I can come in tomorrow if there’s a time that works for you. I am pretty new to hobby electronics, and I have a little arduino experience and my understanding is that arduino is the gold standard for hobby projects.

I am planning to go at 2:30 pm I’ll bring my ESP32

@mandarlimaye I just arrived at DMS and I’ll be in the electronics section if you are already here!