Avatar for stephaneAG

stephaneAG

Member since Jun 2014 • Last active Sep 2018
  • 14 conversations
  • 129 comments

'love hackety trick

Most recent activity

  • in Projects
    Avatar for stephaneAG

    Hi there ( not many answers here .. ;) ): is anyone used to modding the way the Espruino enumerates over USB ?
    To go further on the gamepad subject & as I can't test the pucks nor the RN42 version of the module, I think it'd be quite neat to implement an Xbox 360 gamepad module for Espruino :)) ..
    .. but this controller is NOT a HID device but instead a XInput one .. :/

    ( I have to finish reading some code for a TeensyLC & need few infos on the way Espruino handles the USB stack & cie to port the code ).
    I'll also plan to digg the std usb & hid stuff for STM32F to help in the process ;)

  • in Projects
    Avatar for stephaneAG

    .. and the Puckjs version is below ( I'll have to find a way to update the firmware of my puckjs since I have no ble on my phone & just access it via a dongle on my laptop .. ) -> can someone test it ?

    var gamepad = require("ble_hid_gamepad");
    NRF.setServices(undefined, { hid : gamepad.report });
    gamepad.sendGamepadState(0b1111111111111­111, 127, -127, 127, -127);
    

    https://raw.githubusercontent.com/stepha­neAG/usbHidDescriptors/master/espruino_b­le_hid_gamepad.js

  • in Projects
    Avatar for stephaneAG

    Back again: I updated the code to support the joysticks but still not luck with the troubles expressed above :/

    the current api

    gamepad.A.press(); // press a btn - 1st way
    gamepad.A.release(); // release a btn - 1st way
    gamepad.press([BUTTONS.A]); // release a btn - 2nd way
    gamepad.release([BUTTONS.A]); // release a btn - 2nd way
    gamepad.press([BUTTONS.A, BUTTONS.SELECT, BUTTONS.START]); // press multiple
    gamepad.release([BUTTONS.A, BUTTONS.SELECT, BUTTONS.START]); // release multiple
    gamepad.LJOY_X.move(45); // move joytick axis
    gamepad.set({START:1, SELECT: 1, LJOY_X: 120}); // set many stuff at once
    gamepad.unset(); // reset all to default
    gamepad.sendState(); // send current buttons/joysticks/triggers positions over usb
    
    var button = function(bLabel, bValue){ this.bLabel = bLabel; this.bValue = bValue & 0xFFFF; };
    button.prototype.bLabel = '';
    button.prototype.bValue = 0;
    // set corresponding bit in gamepad obj
    button.prototype.press = function(){
      //gamepad.btnState = ( gamepad.btnState | (1<<this.bValue) ) & 0xFFFF;
      //console.log('WTF: gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF -->' + ( gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF) );
    console.log('WTF: gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF -->' + ( gamepad.btnState | 1<<this.bValue) );
      //gamepad.btnState = gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF;
      gamepad.btnState = gamepad.btnState | (1<<this.bValue) & 0xFFFFFFFF;
      console.log('button const value: ' + this.bValue);
      console.log('gamepad state: 0b' + gamepad.btnState.toString(2) );
    }
    // unset corresponding bit in gamepad obj
    button.prototype.release = function(){
      gamepad.btnState = ( gamepad.btnState & ~(1<<this.bValue)) & 0xFFFF;
      console.log('button const value: ' + this.bValue);
      console.log('gamepad state: 0b' + gamepad.btnState.toString(2) );
    }
    
    
    var joystick = function(bLabel, objAttr){ this.bLabel = bLabel; this.objAttr = objAttr; };
    joystick.prototype.bLabel = '';
    joystick.prototype.objAttr = null;
    joystick.prototype.move = function(value){
      console.log('joystick ' + this.bLabel + ' value: ' + value);
      console.log('joystick ' + this.objAttr + ' value: ' + value);
      //gamepad[this.bLabel] = value;
      gamepad[this.objAttr] = value;
      //this.objAttr = value;
    };
    
    var gamepad = {
      lastBtnState: 0b0000000000000000,
      btnState: 0b0000000000000000,
      x1: 0,
      y1: 0,
      x2: 0,
      y2: 0
    }
    
    // pass an array of btn to be pressed
    // depending on the const(s) passed, we set the bits mapped from the const's value
    gamepad.press = function(btnsArr){
      for(var i=0; i< btnsArr.length; i++){
        this.btnState = ( this.btnState | (1<<btnsArr[i]) ) & 0xFFFF;
        console.log('gamepad state: 0b' + this.btnState.toString(2) );
      }
    };
    // pass an array of btn to be pressed
    // depending on the const(s) passed, we unset the bits mapped from the const's value
    gamepad.release = function(btnsArr){
      for(var i=0; i< btnsArr.length; i++){
        this.btnState = ( this.btnState & ~(1<<btnsArr[i]) ) & 0xFFFF;
        console.log('gamepad state: 0b' + this.btnState.toString(2) );
      }
    };
    // "populate" the gamepad object with buttons children offering press() & release() methods
    Object.keys(BUTTONS).forEach(function(bL­abel){
      gamepad[bLabel] = new button(bLabel, BUTTONS[bLabel]);
    });
    // "populate" the gamepad object with joysticks children offering move(), moveX() & moveY()  methods
    var JOYSTICKS = { LJOY_X: 'x1', LJOY_Y: 'y1', RJOY_X: 'x2', RJOY_Y: 'y2' };
    Object.keys(JOYSTICKS).forEach(function(­bLabel){
      //gamepad[bLabel] = new joystick(bLabel, gamepad[ JOYSTICKS[bLabel] ]);
      gamepad[bLabel] = new joystick(bLabel, JOYSTICKS[bLabel]);
    });
    // pass an array of elements to be set ( updated )
    gamepad.set = function(optsObj){
      var that = this;
      Object.keys(optsObj).forEach(function(op­t){
        //that[opt] = optsObj[opt]; // nope: overwrites our objs -> we want to
        // if the label is present in the BUTTONS consts, then we act depending on the value passed to set or unset correspondign bit
        if(typeof BUTTONS[opt] !== 'undefined'){
          if(optsObj[opt] === 1) that.btnState = that.btnState | (1<<BUTTONS[opt]);
          else that.btnState = that.btnState & ~(1<<BUTTONS[opt] ) & 0xFFFF;
        }
        // else, somehow map to the correct joystick & set its value directly
        // Thnk: accept 'LJOY':[x, y] ?
        else {
          (opt === 'LJOY_X') ? that.x1 = optsObj[opt] :
          (opt === 'LJOY_Y') ? that.y1 = optsObj[opt] :
          (opt === 'RJOY_X') ? that.x2 = optsObj[opt] :
          (opt === 'RJOY_Y') ? that.y2 = optsObj[opt] : null ;
        }
      });
    };
    // resets the gamepad ( unset all pressed stuff & cie - aka release all btns & center joysticks )
    gamepad.unset = function(optsObj){
      this.btnState = 0b0000000000000000;
      this.x1 = 0;
      this.y1 = 0;
      this.x2 = 0;
      this.y2 = 0;
    };
    // send the current state of the gamepad object over usb
    gamepad.sendState = function(){
      E.sendUSBHID([
        this.btnState & 0xFF,      // Byte0
        (this.btnState>>8) & 0xFF, // Byte1
        this.x1,                   // Byte2
        this.y1,                   // Byte3
        this.x2,                   // Byte4
        this.y2,                   // Byte5
      ]);
    };
    
  • in Projects
    Avatar for stephaneAG

    ;)

    what's less nice is the following wip code that's driving me nuts: it seems I can't find the reason why I can't have 'gamepad.btnState' bigger than 256 ?!
    ( and also, stuff has to be done regarding the joysticks to be able to give them methods ( like is done for btns )

    --> if anyone wanna finish the following ( mostly find a fix ), I'd be glad :)
    nb: also, if someone has a better pattern in mind, help's as always appreciated ;)

    /*
    
        == Draf API ==                                                              == current API ==
        gamepad.press(<btnConst>); // press one                                     --> gamepad.release([BUTTONS.A]);
        gamepad.press([<btnConst>, <joyConst>, ..]); // press many                  --> gamepad.press([BUTTONS.A, BUTTONS.SELECT, BUTTONS.START]);
        gamepad.<btnConst>.press(); // press one                                    --> gamepad.A.press();
        gamepad.<joyConst>.press(); // same for joysticks buttons
        gamepad.<btnConst>.release // release one                                   --> gamepad.A.release();
        gamepad.release(<btnConst>, <joyConst>, ..]); // release many               --> gamepad.release([BUTTONS.A, BUTTONS.SELECT, BUTTONS.START]);
        // joysticks only: range [-127..127]
        gamepad.<joyConst>.move(<xValue>, <yValue>);
        gamepad.<joyConst>.moveX(<value>);
        gamepad.<joyConst>.moveY(<value>);
        // triggers only: range [0..255]
        gamepad.<trigConst>.press(<value>); // if analog, 'll range [0..255]
        // global
        gamepad.set({ <const>:<value>, .. });                                       --> gamepad.set({START:1, SELECT: 1, LJOY_X: 120})
        gamepad.unset();                                                            --> gamepad.unset();
        // update
        gamepad.sendState(); // or '.update()' ?                                    --> gamepad.sendState();
    */
    
    var BUTTONS = {
      START:      0x0001, //
      SELECT:     0x0002, //
    
      PAD_UP:     0x0004, //
      PAD_DOWN:   0x0008, // works gamepad.btnState = ( gamepad.btnState | (1<<0x0008) )
      PAD_LEFT:   0x0010, // stop working from here ? :| but gamepad.btnState = ( gamepad.btnState | (1<<0x0010) ) works ??!
      PAD_RIGHT:  0x0020, //
    
      Y:          0x0040, //
      YELLOW:     0x0040, //
      A:          0x0080, //
      GREEN:      0x0080, //
      X:          0x0100, //
      BLUE:       0x0100, //
      B:          0x0200, //
      RED:        0x0200, //
    
      L1:         0x0400, //
      R1:         0x0800, //
    
      L2:         0x1000, //
      R2:         0x2000, //
    
      L3:         0x4000, //
      R3:         0x8000, //
    };
    
    var button = function(bLabel, bValue){ this.bLabel = bLabel; this.bValue = bValue & 0xFFFF; };
    button.prototype.bLabel = '';
    button.prototype.bValue = 0;
    // set corresponding bit in gamepad obj
    button.prototype.press = function(){
      //gamepad.btnState = ( gamepad.btnState | (1<<this.bValue) ) & 0xFFFF;
      //console.log('WTF: gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF -->' + ( gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF) );
    console.log('WTF: gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF -->' + ( gamepad.btnState | 1<<this.bValue) );
      //gamepad.btnState = gamepad.btnState | ( (1<<this.bValue) & 0xFFFF) & 0xFFFF;
      gamepad.btnState = gamepad.btnState | (1<<this.bValue) & 0xFFFFFFFF;
      console.log('button const value: ' + this.bValue);
      console.log('gamepad state: 0b' + gamepad.btnState.toString(2) );
    }
    // unset corresponding bit in gamepad obj
    button.prototype.release = function(){
      gamepad.btnState = ( gamepad.btnState & ~(1<<this.bValue)) & 0xFFFF;
      console.log('button const value: ' + this.bValue);
      console.log('gamepad state: 0b' + gamepad.btnState.toString(2) );
    }
    
    
    var joystick = function(bLabel, bValue){ this.bLabel = bLabel; this.bValue = bValue & 0xFFFF; };
    button.prototype.bLabel = '';
    button.prototype.bValue = 0;
    
    var gamepad = {
      lastBtnState: 0b0000000000000000,
      btnState: 0b0000000000000000,
      x1: 0,
      y1: 0,
      x2: 0,
      y2: 0
    }
    
    // pass an array of btn to be pressed
    // depending on the const(s) passed, we set the bits mapped from the const's value
    gamepad.press = function(btnsArr){
      for(var i=0; i< btnsArr.length; i++){
        this.btnState = ( this.btnState | (1<<btnsArr[i]) ) & 0xFFFF;
        console.log('gamepad state: 0b' + this.btnState.toString(2) );
      }
    };
    // pass an array of btn to be pressed
    // depending on the const(s) passed, we unset the bits mapped from the const's value
    gamepad.release = function(btnsArr){
      for(var i=0; i< btnsArr.length; i++){
        this.btnState = ( this.btnState & ~(1<<btnsArr[i]) ) & 0xFFFF;
        console.log('gamepad state: 0b' + this.btnState.toString(2) );
      }
    };
    // "populate" the gamepad object with children offering press() & release() methods
    Object.keys(BUTTONS).forEach(function(bL­abel){
      gamepad[bLabel] = new button(bLabel, BUTTONS[bLabel]);
    });
    // pass an array of elements to be set ( updated )
    gamepad.set = function(optsObj){
      var that = this;
      Object.keys(optsObj).forEach(function(op­t){
        //that[opt] = optsObj[opt]; // nope: overwrites our objs -> we want to
        // if the label is present in the BUTTONS consts, then we act depending on the value passed to set or unset correspondign bit
        if(typeof BUTTONS[opt] !== 'undefined'){
          if(optsObj[opt] === 1) that.btnState = that.btnState | (1<<BUTTONS[opt]);
          else that.btnState = that.btnState & ~(1<<BUTTONS[opt] ) & 0xFFFF;
        }
        // else, somehow map to the correct joystick & set its value directly
        // Thnk: accept 'LJOY':[x, y] ?
        else {
          (opt === 'LJOY_X') ? that.x1 = optsObj[opt] :
          (opt === 'LJOY_Y') ? that.y1 = optsObj[opt] :
          (opt === 'RJOY_X') ? that.x2 = optsObj[opt] :
          (opt === 'RJOY_Y') ? that.y2 = optsObj[opt] : null ;
        }
      });
    };
    // resets the gamepad ( unset all pressed stuff & cie - aka release all btns & center joysticks )
    gamepad.unset = function(optsObj){
      this.btnState = 0b0000000000000000;
      this.x1 = 0;
      this.y1 = 0;
      this.x2 = 0;
      this.y2 = 0;
    };
    // send the current state of the gamepad object over usb
    gamepad.sendState = function(){
      E.sendUSBHID([
        this.btnState & 0xFF,      // Byte0
        (this.btnState>>8) & 0xFF, // Byte1
        this.x1,                   // Byte2
        this.y1,                   // Byte3
        this.x2,                   // Byte4
        this.y2,                   // Byte5
      ]);
    };
    
    
  • in Interfacing
    Avatar for stephaneAG

    back :)

    a quick update on the subjetc: I just finished writing a USB HID GamePad module ( yaaaaaayyyyy! ^^ ), and I'll digg that RN42 thing as soon as can do.

    this being said, I now better grasp some stuff from the data provided:

    using the RN42 HID firmware & sending a 'raw' hid report is done as ( p7 of their manual at https://cdn.sparkfun.com/datasheets/Wire­less/Bluetooth/RN-HID-User-Guide-v1.0r.p­df):

    E.sendUSBHID([
        0xFD,                // indicates raw hid report
        0x06,                 // bLength
        0x01,                 // bDescriptorType - constant ( String assigned by USB )
        x1,                     // Byte0
        y1,                     // Byte1
        x2,                    // Byte2
        y2                      // Byte3
        btnState & 0xFF,             // Byte4
        (btnState>>8) & 0xFF, // Byte5
    ]);
    
    

    to the contrary, USB HID ( & maybe Espruino's bluetooth HID )

    E.sendUSBHID([
        // 0xFD,                // indicates raw hid report
        //0x06,                 // bLength
        //0x01,                 // bDescriptorType - constant ( String assigned by USB )
        btnState & 0xFF,      // Byte0
        (btnState>>8) & 0xFF, // Byte1
        x1,                   // Byte2
        y1,                   // Byte3
        x2,                   // Byte4
        y2                   // Byte5
    ]);
    

    One good thing to do 'd be summarizing the REALLY interesting parts from their doc to gain a huge amount of scroll time & cie .. ;)

  • in Projects
    Avatar for stephaneAG

    Hi there !

    I was finally able to find the time to implement a "standard" USB HID GamePad as an Espruino module ( js only for now ) :D
    Currently, the triggers are treated as digital buttons, but I'll soon take the time to update the code to support analog triggers ;)

    So, YES, Espruino's should now be able to play CoD & GTA ;p .. and make some perfects on guitar hero hardcore ? -> better: make it count & let's do an Espruino-adaptive-controller !!! ;P ( seriously, I'm on it ;) )

    => enjoy :)

    basic usage is as simple as Gordon's keyboard & mouse modules:

    var gamepad = require('USBGamepad');
    // debug easily using http://html5gamepad.com/ ;p
    gamepad.sendGamepadState(0b1111111111111­111, 127, 127, 127, 127);
    gamepad.sendGamepadState(0b1111111111111­111, -127, -127, -127, -127);
    

    I plan to:

    • maybe add possibility of using analog values for buttons as well ?
    • or so only for the "big" triggers ?

    I also consider writing a little wrapper to make it easier to work with if needed:

    // press & release buttons
    gamepad.press(<btnConst>); // press one
    gamepad.press([<btnConst>, <joyConst>, ..]); // press many
    gamepad.<btnConst>.press(); // press one
    gamepad.<joyConst>.press(); // same for joysticks buttons
    gamepad.<btnConst>.release // release one
    gamepad.release(<btnConst>, <joyConst>, ..]); // release many
    // joysticks only: range [-127..127]
    gamepad.<joyConst>.move(<xValue>, <yValue>);
    gamepad.<joyConst>.moveX(<value>);
    gamepad.<joyConst>.moveY(<value>);
    // triggers only: range [0..255]
    gamepad.<trigConst>.press(<value>); // if analog, 'll range [0..255]
    // global
    gamepad.set({ <const>:<value>, .. });
    // update
    gamepad.sendState(); // or '.update()' ?
    

    Advises are welcome to the above draf of api :)

    I also wonder if it'd be somewhat useful to retain the last gamepad state before any further update, as well as the timestamp & the timestamp diff with previous one ? ..

    The module should also work to send stuff to an RN42* ( or an HC-05 with its HID firmware ) or an Espruino board based on bluetooth

    • just add 0xFD, 0x06 & 0x01 before the stuff to be sent ;)

    I'll also try to get 2 gamepads & adding the possibility to pass a number of those to the module before it calls 'E.setUSBHID()' after a little rest ( digging through the USB HID docs can make one's eyes burn .. )

    On a near subject ( & sorry if I repeat myself ), I have a wip lib aimed to generate hid report descriptors ( currently just easing the mapping between constants & their values ): it's not much but it 'd have been very helpful to write the above ;/ ( I'll add the constants digged to it as soon as can do ;p )

    var reportDescriptorProto = [
      { USAGE_PAGE: 'Generic Desktop' },
      { USAGE: 'Mouse' },
      { COLLECTION: 'Application' },
        { USAGE: 'Pointer' },
        { COLLECTION: 'Physical' },
          { USAGE_PAGE: 'Button' },
          { USAGE_MINIMUM: 1 },
          { USAGE_MAXIMUM: 3 },
          { LOGICAL_MINIMUM: 0 },
          { LOGICAL_MAXIMUM: 1 },
          { REPORT_COUNT: 3 },
          { REPORT_SIZE: 1 },
          { INPUT: 'Data,Variable,Absolute' },
          { REPORT_COUNT: 1 },
          { REPORT_SIZE: 5 },
          { INPUT: 'Constant,Variable,Absolute', },
          { USAGE_PAGE: 'Generic Desktop' },
          { USAGE: 'X' },
          { USAGE: 'Y' },
          { LOGICAL_MINIMUM: -127 },
          { LOGICAL_MAXIMUM: 127 },
          { REPORT_SIZE: 8 },
          { REPORT_COUNT: 2 },
          { INPUT: 'Data,Variable,Relative' },
        { END_COLLECTION: null },
      { END_COLLECTION: null },
    ];
    
    var generatedDescriptor = genReportDescriptor(reportDescriptorProt­o);
    console.log(generatedDescriptor);
    
    // result ( make sure it's ok by drag& dropping in https://eleccelerator.com/usbdescreqpars­er/ ;p )
    /*
    ["0x05", "0x01", "0x09", "0x02", "0xa1", "0x01", "0x09", "0x01", "0xa1", "0x00", "0x05", "0x09", "0x19", "0x01", "0x29", "0x03", "0x15", "0x00", "0x25", "0x01", "0x95", "0x03", "0x75", "0x01", "0x81", "0x2", "0x95", "0x01", "0x75", "0x05", "0x81", "0x3", "0x05", "0x01", "0x09", "0x30", "0x09", "0x31", "0x15", "0x81", "0x25", "0x7f", "0x75", "0x08", "0x95", "0x02", "0x81", "0x6", "0xc0", "0xc0"]
    */
    

    Every contribution is welcome ;)
    https://github.com/stephaneAG/usbHidDesc­riptors

    Last but not least, if anyone has suggestions on the 'hid way' to get output descriptors working on Espruino, it 'd be daaaaaaaamn cool :) ( the goal is to be able to send stuff from host to Espruino - and for the moment, the 'stuff' is not quite defined ^^ )

    ps: while at it & if enough time to do so, I have some idea of a feedback mouse ;)
    +

Actions