Hi,
I have a Feather M0 Bluefruit LE, which I want to use as a customize HID controller. I know, there's an already implemented HID GATT service in the firmware, but I need a much more different thing. So - as in the healththermometer example -, I tried to create the HID service and characteristics myself, with the 'addService' and 'addCharacteristic' functions. Unfortunately, when I'm trying to read out the values of a characteristic from my phone, I can receive nothing. If I change the service and characteristic UUID to some random number, it starts working.
Before I would invest more energy into this, is even possible to "override" the existing HID service? Or is there a "collision" in the module, when I'm trying to do this?
Custom HID controller
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- hodrob
- Posts: 4
- Joined: Fri Dec 08, 2017 7:02 am
Re: Custom HID controller
Let me answer my question: it is possible to "override" the existing GATT service, the Windows can now recognize my HID device. Although the 32-byte characteristic size is a very strict limit in case of the HID descriptor, and also, I cannot set the report_reference descriptor, so it won't work.
- equack
- Posts: 16
- Joined: Tue Jan 02, 2018 3:27 pm
Re: Custom HID controller
I'm very interested in this problem as I need to emulate a proper HID joystick controller with proportional controls. Nobody from Adafruit seems to comment on these issues here in the forum or answers my direct support emails which is quite frustrating. Can you explain how far you got with the "roll your own" HID and exactly what the problem is? Can you share any code? I'm just about to either redesign my solution or bite the bullet and write my own firmware. It sure would be nice to have source code.
- hiemal
- Posts: 1
- Joined: Thu Feb 15, 2018 9:38 pm
Re: Custom HID controller
I bought a bluefruit LE friend for this purpose too, thinking the gamepad service would be able to give analog stick data... now I'm stuck and frustrated, with a board that has no use to me.
- equack
- Posts: 16
- Joined: Tue Jan 02, 2018 3:27 pm
Re: Custom HID controller
I have switched to the Feather nRF52. I still don't have the joystick mode working yet but I'm getting close. The PC sees it as a joystick. I have modified Adafruit's code extensively.
- hodrob
- Posts: 4
- Joined: Fri Dec 08, 2017 7:02 am
Re: Custom HID controller
Hi,
Sorry for the late response, I didn't follow the thread after a while... I made the HID working more-or-less, but the characteristic limit is 32-byte, which is just not enough for a HID descriptor. I was able to do only a limited feature HID device (maybe few buttons?). I wanted to do a more complex device, so I had to change to a Feather nRF52. (Which is still not perfect, as I'm fighting with power consumption issues...)
Maybe I have my quick sketch somewhere about that working trial, but it's just not enough for any real HID device. I can try to dig that up, if absolute necessary, but I don't recommend the Feather M0 Bluefruit as a HID device.
Sorry for the late response, I didn't follow the thread after a while... I made the HID working more-or-less, but the characteristic limit is 32-byte, which is just not enough for a HID descriptor. I was able to do only a limited feature HID device (maybe few buttons?). I wanted to do a more complex device, so I had to change to a Feather nRF52. (Which is still not perfect, as I'm fighting with power consumption issues...)
Maybe I have my quick sketch somewhere about that working trial, but it's just not enough for any real HID device. I can try to dig that up, if absolute necessary, but I don't recommend the Feather M0 Bluefruit as a HID device.
- equack
- Posts: 16
- Joined: Tue Jan 02, 2018 3:27 pm
Re: Custom HID controller
Hodrob- can you share your nRF52 code?
- hodrob
- Posts: 4
- Joined: Fri Dec 08, 2017 7:02 am
Re: Custom HID controller
Doing HID is pretty straightforward on nrf52. Here's my code. I didn't have time to add the output report and other things I want yet, just the buttons.
hid_game.cpp:
hid_game.h:
The simplified main code:
hid_game.cpp:
Code: Select all
#include "hid_game.h"
enum {
REPORT_ID_GAMEPAD = 1,
};
const uint8_t hid_report_descriptor[] =
{
//------------- Keyboard Report -------------//
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ),
HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ),
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),
HID_REPORT_ID ( REPORT_ID_GAMEPAD ),
HID_USAGE_PAGE( HID_USAGE_PAGE_BUTTON ),
HID_USAGE_MIN ( 1 ),
HID_USAGE_MAX ( 32 ),
HID_LOGICAL_MIN ( 0 ),
HID_LOGICAL_MAX ( 1 ),
HID_REPORT_COUNT ( 32 ),
HID_REPORT_SIZE ( 1 ),
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),
HID_COLLECTION_END,
};
HidGame::HidGame(void) :
BLEHidGeneric(1, 0, 0)
{
prev_button_state = 0;
}
err_t HidGame::begin(void)
{
uint16_t input_len[] = { sizeof(hid_game_button_t) };
setReportLen(input_len, NULL, NULL);
enableBootProtocol(false, false);
setReportMap(hid_report_descriptor, sizeof(hid_report_descriptor));
VERIFY_STATUS(BLEHidGeneric::begin());
/* Change connection interval to 11.25-15 ms when starting HID
* min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms */
Bluefruit.setConnInterval(9, 12);
//Bluefruit.setConnInterval(6, 8);
return ERROR_NONE;
}
bool HidGame::reportButtons(hid_game_button_t buttons)
{
if (prev_button_state == buttons)
return true;
#if 0
Serial.println(buttons, HEX);
#endif
prev_button_state = buttons;
return inputReport(REPORT_ID_GAMEPAD, &buttons, sizeof(buttons));
}
Code: Select all
#ifndef _HID_GAME_H
#define _HID_GAME_H
#include <bluefruit.h>
typedef uint32_t hid_game_button_t;
class HidGame : public BLEHidGeneric
{
private:
hid_game_button_t prev_button_state;
public:
HidGame(void);
virtual err_t begin();
/* Set 32 buttons' state (bit-field) */
bool reportButtons(hid_game_button_t buttons);
};
#endif /* _HID_GAME_H */
Code: Select all
HidGame hid_game;
void setup(void)
{
if (hid_game.begin())
error("ERROR");
.......
/* Include game service */
Bluefruit.Advertising.addService(hid_game);
......
}
void loop(void)
{
if (button_polling_timer_expired()) {
hid_game_button_t buttons;
button_polling_timer_restart();
/* Poll and report buttons */
buttons = button_task();
hid_game.reportButtons(buttons);
}
}
- equack
- Posts: 16
- Joined: Tue Jan 02, 2018 3:27 pm
Re: Custom HID controller
Bless you Hodrob. Your solution worked for me. I was able to add XYZ axes to your code by using the following report descriptor:
I tested it using a tool I found called Pointy's Joystick Test. http://www.planetpointy.co.uk/joystick- ... plication/. I have not experimented with 16 bit axis values or reports larger than 32 bits as I do not require them for my application.
I'm not clear on whether the LOGICAL_MIN and LOGICAL_MAX should be signed or unsigned (the axis values themselves behave as if they are signed), but it seems to work this way. I'm also not clear on whether I should use HID_USAGE_DESKTOP_GAMEPAD or HID_USAGE_DESKTOP_JOYSTICK.
There is an extremely useful discussion about supporting multiple axes and large numbers of buttons here https://forum.pjrc.com/threads/23681-Many-axis-joystick although it is not BLE specific. Apparently there are people building elaborate custom flight simulator cockpits that require large numbers of inputs.
Code: Select all
const uint8_t hid_report_descriptor[] =
{
//------------- XYZ axis 8 button Joystick Report -------------//
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ),
HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ),
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),
HID_REPORT_ID ( REPORT_ID_GAMEPAD ),
HID_USAGE_PAGE( HID_USAGE_PAGE_BUTTON ),
HID_USAGE_MIN ( 1 ),
HID_USAGE_MAX ( 8 ),
HID_LOGICAL_MIN ( 0 ),
HID_LOGICAL_MAX ( 1 ),
HID_REPORT_COUNT ( 8 ),
HID_REPORT_SIZE ( 1 ),
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP),
HID_USAGE ( HID_USAGE_DESKTOP_X ),
HID_USAGE ( HID_USAGE_DESKTOP_Y ),
HID_USAGE ( HID_USAGE_DESKTOP_Z ),
HID_LOGICAL_MIN ( 0x00 ),
HID_LOGICAL_MAX ( 0xFF ),
HID_REPORT_COUNT( 3 ), // X, Y, Z position
HID_REPORT_SIZE ( 8 ), // 8 bit value
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), // input values
HID_COLLECTION_END,
};
I'm not clear on whether the LOGICAL_MIN and LOGICAL_MAX should be signed or unsigned (the axis values themselves behave as if they are signed), but it seems to work this way. I'm also not clear on whether I should use HID_USAGE_DESKTOP_GAMEPAD or HID_USAGE_DESKTOP_JOYSTICK.
There is an extremely useful discussion about supporting multiple axes and large numbers of buttons here https://forum.pjrc.com/threads/23681-Many-axis-joystick although it is not BLE specific. Apparently there are people building elaborate custom flight simulator cockpits that require large numbers of inputs.
Please be positive and constructive with your questions and comments.