Help understanding Adafruit TinyUSB example

Specifically the gamepad example:

#include "Adafruit_TinyUSB.h"

/* This sketch demonstrates USB HID gamepad use.
 * This sketch is only valid on boards which have native USB support
 * and compatibility with Adafruit TinyUSB library. 
 * For example SAMD21, SAMD51, nRF52840.
 * 
 * Make sure you select the TinyUSB USB stack if you have a SAMD board.
 * You can test the gamepad on a Windows system by pressing WIN+R, writing Joy.cpl and pressing Enter.
 */

// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] =
{
  TUD_HID_REPORT_DESC_GAMEPAD()
};

// USB HID object. For ESP32 these values cannot be changed after this declaration
// desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_NONE, 2, false);

// Report payload defined in src/class/hid/hid.h
// - For Gamepad Button Bit Mask see  hid_gamepad_button_bm_t
// - For Gamepad Hat    Bit Mask see  hid_gamepad_hat_t
hid_gamepad_report_t    gp;

void setup() 
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
  // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
  TinyUSB_Device_Init(0);
#endif

  Serial.begin(115200);
  
  // Notes: following commented-out functions has no affect on ESP32
  // usb_hid.setPollInterval(2);
  // usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));

  usb_hid.begin();

  // wait until device mounted
  while( !TinyUSBDevice.mounted() ) delay(1);
  
  Serial.println("Adafruit TinyUSB HID Gamepad example");
}

void loop() 
{ 
//  // Remote wakeup
//  if ( TinyUSBDevice.suspended() && btn )
//  {
//    // Wake up host if we are in suspend mode
//    // and REMOTE_WAKEUP feature is enabled by host
//    TinyUSBDevice.remoteWakeup();
//  }

  if ( !usb_hid.ready() ) return;

  // Reset buttons
  Serial.println("No pressing buttons");
  gp.x       = 0;
  gp.y       = 0;
  gp.z       = 0;
  gp.rz      = 0;
  gp.rx      = 0;
  gp.ry      = 0;
  gp.hat     = 0;
  gp.buttons = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  
  // Hat/DPAD UP
  Serial.println("Hat/DPAD UP");
  gp.hat = 1; // GAMEPAD_HAT_UP;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD UP RIGHT
  Serial.println("Hat/DPAD UP RIGHT");
  gp.hat = 2; // GAMEPAD_HAT_UP_RIGHT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD RIGHT
  Serial.println("Hat/DPAD RIGHT");
  gp.hat = 3; // GAMEPAD_HAT_RIGHT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD DOWN RIGHT
  Serial.println("Hat/DPAD DOWN RIGHT");
  gp.hat = 4; // GAMEPAD_HAT_DOWN_RIGHT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

   // Hat/DPAD DOWN
  Serial.println("Hat/DPAD DOWN");
  gp.hat = 5; // GAMEPAD_HAT_DOWN;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Hat/DPAD DOWN LEFT
  Serial.println("Hat/DPAD DOWN LEFT");
  gp.hat = 6; // GAMEPAD_HAT_DOWN_LEFT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD LEFT
  Serial.println("Hat/DPAD LEFT");
  gp.hat = 7; // GAMEPAD_HAT_LEFT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD UP LEFT
  Serial.println("Hat/DPAD UP LEFT");
  gp.hat = 8; // GAMEPAD_HAT_UP_LEFT;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Hat/DPAD CENTER
  Serial.println("Hat/DPAD CENTER");
  gp.hat = 0; // GAMEPAD_HAT_CENTERED;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  
  // Joystick 1 UP
  Serial.println("Joystick 1 UP");
  gp.x = 0;
  gp.y = -127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Joystick 1 DOWN
  Serial.println("Joystick 1 DOWN");
  gp.x = 0;
  gp.y = 127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Joystick 1 RIGHT
  Serial.println("Joystick 1 RIGHT");
  gp.x = 127;
  gp.y = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Joystick 1 LEFT
  Serial.println("Joystick 1 LEFT");
  gp.x = -127;
  gp.y = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Joystick 1 CENTER
  Serial.println("Joystick 1 CENTER");
  gp.x = 0;
  gp.y = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);


  // Joystick 2 UP
  Serial.println("Joystick 2 UP");
  gp.z  = 0;
  gp.rz = 127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Joystick 2 DOWN
  Serial.println("Joystick 2 DOWN");
  gp.z  = 0;
  gp.rz = -127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Joystick 2 RIGHT
  Serial.println("Joystick 2 RIGHT");
  gp.z  = 127;
  gp.rz = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Joystick 2 LEFT
  Serial.println("Joystick 2 LEFT");
  gp.z  = -127;
  gp.rz = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Joystick 2 CENTER
  Serial.println("Joystick 2 CENTER");
  gp.z  = 0;
  gp.rz = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);


  // Analog Trigger 1 UP
  Serial.println("Analog Trigger 1 UP");
  gp.rx = 127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Analog Trigger 1 DOWN
  Serial.println("Analog Trigger 1 DOWN");
  gp.rx = -127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Analog Trigger 1 CENTER
  Serial.println("Analog Trigger 1 CENTER");
  gp.rx = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);


  // Analog Trigger 2 UP
  Serial.println("Analog Trigger 2 UP");
  gp.ry = 127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);
  
  // Analog Trigger 2 DOWN
  Serial.println("Analog Trigger 2 DOWN");
  gp.ry = -127;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // Analog Trigger 2 CENTER
  Serial.println("Analog Trigger 2 CENTER");
  gp.ry = 0;
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  
  // Test buttons (up to 32 buttons)
  for (int i=0; i<32; ++i)
  {
    Serial.print("Pressing button "); Serial.println(i);
    gp.buttons = (1U << i);
    usb_hid.sendReport(0, &gp, sizeof(gp));
    delay(1000);
  }


  // Random touch
  Serial.println("Random touch");
  gp.x       = random(-127, 128);
  gp.y       = random(-127, 128);
  gp.z       = random(-127, 128);
  gp.rz      = random(-127, 128);
  gp.rx      = random(-127, 128);
  gp.ry      = random(-127, 128);
  gp.hat     = random(0,      9);
  gp.buttons = random(0, 0xffff);
  usb_hid.sendReport(0, &gp, sizeof(gp));
  delay(2000);

  // */
}

