• Hi everyone,

    I read the doc describing usage of BLE HID (https://www.espruino.com/BLE+Keyboard) and I wonder how I can use this in order to build a BLE gamepad?

    I don't know if this is still up to date, but on this page (https://www.freebsddiary.org/APC/usb_hid­_usages.php) is described a "Game Controls" command set. Do you guys think I should update the HID descriptor or can I use either the module ble_hid_keyboard or ble_hid_controls to build such a gamepad?

    Basically, my gamepad consists of 8 buttons, laid out like a Gameboy.

    Thank you for your help!

  • Yes, you should just be able to update the HID descriptor to one that matches what buttons you'd like to have. You might be able to find one online. You can see from the module it's pretty basic - so you could just add something to your own app.

    There's also https://github.com/espruino/BangleApps/b­lob/master/apps/boot/hid_info.txt that might be some help - that's for an analog joystick that got added to Bangle.js recently.

    ... of course depending on your use case you might just be able to use the keyboard as-is and send arrow keys?

  • It seems that I can do anything I want with the ble_hid_keyboard profile. But now I am facing another problem:
    When I press button "A", Until now I sent the character "A":
    function btnA(){kb.tap(kb.KEY.A,0);}
    What I should rather do is sending a command saying "A is pressed" or "A is released". Any idea on how to do that over HID?

  • Well it seems the limitation comes from the module itself, in particular the method tap() which sends press and release successively...

    exports.tap = function(keyCode, modifiers, callback) {
      NRF.sendHIDReport([modifiers,0,keyCode,0­,0,0,0,0], function() {
        NRF.sendHIDReport([0,0,0,0,0,0,0,0], function() {
          if (callback) callback();
  • OK I managed to solve my problem by calling NRF.sendHIDReportmanually.
    Now I have a first working gamepad implementation. Here is my script:

    var kb=require("ble_hid_keyboard");
    var keys=[0,0,0,0,0,0,0,0];
    function onInit(){
      NRF.setServices(undefined, { hid : kb.report });
      setWatch("keys[2]=kb.KEY.UP;NRF.sendHIDR­eport(keys);", BTN_UP, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[2]=0;NRF.sendHIDReport(ke­ys);", BTN_UP, {edge:"rising",repeat:true,debounce:50})­;
      setWatch("keys[3]=kb.KEY.DOWN;NRF.sendHI­DReport(keys);", BTN_DN, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[3]=0;NRF.sendHIDReport(ke­ys);", BTN_DN, {edge:"rising",repeat:true,debounce:50})­;
      setWatch("keys[4]=kb.KEY.LEFT;NRF.sendHI­DReport(keys);", BTN_LEFT, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[4]=0;NRF.sendHIDReport(ke­ys);", BTN_LEFT, {edge:"rising",repeat:true,debounce:50})­;
      setWatch("keys[5]=kb.KEY.RIGHT;NRF.sendH­IDReport(keys);", BTN_RIGHT, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[5]=0;NRF.sendHIDReport(ke­ys);", BTN_RIGHT, {edge:"rising",repeat:true,debounce:50})­;
      setWatch("keys[6]=kb.KEY.BACKSPACE;NRF.s­endHIDReport(keys);", BTN_B, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[6]=0;NRF.sendHIDReport(ke­ys);", BTN_B, {edge:"rising",repeat:true,debounce:50})­;
      setWatch("keys[7]=kb.KEY.ENTER;NRF.sendH­IDReport(keys);", BTN_A, {edge:"falling",repeat:true,debounce:50}­);
      setWatch("keys[7]=0;NRF.sendHIDReport(ke­ys);", BTN_A, {edge:"rising",repeat:true,debounce:50})­;

    Currently I only deal with 6 buttons. This is because the HID report can handle up to 6 buttons at a time.
    I will continue to work on it for some improvements and hopefully I will come back soon with some pictures and/or videos just for your curiosity and for my pleasure ;-)

  • BTW, here is a first picture showing my naked gamepad. The enclosure which will be 3d printed will come in the next few days

  • Wow, nice!

  • I suspect that the gamepad running the script above suffers from some delay between button press and actual action by the emulator running on my Android phone. The delay is in the order of hundreds of milliseconds....
    Could it be possible that it comes from the BLE connection interval? If so, is there any chance of decreasing this delay?

  • Could it be possible that it comes from the BLE connection interval?

    Yes, after ~1 minute of inactivity Espruino changes connection interval to a lower power mode. But when there's activity it speeds back up to 7.5ms (the fastest interval).

    Could it be you're just trying it after a bit of inactivity? Is it better after the first few presses?

    You can manually control it with NRF.setConnectionInterval if you need to though.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

BLE, HID descriptor for a (Gameboy like) 8 buttons gamepad?

Posted by Avatar for Jean-Philippe_Rey @Jean-Philippe_Rey