Success with Bluepad32 (RP2040 Connect to Xbox One controller)

I wanted to report that I was successful in loading the Nina version of "Bluepad32" game controller firmware and utilizing the Nina module as a link to an Xbox One controller over Bluetooth. I did this per Bluepad32's instructions, which involved loading a temporary sketch on the RP2040 to pass traffic from the firmware update utility to the Nina module, overwriting the stock Nina firmware.

The project I used it for didn't need WiFi or any other wireless communication, so I haven't tested whether these work while this firmware is loaded. Treating the Nina module as a dedicated link to the game controller fit my needs perfectly.

1 Like

Hey! I was wondering if you could share your code for reference? I'm trying to get it to connect to my Xbox controller and am having issues, Thanks!

Yes can you please share, I am starting this project myself and all info would be very helpful

Yeah here was the working code. I borrowed examples off the internet and didn't bother to even change the comments, so the comments generally aren't mine. I used this sketch to drive a robot toy using a Mindsensors shield for driving Lego motors (this shield also includes the ILI9341 screen). You can strip out all that code and print the joystick values to the serial (or, you could probably just find the original example I based this on, which probably does precisely that). Reminder, you must flash the Nina module first -- this requires loading a stub sketch to the Nano Connect so that you can use "esptool" to flash the Gamepad firmware to the Nina module -- the instructions for that are available elsewhere.

// Copyright 2021 - 2022, Ricardo Quesada
// SPDX-License-Identifier: Apache 2.0 or LGPL-2.1-or-later

/*
 * This example shows how to use the Gamepad API.
 *
 * Supported on boards with NINA W10x like:
 *  - Arduino MKR WiFi 1010,
 *  - UNO WiFi Rev.2,
 *  - Nano RP2040 Connect,
 *  - Nano 33 IoT,
 *  - etc.
 */
#include <Bluepad32.h>
#include <Wire.h>
#include <EVShield.h>
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

// For the Adafruit shield, these are the default.
#define TFT_DC 8
#define TFT_CS 7

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// setup for this example:
// attach external power to EVShield.
// attach 4 motors to motor ports 
// Open the Serial terminal to view.

EVShield          evshield(0x34,0x36);

GamepadPtr myGamepads[BP32_MAX_GAMEPADS] = {};

void setup() {
  // Initialize serial
  Serial.begin(115200);



    delay(2000);                // wait two seconds, allowing time to
                                // activate the serial monitor

    long            rotations = 2;  // Define variable rotations and set
                                    // equal to 90
    char            str[40];

   tft.begin();
   tft.setRotation(3);

  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
  tft.println("The Mindsensors shield isn't on.");
  tft.println("Make sure the batteries are all in");
  tft.print("Press the GO button then reset");


  String fv = BP32.firmwareVersion();
  Serial.print("Firmware version installed: ");
  Serial.println(fv);


    evshield.init( SH_HardwareI2C );
    Wire.setClock(10000);


  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);


  evshield.bank_a.motorStop(SH_Motor_1, SH_Next_Action_Float);
  evshield.bank_a.motorStop(SH_Motor_2, SH_Next_Action_Float);
  evshield.bank_b .motorStop(SH_Motor_1, SH_Next_Action_Float);
  evshield.bank_b.motorStop(SH_Motor_2, SH_Next_Action_Float);



  // BP32.pinMode(27, OUTPUT);
  // BP32.digitalWrite(27, 0);

  // This call is mandatory. It setups Bluepad32 and creates the callbacks.
  BP32.setup(&onConnectedGamepad, &onDisconnectedGamepad);

  // "forgetBluetoothKeys()" should be called when the user performs
  // a "device factory reset", or similar.
  // Calling "forgetBluetoothKeys" in setup() just as an example.
  // Forgetting Bluetooth keys prevents "paired" gamepads to reconnect.
  // But might also fix some connection / re-connection issues.
  BP32.forgetBluetoothKeys();
}

// This callback gets called any time a new gamepad is connected.
// Up to 4 gamepads can be connected at the same time.
void onConnectedGamepad(GamepadPtr gp) {
  bool foundEmptySlot = false;
  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myGamepads[i] == nullptr) {
      Serial.print("CALLBACK: Gamepad is connected, index=");
      Serial.println(i);
      myGamepads[i] = gp;
      foundEmptySlot = true;

      // Optional, once the gamepad is connected, request further info about the
      // gamepad.
      GamepadProperties properties = gp->getProperties();
      char buf[80];
      sprintf(buf,
              "BTAddr: %02x:%02x:%02x:%02x:%02x:%02x, VID/PID: %04x:%04x, "
              "flags: 0x%02x",
              properties.btaddr[0], properties.btaddr[1], properties.btaddr[2],
              properties.btaddr[3], properties.btaddr[4], properties.btaddr[5],
              properties.vendor_id, properties.product_id, properties.flags);
      Serial.println(buf);
      break;
    }
  }
  if (!foundEmptySlot) {
    Serial.println(
        "CALLBACK: Gamepad connected, but could not found empty slot");
  }
}

void onDisconnectedGamepad(GamepadPtr gp) {
  bool foundGamepad = false;

  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myGamepads[i] == gp) {
      Serial.print("CALLBACK: Gamepad is disconnected from index=");
      Serial.println(i);
      myGamepads[i] = nullptr;
      foundGamepad = true;
      break;
    }
  }

  if (!foundGamepad) {
    Serial.println(
        "CALLBACK: Gamepad disconnected, but not found in myGamepads");
  }
}