and more specifically, how buttons presses are sent:

// Test buttons (up to 32 buttons)
  for (int i=0; i<32; ++i)
  {
    Serial.print("Pressing button "); Serial.println(i);
    gp.buttons = (1U << i);
    usb_hid.sendReport(0, &gp, sizeof(gp));
    delay(1000);
  }

Maybe im just dense and/or lacking vital foundational programming knowledge, but i find this example especially confusing and unnecessarily complicated.
I sorta understand all the functions/elements individually but putting them together and/or adapting them into something useful seem to be beyond my current level of comprehension. From what i can tell, this demo appears to just press a button( i= 0-31) and and keeps it "pressed" while subsequent buttons are pressed (and held.) Everything gets reset (buttons released) when the loop restarts, and everything (gp.buttons ) gets reset to 0 . To me, it is very unclear how to release a single button (while keeping track of other buttons being held.

What would be the simplest way (with the least amount of variables and bit manipulations) to call a single button press (button 1, for example) and then, later, release that same button?

thanks in advance

if i understand right it is only demo, it sends sequence of gamepad presses, not real state of controlls.
so your snippet sends 32 times one button press, for each button. you can modify it for your project as

gp.buttons = digitalRead(2)<<7;// it will then gamepad button 7

for more buttons

gp.buttons |= digitalRead(2)<<7;// it will then gamepad button 7
gp.buttons |= digitalRead(10);    //  button 0

maybe you can send entire input port like:

gp.buttons = PORTD;// i don't know real name of input pin register of your board

Thanks a lot- thats a big help. I managed to it get it (partially) working. |= works great for reading and adding multiple button presses, but Im still having difficulty releasing buttons.

for (int i = 0; i < 8; i++) {
    buttonState[i] = digitalRead(button[i]);
    if (buttonState[i] == LOW && previousButtonState[i] == HIGH) {
      gp.buttons |= 1 << command[i];
      usb_hid.sendReport(0, &gp, sizeof(gp));
    }
    if (buttonState[i] == HIGH && previousButtonState[i] == LOW) {
       gp.buttons &= (0 << command[i]);
       usb_hid.sendReport(0, &gp, sizeof(gp));
    }
    previousButtonState[i] = buttonState[i];
  }

Ive learned that using bitwise OR doesnt work for changing things back to zero- everything just filles up with ones = buttons always held. Using "=" works but then all other pressed buttons are released. I thought that i was clever and tried &=, but that seems to behave like =, and all buttons are released. I guess Im changing all of gp.buttons, not just the bits for a specific key. Is there something im missing? How can I "release" individual buttons while still retaining the state of "pressed" buttons?

Maybe something like this.

void press(uint8_t b)
{
 gp.buttons |= (uint16_t)1 << b;
}


void release(uint8_t b)
{
  gp.buttons &= ~((uint16_t)1 << b);
}


void releaseAll(void)
{
  gp.buttons = 0;
}

is it not obvious?

gp.buttons = 0;
gp.buttons |= digitalRead(2)<<7;// it will then gamepad button 7
gp.buttons |= digitalRead(10);    //  button 0
usb_hid.sendReport(0, &gp, sizeof(gp));

or

#define CLR(x,y) (x&=(~(1<<y)))
#define SET(x,y) (x|=(1<<y))
const uint8_t buttonPins[8] = {2, 13, 4, A0, A2, A1, 12, 3};

void setup() {
  // put your setup code here, to run once:

}

void loop() {

  for (uint8_t i = 0; i < 8; i++)
    digitalRead(buttonPins[i]) ? SET(gp.buttons, y << i) : CLR(gp.buttons, y << i);

  usb_hid.sendReport(0, &gp, sizeof(gp));
}

got it! thanks so much.

Hi, I know this is a bit old, but it seems like you got it sorted, and I'm having a similar issue.

I'm trying to adapt the information in this post for a BLE gamepad, based on a Bluefruit nRF52832. The base blehid_gamepad example functions just like the TinyUSB gamepad example, so I think any advice will be relevant.

Ultimately, I'm trying to make a multi button joystick to work with retropi, but currently i have it configured to send keyboard presses. Unfortunately, it is impossible to play with a keyboard as its not recognized as a player input, this is why I need to reprogram.

Here's what's going on, I have been able through some tinkering to get the buttons working, to a degree. I can recognize inputs, but the only way key presses turn off is by pressing another button. I think maybe I'm just missing something in the way to implement this.

Is it possible to past the entire code for your gamepad here, including input setup? I think if I could have a look at an actual working example I'd be able to figure this out.

In the interest of being complete here is the code I have so far tested. I'd really like to be able to assign specific pins for specific button numbers / dpad directions, as right now each button is sort of auto assigned, i realize the dpad functions differently and I think conceptually I might understand that. The code below seems to release the button after each loop regardless of if I continue pushing it, not sure why, but I was able to stop that by commenting out the first part of the loop. Leaving the behavior described above.

In advance thank you to anyone who takes the time to both read and try and help.

/*********************************************************************
 This is a test sketch to determine how button activation works 
 for a gamepad.
 
 uses nRF52832 blehid_gmepad.ino as the base
 
 Added button debounce library.

 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/

/* This sketch demonstrate how to use BLEHidGamepad to send
 * button, dpad, and joystick report
 */

#include <bluefruit.h>
#include <Bounce2.h> 

BLEDis bledis;
BLEHidGamepad blegamepad;

#define UP_PIN A0
#define DOWN_PIN A1


uint8_t pins[] = { UP_PIN, DOWN_PIN };
uint8_t pincount = sizeof(pins)/sizeof(pins[0]);

Bounce * buttons = new Bounce[pincount];

// defined in hid.h from Adafruit_TinyUSB_Arduino
hid_gamepad_report_t gp;

void setup()
{
  Serial.begin(115200);

#if CFG_DEBUG
  // Blocking wait for connection when debug mode is enabled via IDE
  while ( !Serial ) delay(10);
#endif

  Serial.println("Bluefruit52 HID Gamepad Example");
  Serial.println("-------------------------------\n");

  Serial.println();
  Serial.println("Go to your phone's Bluetooth settings to pair your device");
  Serial.println("then open an application that accepts gamepad input");

  Bluefruit.begin();
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values

  // Configure and Start Device Information Service
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather 52");
  bledis.begin();

  /* Start BLE HID
   * Note: Apple requires BLE device must have min connection interval >= 20m
   * ( The smaller the connection interval the faster we could send data).
   * However for HID and MIDI device, Apple could accept min connection interval 
   * up to 11.25 ms. Therefore BLEHidAdafruit::begin() will try to set the min and max
   * connection interval to 11.25  ms and 15 ms respectively for best performance.
   */
  blegamepad.begin();

  /* Set connection interval (min, max) to your perferred value.
   * Note: It is already set by BLEHidAdafruit::begin() to 11.25ms - 15ms
   * min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms
   */
  /* Bluefruit.Periph.setConnInterval(9, 12); */

  // Set up and start advertising
  startAdv();

for (uint8_t i=0; i < pincount; i++ )
  {
    pinMode(i, OUTPUT);
  
  }

  // set up pin as input
  for (uint8_t i=0; i<pincount; i++)
  {
    buttons[i].attach( pins[i] , INPUT_PULLUP  );       //setup the bounce instance for the current button
    buttons[i].interval(25);              // interval in ms
  }



}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_GAMEPAD);

  // Include BLE HID service
  Bluefruit.Advertising.addService(blegamepad);

  // There is enough room for the dev name in the advertising packet
  Bluefruit.Advertising.addName();

  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   *
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds
}

