WebHID로 레거시 하드웨어 되살리기
금을 찾아서!
수년 동안 나는 SomeDay(TM)에 사용할 수 있는 다른 이상한 하드웨어 장치를 저장해 왔습니다.
사실 그 날은 좀처럼 오지 않기 때문에 몇 달 전에 사용하지 않는 물건을 모두 없애기로 했습니다.
내가 처음에 '외부' 파일에 넣은 항목 중 하나는 3Dconnexion Spaceball 5000이었습니다. 어떤 OS에서도 지원이 거의 또는 전혀 없었기 때문입니다.
그러나 무슨 일이 일어날지 알아보기 위해 우분투 컴퓨터에 연결하려고 했습니다.
실행
lsusb
은 다음을 제공합니다.ID 046d:c621 Logitech, Inc. 3Dconnexion Spaceball 5000
...
-v
스위치를 추가하면 노출된 HID 인터페이스가 하나 있음을 알 수 있습니다....
bInterfaceClass 3 Human Interface Device
...
참고: (Ubuntu) Linux를 사용하여 사용자 공간에서 액세스하려면
lsusb
에서 udev 규칙 파일(예: /etc/udev/rules/50-webhid.rules
)로 반환된 USB 공급업체 ID를 사용하여 다음을 추가하십시오.SUBSYSTEM=="hidraw", ATTRS{idVendor}=="046d", MODE:="0666", GROUP="input"
그리고 달린다
sudo udevadm control --reload-rules && sudo udevadm trigger
다른 시스템에서는 필요하지 않습니다.
웹HID
the Fugu API tracker 을 보면 크롬 M89에서 WebHID이 출시된 것을 볼 수 있으므로 연결을 시도하고 스페이스볼이 어떤 종류의 데이터를 보내는지 봅시다.
장치에서 오는 모든 데이터를 읽는 아주 간단한 것부터 시작하겠습니다.
<button id="scan">SCAN</button>
<script>
function scan() {
navigator.hid.requestDevice({filters: [{ vendorId: 0x046d }]}).then(devices => {
if (!devices.length) return;
const device = devices[0];
device.open().then(() => {
console.log('Opened device: ' + device.productName);
device.addEventListener('inputreport', e => {
console.log('Report ID', e.reportId);
console.log('Data', new Int8Array(e.data.buffer));
});
});
});
}
document.querySelector('#scan').addEventListener('click', scan);
</script>
Chrome에서 이것을 실행하고 장치가 연결된 상태에서
SCAN
버튼을 클릭하면 이제 Spaceball 5000을 선택할 수 있는 대화 상자가 표시되어야 합니다.성공적으로 연결되면 콘솔에 다음이 표시되어야 합니다.
Opened device: 3Dconnexion SpaceBall 5000 USB
예!
모든 데이터!
Spaceball을 조작하면 보고서 ID 1 및 2와 쌍을 이루는 것처럼 보이는 6바이트의 많은 데이터 패킷을 콘솔로 보냅니다.
Report ID 1
Data Int8Array(6) [101, -1, -2, -1, 95, 0]
Report ID 2
Data Int8Array(6) [-27, 0, 35, 1, -126, -1]
프로토콜 설명을 조금 검색한 후 2006년의 작은 값post을 읽었습니다. 값은 16비트 부호 있는 X, Y, Z 변환 값이고 회전에 대해서도 동일합니다.
코드를 약간 변경해 보겠습니다.
...
device.addEventListener('inputreport', e => {
// First attempt: Print all the data
// console.log('Report ID', e.reportId);
// console.log('Data', new Int8Array(e.data.buffer));
// Second attempt: extract translation and rotation:
if (e.reportId === 1) console.log('T', new Int16Array(e.data.buffer));
if (e.reportId === 2) console.log('R', new Int16Array(e.data.buffer));
});
...
이제 데이터가 좀 더 이해되기 시작합니다.
T Int16Array(3) [33, -18, 132]
R Int16Array(3) [-230, 67, 0]
T Int16Array(3) [39, -15, 124]
R Int16Array(3) [-208, 67, 0]
Spaceball을 놓으면 0이 되는 것 같습니다.
T Int16Array(3) [0, 0, 0]
R Int16Array(3) [0, 0, 0]
간단한 드라이버 만들기
이제 모든 기본 사항이 준비되었으므로 장치용 작은 웹 드라이버를 작성해 보겠습니다.
확장EventTarget은 드라이버가 구문 분석된 패킷을 적절한 이벤트로 수신기에 보낼 수 있는 기능을 제공합니다.
export const SpaceDriver = new class extends EventTarget {
#device // Just allow one device, for now
constructor() {
super();
this.handleInputReport = this.handleInputReport.bind(this);
// See if a paired device is already connected
navigator.hid.getDevices().then((devices) => {
devices.filter(d => d.vendorId === deviceFilter.vendorId).forEach(this.openDevice.bind(this));
});
navigator.hid.addEventListener('disconnect', evt => {
const device = evt.device;
console.log('disconnected', device);
if (device === this.#device) {
this.disconnect();
}
});
}
openDevice(device) {
this.disconnect(); // If another device is connected - close it
device.open().then(() => {
console.log('Opened device: ' + device.productName);
device.addEventListener('inputreport', this.handleInputReport);
this.#device = device;
this.dispatchEvent(new CustomEvent('connect', {detail: { device }}));
});
}
disconnect() {
this.#device?.close();
this.#device = undefined;
this.dispatchEvent(new Event('disconnect'));
}
scan() {
navigator.hid.requestDevice(requestParams).then(devices => {
if (devices.length == 0) return;
this.openDevice(devices[0]);
});
}
handleInputReport(e) {
switch(e.reportId) {
case 1: // x, y, z
this.handleTranslation(new Int16Array(e.data.buffer));
break;
case 2: // yaw, pitch, roll
this.handleRotation(new Int16Array(e.data.buffer));
break;
}
}
handleTranslation(val) {
this.dispatchEvent(new CustomEvent('translate', {
detail: {
x: val[0],
y: val[1],
z: val[2]
}
}));
}
handleRotation(val) {
this.dispatchEvent(new CustomEvent('rotate', {
detail: {
rx: -val[0],
ry: -val[1],
rz: val[2]
}
}));
}
}
웹 구성 요소 및 CSS3D로 시각화
콘솔에 숫자를 쓰는 것은 재미가 없으며 기본 CSS3D 및 웹 구성 요소를 지원하는 최신 브라우저를 사용하고 있으므로 구성 요소에서 어리석은 3D 개체를 만들어 Spaceball로 조작할 수 있습니다.
export class Demo3DObj extends HTMLElement {
#objtranslate
#objrotate
#obj
constructor() {
super();
this.#objtranslate = '';
this.#objrotate = '';
}
connectedCallback() {
this.innerHTML = `
<style>
.scene {
width: 200px;
height: 200px;
margin: 200px;
perspective: 500px;
}
.obj {
width: 200px;
height: 200px;
position: relative;
transform-style: preserve-3d;
transform: translateZ(-1000px);
transition: transform 100ms;
}
.plane {
position: absolute;
width: 200px;
height: 200px;
border: 5px solid black;
border-radius: 50%;
}
.red {
background: rgba(255,0,0,0.5);
transform: rotateY(-90deg)
}
.green {
background: rgba(0,255,0,0.5);
transform: rotateX( 90deg)
}
.blue {
background: rgba(0,0,255,0.5);
}
</style>
<div class="scene">
<div class="obj">
<div class="plane red"></div>
<div class="plane green"></div>
<div class="plane blue"></div>
</div>
</div>
`;
this.#obj = this.querySelector('.obj');
}
setTranslation(x, y, z) {
this.#objtranslate = `translateX(${x}px) translateY(${y}px) translateZ(${z}px) `;
this.#obj.style.transform = this.#objtranslate + this.#objrotate;
}
setRotation(rx, ry, rz) {
this.#objrotate = `rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg) `;
this.#obj.style.transform = this.#objtranslate + this.#objrotate;
}
}
customElements.define('demo-3dobj', Demo3DObj);
남은 것은
SpaceDriver
와 demo-3dobj
를 결합하여 Spaceball 5000에 연결된 WebHID를 사용하여 3D 개체를 직접 조작하는 것을 보는 것입니다.GitHub의 라이브 데모 및 소스 코드
Try it out here!
source
즐겨!
Reference
이 문제에 관하여(WebHID로 레거시 하드웨어 되살리기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/denladeside/reviving-legacy-hardware-with-web-hid-3447텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)