void loop() {


  static bool blacked=false;
  if (blacked==false) {
    tft.fillScreen(ILI9341_BLACK);
    blacked=true;
  }
  tft.setCursor(0, 0);
  tft.print("               ");
  tft.setCursor(0,0);
  tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
  tft.print("1");























  // This call fetches all the gamepad info from the NINA (ESP32) module.
  // Just call this function in your main loop.
  // The gamepad pointers (the ones received in the callbacks) gets updated
  // automatically.
  BP32.update();

  tft.print("2");

  // It is safe to always do this before using the gamepad API.
  // This guarantees that the gamepad is valid and connected.
  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    GamepadPtr myGamepad = myGamepads[i];

    if (myGamepad && myGamepad->isConnected()) {
      // There are different ways to query whether a button is pressed.
      // By query each button individually:
      //  a(), b(), x(), y(), l1(), etc...
      if (myGamepad->a()) {
        static int colorIdx = 0;
        // Some gamepads like DS4 and DualSense support changing the color LED.
        // It is possible to change it by calling:
        switch (colorIdx % 3) {
          case 0:
            // Red
            myGamepad->setColorLED(255, 0, 0);
            break;
          case 1:
            // Green
            myGamepad->setColorLED(0, 255, 0);
            break;
          case 2:
            // Blue
            myGamepad->setColorLED(0, 0, 255);
            break;
        }
        colorIdx++;
      }

      if (myGamepad->b()) {
        // Turn on the 4 LED. Each bit represents one LED.
        static int led = 0;
        led++;
        // Some gamepads like the DS3, DualSense, Nintendo Wii, Nintendo Switch
        // support changing the "Player LEDs": those 4 LEDs that usually
        // indicate the "gamepad seat". It is possible to change them by
        // calling:
        myGamepad->setPlayerLEDs(led & 0x0f);
      }

      if (myGamepad->x()) {
        // Duration: 255 is ~2 seconds
        // force: intensity
        // Some gamepads like DS3, DS4, DualSense, Switch, Xbox One S support
        // force feedback.
        // It is possible to set it by calling:
        myGamepad->setRumble(0xc0 /* force */, 0x10 /* duration */);
      }

      if (myGamepad->y()) {
        // Disable new gamepad connections
        Serial.println("Bluetooth new connections disabled");
        BP32.enableNewBluetoothConnections(false);


      }

  tft.print("3");


      // Another way to query the buttons, is by calling buttons(), or
      // miscButtons() which return a bitmask.
      // Some gamepads also have DPAD, axis and more.
      char buffer[120];
      snprintf(buffer, sizeof(buffer) - 1,
               "idx=%d, dpad: 0x%02x, buttons: 0x%04x, axis L: %4li, %4li, axis "
               "R: %4li, %4li, brake: %4ld, throttle: %4li, misc: 0x%02x",
               i,                      // Gamepad Index
               myGamepad->dpad(),      // DPAD
               myGamepad->buttons(),   // bitmask of pressed buttons
               myGamepad->axisX(),     // (-511 - 512) left X Axis
               myGamepad->axisY(),     // (-511 - 512) left Y axis
               myGamepad->axisRX(),    // (-511 - 512) right X axis
               myGamepad->axisRY(),    // (-511 - 512) right Y axis
               myGamepad->brake(),     // (0 - 1023): brake button
               myGamepad->throttle(),  // (0 - 1023): throttle (AKA gas) button
               myGamepad->miscButtons()  // bitmak of pressed "misc" buttons
      );

      tft.print("4");

      Serial.println(buffer);
      static bool ledstate=false;
      ledstate = !ledstate;
      //pinMode(LED_BUILTIN, OUTPUT);
      //digitalWrite(LED_BUILTIN, ledstate ? HIGH : LOW);

      int fwdback, absfwdback, fwdbackpct;

      fwdback = myGamepad->axisRY();
      absfwdback = (fwdback < 0) ? -fwdback : fwdback;
      if (absfwdback < 50) absfwdback = 0;
      fwdbackpct = (absfwdback * 100) / 512;
      if (fwdbackpct==0) 
        evshield.bank_a.motorStop(SH_Motor_1, SH_Next_Action_Float);
        else evshield.bank_a.motorRunUnlimited(SH_Motor_1, (fwdback>=0) ? SH_Direction_Forward : SH_Direction_Reverse, fwdbackpct);
        //else evshield.bank_a.motorSetSpeedTimeAndControl(SH_Motor_1, fwdbackpct * ((fwdback>=0) ? 1 : -1, );

      fwdback = myGamepad->axisRX();
      absfwdback = (fwdback < 0) ? -fwdback : fwdback;
      if (absfwdback < 50) absfwdback = 0;
      fwdbackpct = (absfwdback * 100) / 512;
      if (fwdbackpct==0) 
        evshield.bank_b.motorStop(SH_Motor_1, SH_Next_Action_Brake);
        else evshield.bank_b.motorRunUnlimited(SH_Motor_1, (fwdback<0) ? SH_Direction_Forward : SH_Direction_Reverse, fwdbackpct);

      fwdback = myGamepad->axisY();
      absfwdback = (fwdback < 0) ? -fwdback : fwdback;
      if (absfwdback < 50) absfwdback = 0;

      fwdbackpct = (absfwdback * 100) / 512;
      if (fwdbackpct==0) 
        evshield.bank_b.motorStop(SH_Motor_2, SH_Next_Action_Float);
        else evshield.bank_b.motorRunUnlimited(SH_Motor_2, (fwdback>=0) ? SH_Direction_Forward : SH_Direction_Reverse, fwdbackpct);

      tft.print("5");
      if (evshield.isTouched()) tft.print("6");
          // You can query the axis and other properties as well. See Gamepad.h
          // For all the available functions.
        }
      }

  delay(50);
}

wow I didn't think it was actually going to get posted! sweet

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.