void loop()
{
  // nothing to do if not connected or
  if ( !Bluefruit.connected() ) return;

/*  Serial.println("No pressing buttons");
  gp.x       = 0;
  gp.y       = 0;
  gp.z       = 0;
  gp.rz      = 0;
  gp.rx      = 0;
  gp.ry      = 0;
  gp.hat     = 0;
  gp.buttons = 0;
  blegamepad.report(&gp);
  delay(1000);
*/
//section above commented out to stop button from clearing after each loop when held down

  //------------- Joystick 1 -------------//
 for(uint8_t i=0; i < pincount; i++)
  {
    buttons[i].update();
    
    if ( 0 == buttons[i].read() )
    {

    gp.buttons = (1ul << i);
    blegamepad.report(&gp);
    
  }
  

 
  }
}
 

UPDATE: after much searching and reading and re-reading, i found the answer was right here.

gp.buttons = 0;   //this clears out all the button presses in the button mask.
gp.buttons |= digitalRead(2)<<7;    // this writes the value of pin 2 to gamepad button 8
gp.buttons |= digitalRead(10) << 0;    //  this writes the value of pin 10 to gamepad button 0 
usb_hid.sendReport(0, &gp, sizeof(gp));  // this send the report

basically I need to add a line for each button, then i can indicate in the sketch which pins go to which specific buttons. If I want to activate axis or hat commands i need to update those values before sending the report. I hope this added bit of explanation helps someone down the line.

