-
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 ;) -
.. 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(0b1111111111111111, 127, -127, 127, -127);
https://raw.githubusercontent.com/stephaneAG/usbHidDescriptors/master/espruino_ble_hid_gamepad.js
-
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(bLabel){ 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(opt){ //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 ]); };
-
;)
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(bLabel){ 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(opt){ //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 ]); };
-
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/Wireless/Bluetooth/RN-HID-User-Guide-v1.0r.pdf):
// Serial points to either an RN42 or a flashed HC-05 with HID support //E.sendUSBHID([ Serial.write( 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 .. ;)
-
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(0b1111111111111111, 127, 127, 127, 127); gamepad.sendGamepadState(0b1111111111111111, -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(reportDescriptorProto); console.log(generatedDescriptor); // result ( make sure it's ok by drag& dropping in https://eleccelerator.com/usbdescreqparser/ ;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/usbHidDescriptorsLast 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 ;)
+ - maybe add possibility of using analog values for buttons as well ?
-
Hi there !
If it helps on this topic & while what I'm about ot say is a little aside, I had success on another project sniffing what was received on the IR receiver pin & then controlling that pin using a NPN or PNP transistor ( can't remember if the IR receiver rested HIGH & the signal was inverted or not ).
I did the above helped with lirc on a rpi & after learning how to configure its mess ( ^^ ), I had something wroking nicely ( actually controlling an LCD board .. )I plan on trying to do so in the coming weeks & as usual 'll post back updates on thee :)
-
Hi there !
From what I can read in https://www.forward.com.au/pfod/ArduinoProgramming/FioV3/RN-HID-User%20Guide-1.1r.pdf ( pages 3, 6, 7, 8, 9, 10, 11 & above - total 17 pages ), we could write a module somewhat working as the AT one ( putting the device in command mode if not already to alter some of its params & then back to other mode - be it serial or serial+spp, and also surely wrapping stuff with helpers depending on the hid report descriptor chosen ? .. )
It seems they advise to use a "consumer report" to report additional keys, & also to support "keymap registers" ( page 11 for both )
On the "gamepad" thing, after digging a little, I stumbled upon this, which is perfect for some of us ;)
https://github.com/evankale/BluetoothPS2Controller/blob/master/BluetoothPS2Controller.inoBe sure to check this video for an explanation of the relationship between hc05, rn42 & cie ;)
nb: I'll investigate the subject as soon as I'm done writing a usb gamepad hid report for Espruino ;)
+
-
hi there !
it seems not :/ ( also absent from https://theasciicode.com.ar/ascii-control-characters/escape-ascii-code-27.html ) & is Unicode U+2122 ( https://en.wikipedia.org/wiki/Trademark_symbol ).
For my current goal, it won't be a problem since the char did appear outside of espruino's IDE ( ex: an open atom or textedit window will work, as will do a webpage .. ) ;)well noted for the charcodes above 128, thx for the hint :)
-
( not fitting in one post .. :/ )
So, the said-prototype produces the following when connected to ( currently a laptop )
ps: among other stuff, there's a "possible easter egg' somewhere in it .. will you find it ? ^^
EDID version: 1.3 Manufacturer: TEF Model 4687 Serial Number 17042401 Made in week 39 of 2015 Digital display Maximum image size: 16 cm x 9 cm Gamma: 2.20 RGB color display First detailed timing is preferred timing Display x,y Chromaticity: Red: 0.6396, 0.3300 Green: 0.2998, 0.5996 Blue: 0.1503, 0.0595 White: 0.3125, 0.3291 Established timings supported: 640x480@60Hz 4:3 HorFreq: 31469 Hz Clock: 25.175 MHz 800x600@60Hz 4:3 HorFreq: 37900 Hz Clock: 40.000 MHz 1024x768@60Hz 4:3 HorFreq: 48400 Hz Clock: 65.000 MHz Standard timings supported: 1280x1024@60Hz 5:4 HorFreq: 64000 Hz Clock: 108.000 MHz 1600x1200@60Hz 4:3 HorFreq: 75000 Hz Clock: 162.000 MHz Detailed mode: Clock 148.500 MHz, 160 mm x 90 mm 1920 2008 2052 2200 hborder 0 1080 1084 1089 1125 vborder 0 +hsync +vsync VertFreq: 60 Hz, HorFreq: 67500 Hz Detailed mode: Clock 85.500 MHz, 160 mm x 90 mm 1360 1424 1536 1792 hborder 0 768 771 777 795 vborder 0 +hsync +vsync VertFreq: 60 Hz, HorFreq: 47712 Hz Monitor ranges (GTF): 23-61Hz V, 26-76kHz H, max dotclock 230MHz Monitor name: VIRTUOSA Has 1 extension blocks Checksum: 0x18 (valid)
-
Hi everyone !
I interested in messing with a VGA/HDMI/.. connector & the data exchanged between a device connected to it, read on ! ;p
As a reminder, EDID looks like that:
header: 00 ff ff ff ff ff ff 00 serial number: 50 a6 87 46 e1 0b 04 01 27 19 version: 01 03 basic params: 80 10 09 78 0a chroma info: ee 91 a3 54 4c 99 26 0f 50 54 established: 21 08 00 standard: 81 80 a9 40 01 01 01 01 01 01 01 01 01 01 01 01 descriptor 1:
For a current project, I need to:
- read EDID data from a monitor/tv screen connected over hdmi ( using i2c )
- modify ( parts of ) this data
- send this data when requested by other devices connected over hdmi ( using i2c )
The goal is more or less an "HDMI DDC EDID MitM" ( we can change a lot of infos found in the original EDID )^^
Along with the above, it should also act as a "fast hdmi switcher", doing EDID handling & messing with hotplug pins ( since EDID is no longer re-read from the monitor when it's switching to a particular source, but instead read once, when the device connecting to the monitor does so in the background )my current prototype consists of:
- a hacked hdmi switch & hdmi breakouts.
- an original Espruino connected to the monitor/tv screen's hdmi i2c & hotplug pins
- an Arduino connected to an hdmi 'source' device ( laptop, game console, .. )
The Espruino listens for a pin rising ( the monitor's hdmi connector's hotplug pin ) before acting as an i2c master to then write & read data a monitor's ( fixed ) i2c adress 0X50.
The Arduino simulates a 'source' device connection by toggling its hdmi connector's hotplug pin before acting as an i2c slave with address 0x50 & provides EDID data whenever being requested to over i2c.
I am currently wondering what'd be the simplest & most robust way to communicate between the Espruino & the Arduino ( knowing that having a 3IN<->1OUT hdmi switch, I plan to use 2 more Arduinos, one for each hdmi IN ).
Every Arduino has only one hdmi device's i2c pins connected to it, and the Espruino is also using different i2c pins to communicate with the screen.I hesitate between SPI & serial, and the only limitation ( at least those I see for now .. ) is using a pin for each on the Espruino & using a watch to listen for an Arduino's request of the ( modded ) monitor's EDID ( store within the Espruino's program & refreshed on boot ).
Moreover, we have limited memory on both the Espruino & the Arduino & although I did get away with modding the Arduino's "Wire" ( i2c ) lib, it seemed I couldn't get more than 256 bytes out of it at once ( and I couldn't figure out if this was coming from the Espruino side ? .. ) & while increasing the buffer seemed to free memory Arduino-side, I still can't get more than ~253 bytes when reading from an i2c address using Espruino ( I still have to run tests using an Arduino as i2c master to better identify the source of the trouble ).
To bypass this current limitation, I re-wrote the Arduino code to act more like my laptop ( & surely other 'source' devices ) does ( writing a byte to select the portion to be read from i2c, then reading max 128 bytes of data ).
As the EDID is 128 bytes big & so does the Extended one, writing 0x00 then reading 128 bytes gives to EDID, and writing 0x80 then reading 128 bytes gives to Extended data.
Also, writing a byte < 256 & then reading a number of bytes gives the n bytes starting from the previously written one ( seems to be the common way for i2c memory chips ? .. ).
This being said, it seems reading 128 bytes again & again without writing any returns the EDID or the Extended One alternatively in a cycleI mostly replicated this behavior ( although I have to fix the code handling switching between sending alternatively each & sending only 128 bytes from a previously received one on the Arduino-side - which works but is still not working wip since it doesn't handle cycling without receiving a byte beforehand for now .. and yet it's not-that-bad-enough to work ;) )
Once the above was nearly ok, I decided to try scanning the available i2c devices in the monitor, but sadly none of the i2c scanners I found for Espruino worked :/ ..
.. so I modified a little bit the EDID sniffing code to instead trigger an i2c scan right after sensing a rise on the monitor's hotplog pin & it seems to work, albeit very slow for now
I'll try to mod an existing i2c scanner to do the work faster & give a prettier output, but part of my other code seems needed, at least for my monitor ( compared to other i2c devices I guess ? .. )Code provided below is for:
- monitor EDID sniffer ( Espruino ): fetches & decodes EDID & E-EDID data
- monitor i2c addr scanner ( Espruino ): lists i2c peripherials in connected screen
- EDID fuzzer ( Arduino ): acts as a connected monitor & send EDID data when asked
- wip js to decode & encode data in the EDID format ( could be improved A LOT .. and it 'll need to if I want it to fit on the original Espruino .. )
- read EDID data from a monitor/tv screen connected over hdmi ( using i2c )
-
Hi there !
A little update on the topic: I now have a working implementation of a modified version of USBKeyboard.js ( modified to handle FR layout & "high-type" feature* )
https://github.com/stephaneAG/serialToWebsockets/blob/master/USBKeyboard2.js
It could be used as a drop-in replacement for USBKeyboard module, and while it's a little bit bigger, it's quite nice to use ( at least to my taste ;p )
*the main goal, aside from having a layout that maps to my keyboard key mapping, is to be able to 'type' stuff while not taking care of which modifier flag(s) have to be set for some specific character: just '.htype()' & this 'll be handled internally :D
usage examples:
var kb = require("USBKeyboard2"); // use as the original USBKeyboard module setWatch(function() { kb.setModifiers(kb.MODIFY.SHIFT, function() { kb.type("HELLO WORLD", function() { kb.setModifiers(0, function() { kb.tap(kb.KEY.ENTER); }); }); }); }, BTN, {debounce:100,repeat:true, edge:"rising"}); // enjoy niceties exports.htype('abcdefghijklmnopqrstuvwxyz'); exports.htype('abcdefghijklmnopqrstuvwxyz'.toUpperCase()); exports.htype('aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'); exports.htype('1234567890'); // combine with .tap() .. and do inception ? ^^ exports.htype('exports.htype("lol");', function(){ exports.tap(KEY.ENTER); }); // combine any char & it will auto handle applying the right modifier flag(s) exports.htype('rgb(0, 255, 128)'); exports.htype('#0FAC3A'); exports.htype('hsl(360, 100%, 50%)'); // not all characters are supported this way, but adding them is quite easy ( ex: for "©" ) exports.htype('Hello World Espruino© USBKeyboard2.js module !');
Support for accents & additional characters is to be added in a near future ;)
Side note: it seems the ™ character isn't supported in the Espruino IDE ? ( it prints "?", which isn't the mapped key ? .. )
Now onto testing "mixed" device ( appearing as a keyboard + mouse combo ) on it & then on puckjs, I hope this 'll be helpful to some folks out there ;P
+ -
Hi there !
-> as you suggested, I'll start another thread for half-dead Pico ;)
-> still, the LEDs are indeed led brightly, the contacts are clean, and I put some tape on the backside of the USB connector since part of it was a little scratched ( even if it shouldn't impact ? .. ), and it's very unlikely that I powered this from something other than a laptop USB port at 500mA ..I had some luck/fun using USBKeyboard.js on an Espruino WiFi, only to find that the mapping is for us/qwerty ;)
-> hence I wrote the following to help toggling between the two: https://github.com/stephaneAG/serialToWebsockets/blob/master/USBKeyboard.js
.. and then realized that I now need to map dot, colon & cie as well ..
.. and also add support for left & right parenthesis as well as '#' ( I plan to use the keyboard feature combined with a TCS34725 color sensor to type rgb or hex color ;P ) ..In short, the following seems to be useful on a system running Ubuntu:
xinput // to get the ID of our device that acts as a keyboard setxkbmap -device 13 -layout us // to set its layout
The sad part is:
- it has to be run after each time the keyboard is "connected"
- it won't work for other devices / mappings
So right now, I'm wondering what'd be the most practical approach to get the most common layouts selectable within the Espruino USBKeyboard.js module ..
I guess I'll investigate the subject right after finishing up the po with the TCS34725 ;)
- it has to be run after each time the keyboard is "connected"
-
;p
thanks for the quick answer :)
I actually did have some luck when lastly trying to connect to a Puck.js from Mac OS Xusing a CSR4.0 USB dongle ( no config was necessary :P )
As soon as I get my hands on it , I'll try setting the hid to a composite device & giving you a feedback on this :)
Also I was wondering how would appear a pico acting as a hid device + an HC-05 with original firmware ? ( I am guessing nothing, since the HC doesn't natively act as a "passthrough" device but rather as an spp one ? )
-> on this also, I guess I'll see what are the results I get ( .. )On another subject, I have a faulty pico that doesn't seems to respond to anything ( and that has red & green leds lit but not pulsating when connected to USB while its button is pressed ): I wonder how to get it back to life ( .. )
So, I'll post updates as soon as I got those :)
++ -
Hi there !
I am currently working in a project that involves acting as a keyboard+mouse combo & sending data over serial.
Since I only have an original Espruino boardon hand & a soldered HC-05 module ( NOT flashed with a RN42 firmware .. ), I can't:- set the HC-05 to some hid device ( I could by flashing it with the RN42 firmware, and also be able to choose whether to describe itself as spp or hid by setting the GPIO11 pin high on power up, but not spp+hid combo here )
- use the Espruino USBKeyboard or USBMouse since theses don't work for the original Espruino board.
I ended up writing a little nodejs app to forward & parse serial stuff to either websockets or xdotool:
- using xdotool, the keyboard & mouse events are OS-wide & seems to work flawlessly
- using websockets, the client ( running in a web page ) creates the necessary DOM events
This solution is quite nice for quick tries & until I get myself a board supporting my needs ;)
Also, it's API is based on Gordon's USBMouse & USBKeyboard implementations ( thx for the code btw - I didn't forget to mention the original author in the files hosted on the repo ;p ), so usage code is almost identical :)It can be found on the following repo: https://github.com/stephaneAG/serialToWebsockets
Last but not least, I'll be looking for an implementation offering Mouse + Keyboard + serial over bluetooth ( and later ble/smart ) & I don't know if I'd be able to do so using, for example, the Puck.js
( as a start, an usb hid for serial+mouse+keyboard 'd be neat )I now know better how to write hid report descriptors, but I'm not sure of how easy it'd be to have such "composite device" working quickly ;)
On the hid subject, I had some fun writing a quick ( hugely wip ) helper to generate a descriptor's hex representation ( if anyone better knowing the subject is interested: https://github.com/stephaneAG/usbHidDescriptors ) :)ps: as the author of https://eleccelerator.com/usbdescreqparser/ states it, the official program to generate hid report descriptors is "unlikely anyone's cup of tea", and anyway is only available for windows .. if anyone knows of a web-based implementation, I'l be glad to know :)
Hoping this helps someone else, any advice is hugely welcome :)
Happy coding everyone ;p - set the HC-05 to some hid device ( I could by flashing it with the RN42 firmware, and also be able to choose whether to describe itself as spp or hid by setting the GPIO11 pin high on power up, but not spp+hid combo here )
-
hi there !
aside note on the RN42:
- it seems it can be used as bluetooth hid device from firmware version 6.03+ ( keyboard, gamepad, mouse, mouse+keyboard combo, digitizer, sensor, "use cfg* ?" ) additionally to SPP.
- it also supports switching between spp & hid if the GPIO11 pin is high during power up
( I am currently looking for a way to use an original Espruino board as a bluetooth hid device additionally to a serial one & then remembered it didn't support that, contrary to Pico, WiFi & Puck :/ - so the option of switching between spp & hid is quite nice although we're locked to the supported hid report descriptors .. )
hoping this helps someone ;)
*don't know yet what this stands for ( consumer report ? .. )
- it seems it can be used as bluetooth hid device from firmware version 6.03+ ( keyboard, gamepad, mouse, mouse+keyboard combo, digitizer, sensor, "use cfg* ?" ) additionally to SPP.
-
all right, if anyone needs more or less the same hack for servo.move, I have the following code ( not yet tested but worked flawlessly within a browser ;p )
var interval, currentPos; var offs = 1, mul = 1; var options = {}; //var options = { range: 3}; if (options && options.range) { mul = options.range; offs = 1.5-(mul/2); } // helpers // replacement for E.clip when not in Espruino env var clip = function(val, min, max){ if(val < min) val = min; if(val > max) val = max; return val; }; // the good-ol' Ar map() ! var map = function(x, in_min, in_max, out_min, out_max){ //return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; var mapped = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; return mapped.toFixed(3); } var move = function(pos, time, callback) { if (time===undefined) time = 1000; // T: if no time is set as duration for the move, set 1 sec var amt = 0; if (currentPos===undefined) currentPos = pos; // T: if no currentPos is defined, set it to the goal position if (interval) // T: if already existing interval, cancel it clearInterval(interval); var initial = currentPos; // T: set initial position for the move interval = setInterval(function() { // T: schedule re-calls if (amt>1) { // T: move fully executed ( & maybe a little farther ? ) clearInterval(interval); // T: no more re-calls interval = undefined; // T: reset interval amt = 1; // T: set move to fully executed if (callback) callback(); // T: move ended callback } currentPos = pos*amt + initial*(1-amt); // T: calculate currentPos from initialPos, pos & current amount ( steps already took ) //digitalPulse(pin, 1, offs+E.clip(currentPos,0,1)*mul); // T: function digitalPulse(pin, value_highOrLow, time_duration), clip to min & max //digitalPulse(pin, 1, offs+clip(currentPos,0,1)*mul); // T: function digitalPulse(pin, value_highOrLow, time_duration), clip to min & max console.log('HIGH pulse width: ' + ( offs+clip(currentPos,0,1)*mul ) ); console.log('amt: ' + amt); // IDEA: clip/map to 0.03..0.115 ? console.log('hacky analogWrite value:' + map(amt, 0, 1, 0.03, 0.15) ); amt += 1000.0 / (20*time); // T: next step }, 20); }; // test-drive .. move(1, 500, function(){ console.log('movement done !'); })
I'll test the above as soon as I can, although not that efficient, it should hopefully do the trick :)
On the "web side of things", still no success using the attached code ( "kinda" works .. sometimes .. for some values .. so actually more random that working ;p ), and the tests I could run using Audacity to generate tones were no that concluant :/ ..
nb: on a quick try, I also didn't get any signal using the sound card from the pin driven by Espruino "analogWrite" command, even driving it repeatedly ( maybe it needs some amplification ? )
ps: I'm doing stuff on a macbookpro 2K11 ( don't know yet if it means some specific stuff on audio input/output ( .. ) )
-
Hi !
I'm currently trying to get some servos working nicely with an original Espruino board ( and also within a chrome browser ).
The overall goal is to have a video & synchronized servo movements ( controlled, if can do so, by the audio channel within the video, or within a "tied" audio file, or on-the-fly-generated signals )
I solved the "45° mystery" thanks to http://forum.espruino.com/conversations/316585/, which now allows me to move each of the servos I have using the following:
/* micro servo SG90 on C8: Position "0" ( 1.5 ms pulse ) is middle, "90" (~2 ms pulse) is all the way to the right, "-90" (~1 ms pulse) is all the way to the left. */ //var s2 = require("servo").connect(C9); // servo.move() doesn't work for my microservo ( SG90 ): here's what works analogWrite(C9, 0.03, {freq:50}); // -> full left ( -90deg ) analogWrite(C9, 0.07, {freq:50}); // -> middle ( 0deg ) analogWrite(C9, 0.115, {freq:50}); // -> full right ( 90deg )
Now, I'm looking for a replacement for the servo.move() fcn ( or a tweak of it ) that'd allow me to go to a certain position & taking some time to do so - not sure of what to change, one of my guesses is tweaking the following part ?
digitalPulse(pin, 1, offs+E.clip(currentPos,0,1)*mul); // IDEA: clip to 0.03..0.115 ?
On a close subject, I'm also trying to get the same servos to move:
- using an audio file ( or an audio channel of a video ) with "recorded" movements from the servos ( when controlled from an Espruino running some code to generate the signals )
- using the WebAudioAPI to generate the necessary signals
I stumbled on 2 related posts ( http://www.massmind.org/techref/io/servo/wav.htm & https://github.com/pendragon-andyh/WebAudio-PulseOscillator ) which led me to messing around & try stuff, but I didn't achieve ( yet :/ ) my goal of controlling the said servos using purely on-th-fly-generated signals from the WebAudioAPI
Last but not least, I'm aware of the 'ontimeupdate' evt that's available when a video is playing ( & yup, I plan to use it to trigger generating on-the-fly signals sync-ed with the video content )
This being said, here's the code I have so far, I'll be hacking around with it today & the coming days, hopefully coming to a success .. ( .. thanks to hints from here ? ;p )
Thanks in advance for reading this,
Looking forward to reading anything on the subjectps: test files attached ;)
- using an audio file ( or an audio channel of a video ) with "recorded" movements from the servos ( when controlled from an Espruino running some code to generate the signals )
-
back
Nb: other point, but I think my above drawing is completely wrong ( .. )
'this being said",From what I could read, 38kHz is a far reach for the esp8266, if its said max is actually 100Hz ( & I'm not talking about code taking too much time to exec & disrupting the WiFi part .. )
From the post linked above, it seems using 'gpio_write(pin, 1)' & 'os_delay_us(13)' for HIGH & then for LOW could do the trick on the esp8266 platform - maybe as a tiny module or tweak to the build ?I'm also wondering which of the following could best suit the need ( & work as intended, if working at all, on most of the platforms .. ):
// way A var arr38kHz = [0.013,0.013,0.013,0.013, ..]; // huuge array ? function sendSig(sigArr){ digitalPulse(irLed_anodePin, 1, arr38kHz); // start 38kHz digitalPulse(irLed_cathodePin, 1, sigArr); // send signal digitalPulse(irLed_cathodePin, 1, 0); // wait until signal finished before further code execution digitalRead(irLed_anodePin); // stop 38kHz // or digitalWrite(irLed_anodePin, 0); // if I'm not wrong ;) // or digitalPulse(irLed_anodePin, 0, 0); // as well as above ;) } // way B var interval function sendSig(sigArr){ interval = setInterval(function(){ digitalPulse(irLed_anodePin, 1, 0.013); digitalPulse(irLed_anodePin, 0, 0.013); }, 0.026); // start 38kHz digitalPulse(irLed_cathodePin, 1, sigArr); // send signal digitalPulse(irLed_cathodePin, 1, 0); // wait until signal finished before further code execution clearInterval(interval); // stop 38kHz digitalRead(irLed_anodePin); // make sure it's off } // way C var signal = [.., ..]; for(var i=0; i< signal.length; i++){ if( i.isEven() ){ // we need a HIGH digitalWrite(irLed_cathodePin, 1); for(var j=0; j < signal[i]/2; j++){ digitalPulse(irLed_anodePin, 1, 0.013); digitalPulse(irLed_anodePin, 0, 0.013); } } else { // we need a LOW digitalPulse(irLed_cathodePin, 0, signal[i]); // stay Low for a while /* or, but not mandatory since we're LOW anyway ? digitalWrite(irLed_cathodePin, 0); for(var j=0; j < signal[i]/2; j++){ digitalPulse(irLed_anodePin, 1, 0.013); digitalPulse(irLed_anodePin, 0, 0.013); } */ } } // way D - using only one leg of the IR LED & the other to Gnd ? var signal = [.., ..]; for(var i=0; i< signal.length; i++){ if( i.isEven() ){ // we need a HIGH for(var j=0; j < signal[i]/2; j++){ digitalPulse(irLed_cathodePin, 1, 0.013); digitalPulse(irLed_cathodePin, 0, 0.013); } } else { // we need a LOW digitalPulse(irLed_cathodePin, 0, signal[i]); // stay Low for a while } }
I'll test as soon as I can, but sadly I'm surely not as confident in tweaking the esp build or writing a quick module for it for now :/ ..
-
little update:
as an cheap alternative to transmitting the actual IR signals, it seems I can use the learn mode to learn other IR signals ( if those are both supported & long enough - like holding a btn on an actual remote until hearing a buzzing sound as confirmation that some IR signal was learned successfully ): after some quick tries, it doesn't seem to be working all the time ( more like 33% ), even using a code that used to work ( one sniffed from the device, not one learned )
since the IR signals can be learned, I suppose it'd be a good idea to look for custom IR signals that could be easily transmitted from whatever board ( I plan to test on original Espruino, Pico, EspruinoWiFi, & esp8266 esp-01, esp-12 & Wemos D1 Mini Pro ), although it 'd be awesome to easily support the actual signals of some device :)
I gotta digg the following link to experiment with the signals I already have & custom ones - any hint welcome ;) https://internetofhomethings.com/homethings/?p=899
on a related subject, it seems someone has ported /is porting Ken Shirriff's IR remote lib for the esp8266 https://github.com/markszabo/IRremoteESP8266
Aside from this, it seems I have troubles with an EspruinoWiFi: "NetworkJS" & 'http' modules not found ? ( though, it seems it can connect but I can't for ex call 'wifi.getIP()' for some reason :/ ).
The WeMos is ok ( wifi & hosting the UI ) but doesn't yet support sending IR signals, so pretty much useless for now .. ( .I didn't try again using the original Espruino board + esp8266 esp-01 for WiFi, I'll do & hope it works on 1st try ;p )Last but not least, I also have to check if the IR signals learnt can be / are reset when plugging off the tv support power supply ( .. )
On the html/UI side, the code used 'll be published on the github repo later tonight, & I also plan to try using some jsonp to get an html page bigger than can be hosted as is on the Espruino ( nb: my 1st try on the WeMos ran out of memory :/ .. ) - I'll be posting the logic here as well
"onto the next update " ..
-
@ClearMemory041063 -> nice work !
I plan to use a LSM9DS0 9-DOF ( from adafruit ) for another project & I wonder how much work it'd be ( & how close it is to the LSM9DS1 ) to adapt you nearly-module-code to handle it ? ( also I'd be willing to help/do so if it's not "too advanced" for me for now ;) )this being said, kudos ++ ;)
-
Hi there ! :)
Now back, I was glad to receive 2 Espruino WiFI boards :D ( @Gordon #keepUpTheGoodWork #supportingEspruino ;P )
While I didn't have time yet to test my code on the Espruino Wifi or try the external oscillator way, I did a quick visual recap of the idea ( feel free to correct me if I'm wrong - I really may be so :/ .. ) - the attached .pdf file.
On that a question: when setting a pin to oscillate, it jump-starts HIGH, right ?
( since the logic - if I'm not false - is: "when oscillating pin goes LOW & signal pin is HIGH, IR LED is lit", I try that guess using "visual cues" ;) ( .. ) )If so, then I'll only have to write code that actually mixes some signal[] to some passed freq of oscillation & return the "mixed" signal ( I guess it'd be better to pre-process the existing one instead of doing so "on the fly" ( .. ) ), then test it out :)
I plan to test out the aboves in few days & really hope the esp8266-emitted signals 'll do the trick, but now that I have the said Espruino WiFi, I'd really consider pushing the project further, adding*:
- accel/gyro/mag detection ( LSM9DS0 9-DOF )
- ble support ( HC-05 )
- rgb LED ==> to be added anyway ;)
& maybe opening up the actual tv support to directly interface with its motors ? => 'de be GREAT ( to learn stuff as well as to bypass some limitations of the "available range of positions" to which the tv can be set to ( .. ) )
Last but not least, if the signals can be sent correctly for the esp8266 while its also handling changing the rgb led colors & the WiFi stuff, I still have to figure out where the troubles came from in my "quick svg ui" for the tv support ( I'll post that as well as an updated version soon ;) ), but I guess this should be the least of the troubles ..
This being said, whishing any reader a very nice day :)
*I guess now I'll also have to try interfacing these with the WeMos D1 as well as do so on the Espruino WiFi ( .. ) ;p
- accel/gyro/mag detection ( LSM9DS0 9-DOF )
-
hi !
yup, I think it may work ( although I'd have to workout the resulting Float32Array from the signals[] array + 38kHz pulses ) ;)
For the second option, maybe connecting a 555 'd work ? ( but I'd truly prefer the 1st option .. )
As said above, I'll investigate more as I get back home ( where all my uCs are .. )
Nevertheless, thanks for the quick answer ;)
back !
So, first, an early version of the Espruino GamePad API ( could be used when using either USB HID, BLE HID, & RN-42 HID .. and soon XInput to act as an Xbox360 controller ;p ):
https://raw.githubusercontent.com/stephaneAG/usbHidDescriptors/master/espruino_hid_gamepad_draftApi.js
Second, I am currently debugging a test implementation of an Xbox360 controller using Teensy+Espruino ( Thee teensy emulates the controller on the Xbox360 USB port while the Espruino controls the Teensy using Serial ).
To be able to use only the Espruino, I am investigating some STM32 stuff as well as the Espruino sources.
I found ( parts of ? ) the stuff related to USB, HID & CDC, but this involves knowing Espruino & STM32F USB stuff more than I currently do :/ ..
So, if anyone interested in working on the opportunity to get more advanced USB stuff on the Espruino, these files seems of particular interest:
The 3rd link is particularly interesting on the lines starting from 1544
I'll try some of the STM32 USB stuff as soon as can do & 'll come back for updates on the subject.
On a close subject, anyone with a working HID implementation of a PS3 controller ?