diff --git a/package-lock.json b/package-lock.json index 0a832fb..0d0af46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "joy-con-webhid", - "version": "0.3.1", + "version": "0.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "joy-con-webhid", - "version": "0.3.1", + "version": "0.4.0", "license": "Apache-2.0", "dependencies": { "ahrs": "^1.3.1" }, "devDependencies": { - "eslint": "^8.23.1", + "eslint": "^8.24.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^8.5.0", "http-server": "^14.1.1", @@ -62,9 +62,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz", + "integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -498,13 +498,13 @@ } }, "node_modules/eslint": { - "version": "8.23.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz", - "integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/config-array": "^0.10.5", "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", @@ -1879,9 +1879,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz", + "integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -2216,13 +2216,13 @@ "dev": true }, "eslint": { - "version": "8.23.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz", - "integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.4", + "@humanwhocodes/config-array": "^0.10.5", "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", diff --git a/package.json b/package.json index b7a84da..7b565d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "joy-con-webhid", - "version": "0.3.1", + "version": "0.4.0", "description": "Joy-Con over WebHID ", "scripts": { "start": "npx http-server", @@ -36,7 +36,7 @@ }, "homepage": "https://github.com/tomayac/joy-con-webhid#readme", "devDependencies": { - "eslint": "^8.23.1", + "eslint": "^8.24.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^8.5.0", "http-server": "^14.1.1", diff --git a/src/connectRingCon.js b/src/connectRingCon.js index a1cc8fd..eba502e 100644 --- a/src/connectRingCon.js +++ b/src/connectRingCon.js @@ -4,88 +4,106 @@ export const connectRingCon = async (device) => { const defineSendReportAsyncFunction = ({ subcommand, expectedReport, - timeoutErrorMessage = 'timeout.' + timeoutErrorMessage = 'timeout.', }) => { - return device => new Promise(async (resolve, reject) => { - const timeoutId = setTimeout(() => { - device.removeEventListener('inputreport', checkInputReport); - reject(new Error(timeoutErrorMessage)); - }, 5000); + return (device) => + new Promise(async (resolve, reject) => { + const timeoutId = setTimeout(() => { + device.removeEventListener('inputreport', checkInputReport); + reject(new Error(timeoutErrorMessage)); + }, 5000); - const checkInputReport = event => { - if (event.reportId !== 0x21) { - return; - } - - const data = new Uint8Array(event.data.buffer); - for (const [key, value] of Object.entries(expectedReport)) { - if (data[key - 1] !== value) { + const checkInputReport = (event) => { + if (event.reportId !== 0x21) { return; } - } - device.removeEventListener('inputreport', checkInputReport); - clearTimeout(timeoutId); - setTimeout(resolve, 50); - }; - device.addEventListener('inputreport', checkInputReport); - await device.sendReport(0x01, new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, ...subcommand - ])); - //resolve(); - }); + const data = new Uint8Array(event.data.buffer); + for (const [key, value] of Object.entries(expectedReport)) { + if (data[key - 1] !== value) { + return; + } + } + + device.removeEventListener('inputreport', checkInputReport); + clearTimeout(timeoutId); + setTimeout(resolve, 50); + }; + device.addEventListener('inputreport', checkInputReport); + await device.sendReport( + 0x01, + new Uint8Array([ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ...subcommand, + ]) + ); + // resolve(); + }); }; - const set_input_report_mode_to_0x30 = defineSendReportAsyncFunction({ + // eslint-disable-next-line no-unused-vars + const setInputReportModeTo0x30 = defineSendReportAsyncFunction({ subcommand: [0x03, 0x30], expectedReport: { - 14: 0x03 + 14: 0x03, }, }); - const enabling_MCU_data_22_1 = defineSendReportAsyncFunction({ + const enablingMCUData221 = defineSendReportAsyncFunction({ subcommand: [0x22, 0x01], expectedReport: { 13: 0x80, - 14: 0x22 + 14: 0x22, }, }); - const enabling_MCU_data_21_21_1_1 = defineSendReportAsyncFunction({ - subcommand: [0x21, 0x21, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3 + const enablingMCUData212111 = defineSendReportAsyncFunction({ + subcommand: [ + 0x21, 0x21, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf3, ], expectedReport: { - 14: 0x21 + 14: 0x21, }, }); - const get_ext_data_59 = defineSendReportAsyncFunction({ + const getExtData59 = defineSendReportAsyncFunction({ subcommand: [0x59], expectedReport: { 14: 0x59, - 16: 0x20 + 16: 0x20, }, timeoutErrorMessage: 'ring-con not found.', }); - const get_ext_dev_in_format_config_5C = defineSendReportAsyncFunction({ - subcommand: [0x5C, 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36, - 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36 + const getExtDevInFormatConfig5C = defineSendReportAsyncFunction({ + subcommand: [ + 0x5c, 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x16, 0xed, + 0x34, 0x36, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x0b, 0xe6, 0xa9, 0x22, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xa8, 0xe1, + 0x34, 0x36, ], expectedReport: { - 14: 0x5C + 14: 0x5c, }, }); - const start_external_polling_5A = defineSendReportAsyncFunction({ - subcommand: [0x5A, 0x04, 0x01, 0x01, 0x02], + const startExternalPolling5A = defineSendReportAsyncFunction({ + subcommand: [0x5a, 0x04, 0x01, 0x01, 0x02], expectedReport: { - 14: 0x5A + 14: 0x5a, }, }); - //await set_input_report_mode_to_0x30(device); // same as enableStandardFullMode - await enabling_MCU_data_22_1(device); - await enabling_MCU_data_21_21_1_1(device); - await get_ext_data_59(device); - await get_ext_dev_in_format_config_5C(device); - await start_external_polling_5A(device); + // await set_input_report_mode_to_0x30(device); // same as enableStandardFullMode + await enablingMCUData221(device); + await enablingMCUData212111(device); + await getExtData59(device); + await getExtDevInFormatConfig5C(device); + await startExternalPolling5A(device); }; diff --git a/src/index.js b/src/index.js index 0565c46..c586895 100644 --- a/src/index.js +++ b/src/index.js @@ -14,13 +14,21 @@ const getDeviceID = (device) => { const addDevice = async (device) => { const id = getDeviceID(device); - console.log(`HID connected: ${id} ${device.productId.toString(16)} ${device.productName}`); + console.log( + `HID connected: ${id} ${device.productId.toString(16)} ${ + device.productName + }` + ); connectedJoyCons.set(id, await connectDevice(device)); }; const removeDevice = async (device) => { const id = getDeviceID(device); - console.log(`HID disconnected: ${id} ${device.productId.toString(16)} ${device.productName}`); + console.log( + `HID disconnected: ${id} ${device.productId.toString(16)} ${ + device.productName + }` + ); connectedJoyCons.delete(id); }; @@ -97,7 +105,7 @@ const connectDevice = async (device) => { } } if (!joyCon) { - //console.log(device.productId.toString(16), device.productName); + // console.log(device.productId.toString(16), device.productName); joyCon = new GeneralController(device); // for other controllers } await joyCon.open(); diff --git a/src/joycon.js b/src/joycon.js index 0e19396..9bbb63c 100644 --- a/src/joycon.js +++ b/src/joycon.js @@ -1,5 +1,5 @@ import * as PacketParser from './parse.js'; -import { connectRingCon } from "./connectRingCon.js"; +import { connectRingCon } from './connectRingCon.js'; /** * Concatenates two typed arrays. @@ -261,7 +261,7 @@ class JoyCon extends EventTarget { * @memberof JoyCon * @seeAlso https://github.com/mascii/demo-of-ring-con-with-web-hid */ - async enableRingCon() { + async enableRingCon() { /* const cmds = [ [0x22, 0x01], // enabling_MCU_data_22_1 @@ -291,7 +291,10 @@ class JoyCon extends EventTarget { * @memberof JoyCon */ async enableUSBHIDJoystickReport() { - const usb = this.device.collections[0].outputReports.find(r => r.reportId == 0x80) != null; + const usb = + this.device.collections[0].outputReports.find( + (r) => r.reportId == 0x80 + ) != null; if (usb) { await this.device.sendReport(0x80, new Uint8Array([0x01])); await this.device.sendReport(0x80, new Uint8Array([0x02])); @@ -299,7 +302,7 @@ class JoyCon extends EventTarget { await this.device.sendReport(0x80, new Uint8Array([0x04])); } } - + /** * Send a rumble signal to Joy-Con. * @@ -364,22 +367,26 @@ class JoyCon extends EventTarget { /** * set LED state. + * @param {int} n position(0-3) * * @memberof JoyCon */ - async setLEDState(n) { + async setLEDState(n) { const NO_RUMBLE = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; const subcommand = [0x30, n]; - await this.device.sendReport(0x01, new Uint8Array([...NO_RUMBLE, 0, ...subcommand])); + await this.device.sendReport( + 0x01, + new Uint8Array([...NO_RUMBLE, 0, ...subcommand]) + ); } /** * set LED. * * @memberof JoyCon - * @param n position(0-3) + * @param {int} n position(0-3) */ - async setLED(n) { + async setLED(n) { this.ledstate |= 1 << n; await this.setLEDState(this.ledstate); } @@ -388,9 +395,9 @@ class JoyCon extends EventTarget { * reset LED. * * @memberof JoyCon - * @param n position(0-3) + * @param {int} n position(0-3) */ - async resetLED(n) { + async resetLED(n) { this.ledstate &= ~((1 << n) | (1 << (4 + n))); await this.setLEDState(this.ledstate); } @@ -399,9 +406,9 @@ class JoyCon extends EventTarget { * blink LED. * * @memberof JoyCon - * @param n position(0-3) + * @param {int} n position(0-3) */ - async blinkLED(n) { + async blinkLED(n) { this.ledstate &= ~(1 << n); this.ledstate |= 1 << (4 + n); await this.setLEDState(this.ledstate); @@ -641,7 +648,7 @@ class GeneralController extends JoyCon { * @param {*} packet * @memberof GeneralController */ - _receiveInputEvent(packet) { + _receiveInputEvent(packet) { this.dispatchEvent(new CustomEvent('hidinput', { detail: packet })); } } diff --git a/src/parse.js b/src/parse.js index 2592cc1..c05fbcd 100644 --- a/src/parse.js +++ b/src/parse.js @@ -70,6 +70,7 @@ const scale = Math.PI / 2; * accelerometer data. * * @export + * @param {object} lastValues * @param {*} gyroscope * @param {*} accelerometer * @param {*} productId @@ -77,9 +78,7 @@ const scale = Math.PI / 2; */ export function toEulerAngles(lastValues, gyroscope, accelerometer, productId) { const now = Date.now(); - const dt = lastValues.timestamp - ? (now - lastValues.timestamp) / 1000 - : 0; + const dt = lastValues.timestamp ? (now - lastValues.timestamp) / 1000 : 0; lastValues.timestamp = now; // Treat the acceleration vector as an orientation vector by normalizing it. @@ -90,8 +89,7 @@ export function toEulerAngles(lastValues, gyroscope, accelerometer, productId) { accelerometer.x ** 2 + accelerometer.y ** 2 + accelerometer.z ** 2 ); - lastValues.alpha = - (1 - zeroBias) * (lastValues.alpha + gyroscope.z * dt); + lastValues.alpha = (1 - zeroBias) * (lastValues.alpha + gyroscope.z * dt); if (norm !== 0) { lastValues.beta = bias * (lastValues.beta + gyroscope.x * dt) + @@ -104,14 +102,8 @@ export function toEulerAngles(lastValues, gyroscope, accelerometer, productId) { alpha: // ToDo: I could only get this to work with a magic multiplier (430). productId === 0x2006 - ? ( - (((-1 * (lastValues.alpha * 180)) / Math.PI) * 430) % - 90 - ).toFixed(6) - : ( - (((lastValues.alpha * 180) / Math.PI) * 430) % - 360 - ).toFixed(6), + ? ((((-1 * (lastValues.alpha * 180)) / Math.PI) * 430) % 90).toFixed(6) + : ((((lastValues.alpha * 180) / Math.PI) * 430) % 360).toFixed(6), beta: ((-1 * (lastValues.beta * 180)) / Math.PI).toFixed(6), gamma: productId === 0x2006