I ultimately implemented a compare state if statement to trigger the button updates.
remember i'm using the bounce2 lib with this as well. You will also need to define the "lastButtonState" array in the setup section

void loop()
{
for (uint8_t i=0; i < pinCount; i++)
    {
      buttons[i].update();
       
        uint8_t currentButtonState = buttons[i].read();
        if (currentButtonState != lastButtonState[i]) 
        {
          gp.buttons = 0;
          gp.buttons |= !digitalRead(BTN1_PIN) << 0;
          gp.buttons |= !digitalRead(BTN2_PIN) << 1;
          gp.buttons |= !digitalRead(BTN3_PIN) << 2;
         lastButtonState[i] = currentButtonState;
}
}
     blegamepad.report(&gp);
}

hello I have noticed your use of Bluetooth in your project. I'm currently trying to remake a game controller i designed using a Arduino promicro but wanted to make it bluetooth so i picked up a few pro micro nrf52840 boards and was curious on what your project is and if you are having success? I'm having issues writing a code using the example your using

I was successful using the code above with a Bluefruit Feather nRF52832. From my reading this should work basically the same way with the nRF52840. I was successful in having the device recognized as a gamepad, and it works as I intended.

For my project i built a joystick gamepad out of a book i hollowed out. It has 11 buttons and a samwa joystick. I use it primarily to play retro arcade games on a raspberry pi. It does have some lag, and that's not ideal, but functions reasonably well for most games.

Do you mind sharing your code? My project is very similar 15 buttons one joystick (joycon hall effect stick). Iv made multiple Arduino pro micro flight sticks and controllers before but nothing with wireless communications (Bluetooth).

Just in case you're wondering My controller is like a one-handed gamepad there is three buttons for each finger similar to an azeron keyboard with a joycon hall effect stick. I play a lot of computer games and using a keyboard is not ideal for me I picked up an azeron thinking it would help me but I ended up paying for 3D prints that make my hand cramp even more. So I 3d scanned my hand and made a controller that resembles the exact shape of my hand. It came out amazing zero cramping and fatigue on my hand but the problem is it needs a wire and I use a handheld a lot and there's only one port to charge it so plugging in this controller and charging my handheld is a issue
(Legion go)

Sounds cool, i'd love to see a pic of the finished product. Find the code below.

/*********************************************************************
 This is an attempt to change the joystick book so it is recognized 
 as a game pad by the connected device. 
 ********************************************************************/

