A little help with a gamepad

Hello, I have a few gamepads/flightsticks I made a few years back using Pro Micro's to help me physically plays games and I wanted to add/or make them bluetooth. Finding out now that's a lot easier said than done I've opted to grab some new boards ProMicro nRF52840's. that resemble the ones that I already use, size wise and the same pins to just swap boards without adding things and/or causing me to redesign the cad models and print them again. Now I've managed to get all of them functioning with buttons but I can't get any of them to read analog inputs from joysticks. (Hall effect joycon stick and basic analog)

Using one as a example/starting point. I have a one-handed, 15 button, 1 joystick gamepad I use ALOT as 2 hands are not a option.. all 15 buttons function as intend but I can not get analog readings from my joystick (x, y). I'm reaching out to anyone with experience to help finish the code on this one in particular as it would help with my other 13 custom HID's as its the same issue with all.

#include <Adafruit_TinyUSB.h>

/*********************************************************************
     Single Handed Gamepad, 15 Buttons, 1 Joystick
 ********************************************************************/


#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 X_PIN A1      //JOY X
#define Y_PIN A2      //JOY Y
#define BTN1_PIN 0    //Pinky1
#define BTN2_PIN 1    //Pinky2
#define BTN3_PIN 2    //Pinky3
#define BTN4_PIN 3    //Ring4
#define BTN5_PIN 4    //Ring5
#define BTN6_PIN 5    //Ring6
#define BTN7_PIN 6    //Middle7
#define BTN8_PIN 7    //Middle8
#define BTN9_PIN 8    //Middle8
#define BTN10_PIN 9   //Pointer10
#define BTN11_PIN 13  //Pointer11
#define BTN12_PIN 12  //Pointer12
#define BTN13_PIN 11  //Thumb13
#define BTN14_PIN 10  //Menu14
#define BTN15_PIN 16  //Start15

//defining the pin array
uint8_t x = {X_PIN};
uint8_t y = {Y_PIN};
uint8_t myPins[] = {BTN1_PIN, BTN2_PIN, BTN3_PIN, BTN4_PIN, BTN5_PIN, BTN6_PIN, BTN7_PIN,
  BTN8_PIN, BTN9_PIN, BTN10_PIN, BTN11_PIN, BTN12_PIN, BTN13_PIN, BTN14_PIN, BTN15_PIN};
  //defines the array length
uint8_t pinCount = sizeof(myPins)/sizeof(myPins[0]);
uint8_t unusedPins[] = {};
uint8_t unusedPinCount = 0;
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("ChazronBLE");
  Serial.println("------------------------------------\n");
  Serial.println("FileName: Chazron.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("Chazron");
  // Configure and Start Device Information Service
  bledis.setManufacturer("InMyRoom Productions");
  bledis.setModel("Chucke Ergo - nRF52840");
  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;
          gp.buttons |= !digitalRead(BTN12_PIN) << 11;
          gp.buttons |= !digitalRead(BTN13_PIN) << 12;
          gp.buttons |= !digitalRead(BTN14_PIN) << 13;
          gp.buttons |= !digitalRead(BTN15_PIN) << 14;  //assigns current state value to button 15

         
     
          lastButtonState[i] = currentButtonState;

        }
          
    }
      blegamepad.report(&gp);
}

Do you know the values being received by the Pro Micro? Is the current code only allowing left/center/right and top/middle/bottom, and not increasing values from left to right and top to bottom?

My original code is lost with my old PC as these devices I made are several years old.

So I started from scratch and I got a code going that properly connects via Bluetooth (thanks to this forum) and works for the 15 buttons but I can't figure out where to input the code for reading the joystick. Don't get me wrong I've attempted but from what my beginner self understands the chips are different so the code/library's from the old devices will not work on here so I'm in the dark but learning. I will include the code iv got working on the device. Excuse me if its a mess.

#include <Adafruit_TinyUSB.h>

/*********************************************************************
     Single Handed Gamepad, 15 Buttons, 1 Joystick
 ********************************************************************/


#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 X_PIN A1      //JOY X
#define Y_PIN A2      //JOY Y
#define BTN1_PIN 0    //Pinky1
#define BTN2_PIN 1    //Pinky2
#define BTN3_PIN 2    //Pinky3
#define BTN4_PIN 3    //Ring4
#define BTN5_PIN 4    //Ring5
#define BTN6_PIN 5    //Ring6
#define BTN7_PIN 6    //Middle7
#define BTN8_PIN 7    //Middle8
#define BTN9_PIN 8    //Middle8
#define BTN10_PIN 9   //Pointer10
#define BTN11_PIN 13  //Pointer11
#define BTN12_PIN 12  //Pointer12
#define BTN13_PIN 11  //Thumb13
#define BTN14_PIN 10  //Menu14
#define BTN15_PIN 16  //Start15

//defining the pin array
uint8_t x = {X_PIN};
uint8_t y = {Y_PIN};
uint8_t myPins[] = {BTN1_PIN, BTN2_PIN, BTN3_PIN, BTN4_PIN, BTN5_PIN, BTN6_PIN, BTN7_PIN,
  BTN8_PIN, BTN9_PIN, BTN10_PIN, BTN11_PIN, BTN12_PIN, BTN13_PIN, BTN14_PIN, BTN15_PIN};
  //defines the array length
uint8_t pinCount = sizeof(myPins)/sizeof(myPins[0]);
uint8_t unusedPins[] = {};
uint8_t unusedPinCount = 0;
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("ChazronBLE");
  Serial.println("------------------------------------\n");
  Serial.println("FileName: Chazron.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("Chazron");
  // Configure and Start Device Information Service
  bledis.setManufacturer("InMyRoom Productions");
  bledis.setModel("Chucke Ergo - nRF52840");
  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;
          gp.buttons |= !digitalRead(BTN12_PIN) << 11;
          gp.buttons |= !digitalRead(BTN13_PIN) << 12;
          gp.buttons |= !digitalRead(BTN14_PIN) << 13;
          gp.buttons |= !digitalRead(BTN15_PIN) << 14;  //assigns current state value to button 15

         
     
          lastButtonState[i] = currentButtonState;

        }
          
    }
      blegamepad.report(&gp);
}    
 

I understand what you are referring to as the code I used to get these new boards working had four buttons that would function as the analog joystick but they would only give me left/center/right top/middle/bottom I was looking for full analog input.
here's the Original code

/*********************************************************************
 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);
    
 
}

Buttons are digital (on/off). To simulate "analog" time each button press...

  • a press/release could be your "center to edge" max movement
  • a button held would use time to slowly move the object in an "analog" manner
  • a button held longer would "increase the rate of increase" (that is to say, the longer you hold the button, the faster the object moves)

If you are not into coding the button timing MobaTools has button press functions. Ask @MicroBahner about MobaTools.

I went over and checked that out. That library is not supported on my board.

I also think you misunderstood my ask for help.
I'm trying to get XY inputs from a analog joystick to show up in the gamepad tester in Windows as analog inputs not digital presses. I've done this with the promicros that use USB to connect before but this board uses a NRF52840 chip with integrated Bluetooth.

The original code I posted is the one I got functioning with my board(nrf) minus the analog stick.
The second code I posted is the code I used to make mine work on my board(after making a custom variant for my board)

I'm just trying to get x and y inputs sent over Bluetooth so the computer reads them as it reads the 15 buttons wired to the board.

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