/* This sketch demonstrate how to use BLEHidGamepad to send
 * button, dpad, and joystick report
 */

#include <bluefruit.h>
#include <Bounce2.h>  //button debounce library


BLEDis bledis;
BLEHidGamepad blegamepad;

// define each function and its input pin number comments describe physical switches
#define UP_PIN A0  //JOY UP
#define DOWN_PIN A1   //JOY DOWN
#define LEFT_PIN A2   //JOY LEFT
#define RIGHT_PIN A3   //JOY RIGHT
#define BTN1_PIN 13  //Left Red
#define BTN2_PIN 14 //Middle Red
#define BTN3_PIN 11  //Right Red
#define BTN4_PIN A4  //Left Black
#define BTN5_PIN A5  //Middle Black
#define BTN6_PIN 12  //Right Black
#define BTN7_PIN 30  //Start button
#define BTN8_PIN 27  //Select Button
#define BTN9_PIN 31   //Back Button
#define BTN10_PIN 15   //Home Button
#define BTN11_PIN 16   //Menu Button

//defining the pin array
uint8_t myPins[] = {UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN,
  BTN1_PIN, BTN2_PIN, BTN3_PIN, BTN4_PIN, BTN5_PIN, BTN6_PIN, BTN7_PIN,
  BTN8_PIN, BTN9_PIN, BTN10_PIN, BTN11_PIN  };
  //defines the array length
uint8_t pinCount = sizeof(myPins)/sizeof(myPins[0]);
uint8_t unusedPins[] = { 21, 19, 34, 3, 2, 20, 35};
uint8_t unusedPinCount = 7;
uint8_t lastButtonState[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

Bounce * buttons = new Bounce[pinCount];  //start the bounce container

// defined in hid.h from Adafruit_TinyUSB_Arduino
hid_gamepad_report_t gp;

void setup()
{

  
// LOW POWER MODE!
  // Pins default to INPUT mode. To save power, turn them all to OUTPUT
  // initially, so only those being used will be turn on. See:
  // http://www.pjrc.com/teensy/low_power.html

  for (uint8_t pin=0; pin < unusedPinCount; pin++ )
  {
    pinMode(unusedPins[pin], OUTPUT);
  }

  // set up pins as input for bounce 2 library and digital read 
  for (uint8_t i=0; i < pinCount; i++)
  {
    buttons[i].attach( myPins[i] , INPUT_PULLUP  );       //setup the bounce instance for current button
    buttons[i].interval(5);              // interval in ms


  }
  Serial.begin(115200);

#if CFG_DEBUG
  // Blocking wait for connection when debug mode is enabled via IDE
  while ( !Serial ) delay(10);
#endif

  Serial.println("Ron's JoyPad Mark 5 - gamepad_hid");
  Serial.println("------------------------------------\n");
  Serial.println("FileName: Rons_JoyPad_M5_blehid-gamepad.ino");
  Serial.println();
  Serial.println("Go to your devices's Bluetooth settings to pair your device");
  Serial.println("then open an application that accepts gamepad input");

  Bluefruit.begin();
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
  Bluefruit.setName("Moby Dick Joy");
  // Configure and Start Device Information Service
  bledis.setManufacturer("RonBot Productions");
  bledis.setModel("Ron's Joypad - nRF52832");
  bledis.begin();

  /* Start BLE HID
   * Note: Apple requires BLE device must have min connection interval >= 20m
   * ( The smaller the connection interval the faster we could send data).
   * However for HID and MIDI device, Apple could accept min connection interval 
   * up to 11.25 ms. Therefore BLEHidAdafruit::begin() will try to set the min and max
   * connection interval to 11.25  ms and 15 ms respectively for best performance.
   */
  blegamepad.begin();

  /* Set connection interval (min, max) to your perferred value.
   * Note: It is already set by BLEHidAdafruit::begin() to 11.25ms - 15ms
   * min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms
   */
  /* Bluefruit.Periph.setConnInterval(9, 12); */

  // Set up and start advertising
  startAdv();

}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_GAMEPAD);

  // Include BLE HID service
  Bluefruit.Advertising.addService(blegamepad);

  // There is enough room for the dev name in the advertising packet
  Bluefruit.Advertising.addName();

  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   *
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds
}

void loop()
{
  // nothing to do if not connected or
  if ( !Bluefruit.connected() ) return;

    
    for (uint8_t i=0; i < pinCount; i++)
    {
      buttons[i].update();

            
        uint8_t currentButtonState = buttons[i].read();   //Initialize current state of buttons array
        if (currentButtonState != lastButtonState[i])   //compare current state to previous state and run proccess to update the gp report
        {
          gp.buttons = 0;     //clears button mask before entering new values for each button
          gp.buttons |= !digitalRead(BTN1_PIN) << 0;  //assigns current state value to button 1
          gp.buttons |= !digitalRead(BTN2_PIN) << 1;
          gp.buttons |= !digitalRead(BTN3_PIN) << 2;
          gp.buttons |= !digitalRead(BTN4_PIN) << 3;
          gp.buttons |= !digitalRead(BTN5_PIN) << 4;
          gp.buttons |= !digitalRead(BTN6_PIN) << 5;
          gp.buttons |= !digitalRead(BTN7_PIN) << 6;
          gp.buttons |= !digitalRead(BTN8_PIN) << 7;
          gp.buttons |= !digitalRead(BTN9_PIN) << 8;
          gp.buttons |= !digitalRead(BTN10_PIN) << 9;
          gp.buttons |= !digitalRead(BTN11_PIN) << 10;  //assigns current state value to button 11

          if(digitalRead(UP_PIN)==LOW)
          {
            gp.y = -127;
          }
          else if(digitalRead(DOWN_PIN)==LOW)
          {
            gp.y = 127;
          }
          else 
          {
            gp.y = 0;
          }

          if(digitalRead(LEFT_PIN)==LOW)
          {
            gp.x = -127;
          }
          else if(digitalRead(RIGHT_PIN)==LOW)
          {
            gp.x = 127;
          }
          else 
          {
            gp.x = 0;
          }
     
          lastButtonState[i] = currentButtonState;

        }
    }
      blegamepad.report(&gp);
    
 
}

Remember i'm using the nRF52832 Bluefruit Feather, so make sure you are using the right board

Let me know if there's anything else i can do to help.

i sure will share some photos when complete! I appreciate the help I have something working now (minus the hall effect stick). I'm finding out this is different from a these pro micros I'm used to.
Looks like the library's have differences and that's where my confusion lies.
So my old code is useless with such a board (adafruit itsybitsy nRF52840)
iv got all the buttons(15) functioning I'm currently reading up to figure out the joystick. honestly been quite awhile since iv had to write code I switched to rr_configurator when it was released because how simple it made my joystick/gamepad projects... truly spoiled me.

after a day of troubleshooting i found that theres not a proper variant file for my board so i cant access all usable pins. it has the same pinout as a pro micro but theres no boards that have the same pinout using the nrf52840. creating custom board files for Arduino is beyond me tho im going to try.

have you any experience?

Edit: i risked it and edited the variant files knowing nothing about it, it was pretty simple copy pasting and paying attention

I'm still stuck trying to get analog joystick readings for x and y

Sounds interesting. I do t have any experience with custom board configuration.

I thought the Arduino ide does have support available for the nRF52840 you may need to download the board support. I think you want the adafruit one.

I also don't have any specific experience with analog sticks, but I think it basically works the same way, just specific values on a spectrum rather than extremes. You may need a simple conversion line for the value read at the pin vs what you want to set the gamepad to report.

I did not have any custom board experience until a couple of days ago. I had to make my own variant. The board I got was meant for use with software that programs them for you so pin out from the chip was different from any other board in Arduino IDE. I got it all solved, my pins are functioning with the corresponding assignments. Still no luck with my analog joystick though. Im quite new to fully programming my controllers as this board doesnt support RR_configurator (free software to make programming joysticks and gamepads easier) so I'm currently taking a crash course with Arduino IDE.

Seeing how I have many controllers and flight sticks I made for one arm/hand use so I can play games again. I'm going to have to write the code for all of them to make them Bluetooth. Wires are getting in the way.

Hello sir up your TX power to max (8) which will decrease lag (path loss for me all though my TX and RX are within a foot of one another) worked well for me. I also added to the code

Bluefruit.configProphBandwidth(4)

which will max bandwidth. I believe it's defaulted to auto(0).
This also helped decrease the latency as I have tested it before and after.
Yes all this probably maximizes power draw from the battery but I currently run my devices off removable 18650 batteries which I charge separately from the device so I don't need to plug any wires into it.

Me using it and not shutting it off have shown with one 18650 2500mah. I've gotten a total of 5 days without swapping battery and when swapped it for a charged one i tested it's voltage and I got 3.99 volts.

Me not being concerned about power consumption. I let it have it.


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