{"id":8179,"date":"2022-04-15T17:00:00","date_gmt":"2022-04-15T15:00:00","guid":{"rendered":"https:\/\/vielzutun.ch\/wordpress\/?p=8179"},"modified":"2022-05-16T14:10:22","modified_gmt":"2022-05-16T12:10:22","slug":"spacemouse-eine-odyssee-mit-happy-end","status":"publish","type":"post","link":"https:\/\/vielzutun.ch\/wordpress\/?p=8179","title":{"rendered":"SpaceMouse auslesen unter JavaScript"},"content":{"rendered":"\n<p>Also available in: <a href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8416\" target=\"_blank\" rel=\"noreferrer noopener\">English<\/a>.<\/p>\n\n\n\n<p>In diesem Beitrag beschreibe ich die Entwicklung (inkl. Ver\u00f6ffentlichung des vollst\u00e4ndigenen Quellcodes, 1 Datei mit ca. 70 <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Lines_of_Code\" target=\"_blank\">SLOC<\/a>) einer low-level Schnittstelle f\u00fcr den Zugriff auf eine SpaceMouse bzw. SpaceNavigator unter JavaScript.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1004\" height=\"1024\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-1004x1024.jpg\" alt=\"\" class=\"wp-image-8185\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-1004x1024.jpg 1004w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-294x300.jpg 294w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-768x783.jpg 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-1507x1536.jpg 1507w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator-1200x1223.jpg 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceNavigator.jpg 2000w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption><em>SpaceMouse \/ SpaceNavigator &#8211; ein <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Sechs_Freiheitsgrade\" target=\"_blank\">6DoF<\/a> Eingabeger\u00e4t<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Inhaltsverzeichnis:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"#Einleitung\">Einleitung<\/a><\/li><li><a href=\"#PleiteAnbieter\">Pleite beim Anbieter<\/a><\/li><li><a href=\"#Wireshark\">Auftritt Wireshark<\/a><\/li><li><a href=\"#WebUSB\">Auftritt WebUSB<\/a><\/li><li><a href=\"#Entwicklungsumgebung\">Entwicklungsumgebung<\/a><\/li><li><a href=\"#WebHID\">Auftritt WebHID<\/a><\/li><li><a href=\"#Koordinatensystem\">Koordinatensystem<\/a><\/li><li><a href=\"#Hardware\">Hardware<\/a><\/li><li><a href=\"#Quellcode\">Quellcode<\/a><\/li><li><a href=\"#Einschr\u00e4nkungen\">Bekannte Einschr\u00e4nkungen<\/a><\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"has-light-blue-background-color has-background\">Schau Dir auch meinen <a href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8554\" target=\"_blank\" rel=\"noreferrer noopener\">Folgebeitrag<\/a> (englisch) an, in dem ich einen 6DoF Treiber vom Typ &#171;Kamerasteuerung&#187; vorstelle, einschliesslich vollst\u00e4ndigem Quellcode auf der Basis von Three.js v.140.<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Einleitung\"><strong>Einleitung<\/strong><\/p>\n\n\n\n<p>Wer diesen Blog verfolgt, dem wird nicht entgangen sein das ich mich gerne und intensiv mit Computergrafik befasse. Prominente Beispiele sind meine <a rel=\"noreferrer noopener\" href=\"https:\/\/vielzutun.ch\/wordpress\/?p=5764\" target=\"_blank\">Interaktive 3D Desmo-Animation<\/a> und mein <a rel=\"noreferrer noopener\" href=\"https:\/\/vielzutun.ch\/wordpress\/?p=7439\" target=\"_blank\">Interaktiver 3D Gespann-Simulator<\/a>. <\/p>\n\n\n\n<p>Sowohl Monster als auch Ural entsprechen inzwischen weitgehend meinen Vorstellungen und werden weiterhin aktiv genutzt. Es stehen keine dringenden Versch\u00f6nerungs- bzw. Verbesserungsprojekte an. Zudem bin ich durch unseren Umzug ins Appenzell und den damit einhergehenden Verlust meiner Drehbank weitgehend meiner Fertigungskapazit\u00e4ten beraubt.<\/p>\n\n\n\n<p>Das gibt mir Zeit, mich wieder vertieft einer alten Leidenschaft, der Erstellung Interaktiver 3D Visualisierungen zu widmen, auch abseits von direktem Bike-Zusammenhang. Hilfsmittel dabei ist <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\" target=\"_blank\">Three.js<\/a>, eine JavaScript Bibliothek die das Erstellen und Plattform-unabh\u00e4ngige Web-Publishing von interaktiven 3D Inhalten im Browser erm\u00f6glicht. Inzwischen bin ich vom reinen Anwender von Three.js zum &#171;Contributor&#187; aufgestiegen: Verbesserungsvorschl\u00e4ge, welche ich dem Entwickler-Team in Form von &#171;Pull Requests&#187; unterbreitet hatte, wurden akzeptiert und sind inzwischen Bestandteil aller zuk\u00fcnftigen Three.js Versionen. Aber das ist eine andere Geschichte &#8230;<\/p>\n\n\n\n<p>Jedenfalls lag es nahe, mich an meine leicht angestaubte, ca. 15 Jahre alte <a rel=\"noreferrer noopener\" href=\"https:\/\/3dconnexion.com\/de\/product\/spacemouse-compact\/\" target=\"_blank\">SpaceMouse<\/a> (s.o.) zu erinnern, seinerzeit noch als &#171;SpaceNavigator&#187; vermarktet, welche in der hintersten Ecke einer meiner Schreibtisch-Schubladen schlummerte. Ich beschloss, sie zu reaktivieren und als Steuerger\u00e4t meiner existierenden und zuk\u00fcnftigen 3D-Animationen zu nutzen. Damit begann eine ungew\u00f6hnliche Unternehmung.<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"PleiteAnbieter\"><strong>1. Etappe &#8211; die Pleite beim Anbieter<\/strong><\/p>\n\n\n\n<p>Da SpaceNavigator bzw. SpaceMouse vergleichsweise ungew\u00f6hnliche Computer-Eingabeger\u00e4te darstellen die von Betriebssystemen nicht standardm\u00e4\u00dfig unterst\u00fctzt werden, ben\u00f6tigt man spezielle Treiber, um die Daten dieser Ger\u00e4te einer Applikation zug\u00e4nglich zu machen. Der erste Schritt auf der Suche nach einem aktuellen Treiber f\u00fchrte mich zum Anbieter der inzwischen wieder SpaceMouse umbenannten Eingabeger\u00e4te, der Firma 3Dconnexion. Diese Firma unterh\u00e4lt ein eigenes Software Developer Programm und auch ein eigenes Forum. Nachdem ich mich f\u00fcr beide registriert hatte und erst einmal alle SDKs f\u00fcr die Plattformen Windows, MacOS und Linux heruntergeladen und gesichtet hatte, fand ich schlie\u00dflich ein &#171;Web&#187; Unterverzeichnis im Windows SDK. <\/p>\n\n\n\n<p>Ich fasse mich an dieser Stelle m\u00f6glichst kurz, um nicht allzu unfreundlich zu werden:<\/p>\n\n\n\n<p>Was 3Dconnexion hier als Software-Unterst\u00fctzung, gar SDK f\u00fcr die Nutzung in einer Web-Umgebung unter JavaScript anbietet, spottet jeder Beschreibung.<\/p>\n\n\n\n<p>Ein irrsinnig komplexer Protokoll-Stack, mit WebSockets, eigenem Proxy-Server (mit eigenem Root Zertifikat und Schl\u00fcssel), Cryptographie-Bibliothek, einer eigenen (closed source) &#171;Navlib&#187;, die mit einer  gegen\u00fcber einem potenziellen Anwendungsprogrammierer immerhin dokumentierten Anwendungsschicht kommuniziert. Die je Rechner zu installierende aktuelle 3DxWare belegt rund 63 MB Speicherplatz und startet geschlagene f\u00fcnf Hintergrundprozesse:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"271\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-1024x271.png\" alt=\"\" class=\"wp-image-8199\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-1024x271.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-300x80.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-768x204.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-1536x407.png 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes-1200x318.png 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/3DxWare_Processes.png 1592w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption>Durch 3DxWare automatisch gestartete Hintergrundprozesse<\/figcaption><\/figure>\n\n\n\n<p>Beigef\u00fcgt war ein offensichtlich seit Jahren ungewartetes Beispielprogramm auf der Basis von Three.js in der Version <strong>71<\/strong> (aktuell steht Three.js bei Version <strong>139<\/strong>), basierend auf dem o.a. Protokoll-Stack und der dar\u00fcber liegenden Abstraktionsschicht.  Wobei diese Schicht sich anma\u00dft, die Hoheit \u00fcber die Three.js cameraMatrixWorld zu beanspruchen: der potenzielle Nutzer muss(!) Angaben zur camera.position, zum camera.lookAt(), zum camera.fov, zur bounding box der Geometrien in der Three.js scene und weitere Angaben machen, aus denen der sogenannte &#171;Treiber&#187; dann eine seiner Meinung nach passende cameraMatrixWorld f\u00fcr Three.js berechnet. Dazu noch Angaben zu einer &#171;construction plane&#187;, einer Standard Ansichtsrichtung etc. Das sind alles Angaben, die einen <strong><em>Treiber<\/em><\/strong> mit Verlaub gesagt einen feuchten Schei\u00dfdreck angehen. Sowas geht <strong>garnicht<\/strong>, und kein ernsthafter Three.js-Entwickler wird sich ohne Not in ein derartiges Korsett zw\u00e4ngen lassen.<\/p>\n\n\n\n<p>Meine Frage an einen der Software-Verwalter im Software Support Forum, ob es keine M\u00f6glichkeit gebe, die rohen (raw) Sensor Daten der SpaceMouse unbearbeitet durch den Protokollstack durchzuschleifen und Software-Entwicklern zur Verf\u00fcgung zu stellen, wurde nach intensiver und hitziger werdender Diskussion leider immer wieder abschl\u00e4gig beschieden.<\/p>\n\n\n\n<p>Selbst mein Hinweis darauf, da\u00df Three.js inzwischen \u00fcber 100&#8217;000 Webseiten unterst\u00fctzt, mit absehbar einem Vielfachen an Besuchern, und da\u00df dies ein gewaltiges Reservoir potenzieller Neukunden f\u00fcr 3DConnexion darstelle, welches aber ohne angemessenen Support f\u00fcr Software-Entwickler nicht angezapft werden w\u00fcrde, \u00e4nderte nichts am Ergebnis. <\/p>\n\n\n\n<p>Nun geht es mir grunds\u00e4tzlich am Ges\u00e4\u00df vorbei, ob 3Dconnexion sein Marktpotenzial aussch\u00f6pft oder nicht. Was mir aber nicht egal ist, ist, da\u00df ein phantastisch intuitiv nutzbares Eingabeger\u00e4t nun in einer Web-Umgebung nicht mehr nach meinem Belieben nutzbar sein soll. Z.B. so, wie ich es in meinen fr\u00fcheren Linux-basierten Anwendungen vor 15 Jahren bereits kennen und sch\u00e4tzen gelernt hatte.<\/p>\n\n\n\n<p>Die Erfinder des Begriffs <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Bloatware\" target=\"_blank\">Bloatware<\/a> scheinen jedenfalls genau solche Softwarepakete im Sinn gehabt zu haben, die einen sinnlos aufgebl\u00e4hten Funktionsumfang und Ressourcenverbrauch aufweisen, und dabei Kernfunktionalit\u00e4t garnicht mehr bereitstellen, bzw. ben\u00f6tigte Informationen maximal verschleiern. Hierf\u00fcr den Begriff &#171;Overengineering&#187; zu verwenden w\u00fcrde nach meinem Empfinden die Grenze zur Beleidigung echter Ingenieure \u00fcberschreiten.<\/p>\n\n\n\n<p>Der oben verlinkte Artikel \u00fcber Bloatware h\u00e4lt sehr treffend fest:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-block-quote-is-layout-flow\"><p>Bloatware&nbsp;entsteht in der Regel aus Marketinggr\u00fcnden oder \u2013 <strong>auch angeblichen<\/strong> \u2013 Anwenderw\u00fcnschen.<\/p><cite><a href=\"https:\/\/de.wikipedia.org\/wiki\/Bloatware#Gr\u00fcnde_f\u00fcr_das_Entstehen_von_Bloatware\" target=\"_blank\" rel=\"noreferrer noopener\">Wikipedia<\/a><\/cite><\/blockquote>\n\n\n\n<p>Exakt diese Begr\u00fcndung hatte mir der Verwalter im Software Developer Forum auch gegeben: das SDK sei deshalb so komplex, weil anderenfalls, bei Bereitstellung der rohen (raw) Sensordaten, ein Entwickler &#171;the entirety of navigation features <strong>customers have come to expect<\/strong>&#187; selbst entwickeln m\u00fcsse.<\/p>\n\n\n\n<p>Es wurde mir schlie\u00dflich klar, da\u00df von 3Dconnexion keine Hilfe zu erwarten sein w\u00fcrde. Ich entschied mich, die Sache in die eigenen H\u00e4nde zu nehmen und mir nach M\u00f6glichkeit selbst zu helfen. <\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Wireshark\"><strong>2. Etappe &#8211; Auftritt Wireshark<\/strong><\/p>\n\n\n\n<p>Ich wollte die Sache von Grund auf angehen, bevor irgend ein Treiber evtl. Daten verf\u00e4lschen oder verstecken w\u00fcrde. Mein SpaceNavigator ist ein kabelgebundenes USB-Device und ich wusste, da\u00df dieses Ger\u00e4t die von mir gew\u00fcnschten Daten tats\u00e4chlich bereitstellt. Die Daten, die ich auslesen und verarbeiten m\u00f6chte, werden zun\u00e4chst in den <strong>U<\/strong>niversal <strong>S<\/strong>erial <strong>B<\/strong>us eingespeist. Das Werkzeug der Wahl f\u00fcr das Belauschen von Datenprotokollen durch einen engagierten Hacker heisst <a rel=\"noreferrer noopener\" href=\"https:\/\/www.wireshark.org\/index.html#aboutWS\" target=\"_blank\">Wireshark<\/a>. Wireshark ist ein OpenSource Programm welches es auch in einer Version f\u00fcr macOS gibt. Das installierte ich mir. Urspr\u00fcnglich f\u00fcr die Protokollanalyse von Netzwerk-Datenverkehr gedacht, kann Wireshark inzwischen auch am USB lauschen. Ben\u00f6tigt wird unter macOS das &#171;interface&#187; XHC20.<\/p>\n\n\n\n<p>Meine Entwicklungsmaschinen sind ein iMac aus 2014 und ein MacBook Air aus 2017, die beide unter macOS Catalina (10.15.7) laufen. <strong><em>Aus Sicherheitsgr\u00fcnden<\/em><\/strong> (das sollte ich im weiteren Verlauf meines Abenteuers noch \u00f6fter zu lesen kriegen&#8230;) ist das XHC20 Interface in macOS Catalina standardm\u00e4\u00dfig deaktiviert. Es gibt aber einen Trick, wie man es wieder aktivieren kann. Ich folgte <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.apple.com\/forums\/thread\/124875\" target=\"_blank\">dieser Anleitung<\/a> im Apple developer Forum. Der Warnung folgend, nahm ich f\u00fcr dieses Experiment mein MacBook Air, welches die Prozedur und deren R\u00fcckg\u00e4ngigmachung bisher anscheinend schadlos \u00fcberstanden hat.<\/p>\n\n\n\n<p>Es war dies meine erste Wireshark-Session, und die von Wireshark gelieferte Informationsf\u00fclle und -Aufbereitung hat mich echt beeindruckt! \ud83d\ude0e :<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"871\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-1024x871.png\" alt=\"\" class=\"wp-image-8208\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-1024x871.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-300x255.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-768x653.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-1536x1306.png 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark-1200x1020.png 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Wireshark.png 1642w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption><em>Ausschnitt einer Wireshark Session. Hier: Device plugged into USB<\/em><\/figcaption><\/figure>\n\n\n\n<p>Der vertikal dreigeteilte Bildschirm bietet im oberen Panel eine Liste mit Eckdaten der mitgeschnittenen Telegramme. Neuere Telegramme werden unten angeh\u00e4ngt. Vielleicht kann man diese Sortierung auch konfigurieren..<\/p>\n\n\n\n<p>Im mittleren Panel werden Details desjenigen Telegramms angezeigt, welches im oberen Panel markiert ist. Soweit m\u00f6glich, werden den Daten &#171;sprechende&#187; Label vorangestellt, vermutlich spezifizierte Namen von Datenfeldern.<\/p>\n\n\n\n<p>Im untersten Panel wird der Hex-Dump des oben markierten Telegramms angezeigt. Das ist dann wirklich &#171;roh&#187;, bzw. &#171;raw&#187;. Also genau das, was ich wollte.<\/p>\n\n\n\n<p>Im obigen Screenshot zeige ich einen Ausschnitt aus dem durch Einstecken des USB-Steckers in meinen USB-Hub ausgel\u00f6sten Datenverkehr. Ich nenne das das &#171;<strong>Plug-Event<\/strong>&#171;. Die markierte Zeile im oberen Panel ist die Antwort (Response) meines SpaceNavigators auf die unmittelbar vorhergehende Anfrage (Request) des USB an das neue Device, unmittelbar nachdem ich den Stecker des USB-Kabels meines SpaceNavigators in der Buchse meines USB-Hubs versenkt hatte: Wer bist Du denn? und: Was kannst Du denn?<\/p>\n\n\n\n<p>In seiner Response stellt sich mein SpaceNavigator artig vor: u.a., da\u00df er vom Anbieter mit der idVendor <strong>0x046d<\/strong> (Logitech, Inc., gr\u00fcn markiert) stammt, und eine idProduct von <strong>0xc626<\/strong> (3Dconnexion SpaceNavigator 3D Mouse, blau markiert) hat.  Im Hex-Dump sieht man auch, da\u00df wir es mit einem &#171;<a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Byte-Reihenfolge\" target=\"_blank\">Little Endian<\/a>&#187; Device zu tun haben, also das least significant Byte <strong><em>vor<\/em><\/strong> dem most significant Byte aufgef\u00fchrt wird.<\/p>\n\n\n\n<p>Da ich den Zustand &#171;System Integrity Protection aufgehoben&#187; f\u00fcr mein MacBook Air nicht unn\u00f6tig lange ausdehnen oder wiederholen wollte, habe ich mir einen Satz m\u00f6glicher Situationen, ein sogenanntes &#171;Szenario&#187; ausgedacht, welches ein typisches Nutzerprofil eines Nutzers einer SpaceMouse bzw. eines SpaceNavigators abdeckt. Mein SpaceNavigator ist ein sehr schlichtes Ger\u00e4t, welches den Fokus noch auf die Bereitstellung von 6DoF Daten richtet. Zus\u00e4tzlich weist er zwei seitlich angeordnete Tasten auf, sowie einen den Puck umschliessenden blauen Leuchtring. Neuere Ger\u00e4te am oberen Ende des 3Dconnexion Produktspektrums werden fast schon schwerpunktm\u00e4\u00dfig als programmierbare Tastaturen vermarktet, mit 6DoF quasi nur noch als Kollateralnutzen. Nicht mein Ding.<\/p>\n\n\n\n<p>Das sind die Anwendungsf\u00e4lle, die mir f\u00fcr meine puristische SpaceMouse eingefallen sind:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Device plug event (device Stecker eingesteckt)<\/li><li>Device unplug event (device Stecker abgezogen)<\/li><li>Taste(n) gedr\u00fcckt\/losgelassen<\/li><li>LED ein\/ausschalten<\/li><li>Translation event<\/li><li>Rotation event<\/li><\/ul>\n\n\n\n<p>F\u00fcr jeden dieser Anwendungsf\u00e4lle habe ich einen (m\u00f6glichst reinen) Protokollmittschnitt angefertigt und als separate Datei abgespeichert. Gleichzeitig stattfindender Telegrammverkehr anderer devices, die an den gleichen USB angeschlossen sind, kann man in Wireshark ausfiltern. Die in separaten Dateien gespeicherten Telegrammmitschnitte  erm\u00f6glichen die nachtr\u00e4gliche Offline-Analyse des Telegrammverkehrs, bei wieder aktivierter System Integrity Protection.<\/p>\n\n\n\n<p>Der oben beschriebene Screenshot eines Telegramms aus einer Serie von Telegrammen, die durch das Verbinden des Ger\u00e4ts mit dem Bus ausgel\u00f6st wurden, ist also nur ein Beispiel von mehreren. Auch Tastendr\u00fccke bzw. -Loslassen l\u00f6sen \u00e4hnlichen, wenn auch weniger ausgedehnten Informationsaustausch \u00fcber den USB aus. Genauso wie Auslenkungen des &#171;Pucks&#187; der SpaceMouse aus seiner statischen Mittellage. Hier konnte ich bereits erstmals die Bytes \u00fcber den Bildschirm flitzen sehen, an denen ich letztendlich interessiert bin. Was meine Motivation, mich hier durchzubeissen nochmals deutlich befl\u00fcgelte \ud83d\ude0e <\/p>\n\n\n\n<p>Damit komme ich von der Analyse zur Synthese: wie kann man das gewonnene Wissen \u00fcber den Datenverkehr sinnvoll nutzen?<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"WebUSB\"><strong>3. Etappe &#8211; Auftritt WebUSB<\/strong><\/p>\n\n\n\n<p>Meine weiteren Recherchen f\u00fchrten mich alsbald zur <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/WebUSB\" target=\"_blank\">WebUSB<\/a> API. Einer Schnittstelle zur Verf\u00fcgbarmachung von USB-Ger\u00e4ten in einem Web-Context, also mittels JavaScript. Das klang vielversprechend!<\/p>\n\n\n\n<p>Ich nahm den <a rel=\"noreferrer noopener\" href=\"https:\/\/web.dev\/usb\/\" target=\"_blank\">Beispiel-Code<\/a> eines der Entwickler von WebUSB als Startpunkt meiner Gehversuche. Die ersten Zeilen dieser Referenz-Implementierung funktionierten auch prima, aber dann muss man recht bald ein ausgew\u00e4hltes und ge\u00f6ffnetes device auch &#171;claimen&#187;, d.h.: den exklusiven Zugriff darauf programm-technisch beanspruchen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>device.claimInterface(2)) \/\/ Request exclusive control over interface #2.<\/code><\/pre>\n\n\n\n<p>Dieser Aufruf l\u00f6ste bei mir immer eine Fehlermeldung in der JavaScript Console meines Browsers aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DOMException: The requested interface implements a protected class<\/code><\/pre>\n\n\n\n<p>Da habe ich lange ger\u00e4tselt, was das denn f\u00fcr eine Ursache haben k\u00f6nnte. Bis ich auf die Idee kann, die komplette Fehlermeldung (in doppelte Hochkommata eingefasst) in eine Suchmaschine einzugeben.<\/p>\n\n\n\n<p>Die Antwort fand sich, wie so h\u00e4ufig, auf <a rel=\"noreferrer noopener\" href=\"https:\/\/stackoverflow.com\/questions\/54910706\/webusb-the-requested-interface-implements-a-protected-class\" target=\"_blank\">stackoverflow.com<\/a> : es <a rel=\"noreferrer noopener\" href=\"https:\/\/groups.google.com\/a\/chromium.org\/g\/blink-dev\/c\/LZXocaeCwDw\/m\/GLfAffGLAAAJ\" target=\"_blank\">stellte sich heraus<\/a>, da\u00df die Entwickler von WebUSB absichtlich &#171;<em>aus Sicherheitsgr\u00fcnden<\/em>&#187; das &#171;claimen&#187; von Ger\u00e4ten bestimmter Ger\u00e4teklassen unterbunden hatten. Darunter eben leider auch die Ger\u00e4teklasse <a href=\"https:\/\/de.wikipedia.org\/wiki\/Human_Interface_Device\" target=\"_blank\" rel=\"noreferrer noopener\">HID<\/a>, human interface device, zu der auch die SpaceMouse geh\u00f6rt.<\/p>\n\n\n\n<p>So erwies sich dieser hoffnungsvolle Ansatz leider als Sackgasse. Immerhin verwies der Entwickler, der die zur\u00fcckgezogene Unterst\u00fctzung f\u00fcr die Ger\u00e4teklasse HID propagiert hatte, auf die dedizierte API <strong>WebHID<\/strong>, die im Prinzip die gleiche Funktionalit\u00e4t bieten w\u00fcrde, nur eben beschr\u00e4nkt auf Ger\u00e4te der Klasse HID.<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Entwicklungsumgebung\"><strong>Zwischenspiel: die Entwicklungsumgebung<\/strong><\/p>\n\n\n\n<p>Ein etwas genauerer Blick in die <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HIDInputReportEvent\" target=\"_blank\" rel=\"noreferrer noopener\">Spezifikation des WebHID<\/a> API zeigte mir, da\u00df zentrale Funktionen nur in einem &#171;<a rel=\"noreferrer noopener\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HIDInputReportEvent\" target=\"_blank\">secure context<\/a>&#187; verf\u00fcgbar sein w\u00fcrden, also nur unter dem <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Hypertext_Transfer_Protocol_Secure\" target=\"_blank\">https<\/a> Protokoll.<\/p>\n\n\n\n<p>Nun muss ich ein wenig ausholen, um die Auswirkungen dieser Anforderung verst\u00e4ndlich zu machen:<\/p>\n\n\n\n<p>Jedes Web-Publishing Projekt umfasst die Entwicklung, d.h. das Schreiben, Testen, Korrigieren von Textdateien. Im einfachsten Fall sind das reine HTML-Dateien. Diese k\u00f6nnen weitere Textdateien &#171;referenzieren&#187;, z.B. JavaScript Dateien mittels <code><a rel=\"noreferrer noopener\" href=\"https:\/\/www.w3schools.com\/tags\/tag_script.asp\" target=\"_blank\">&lt;script&gt; &lt;\/script&gt;<\/a><\/code> Tags einbinden.<\/p>\n\n\n\n<p>Nach abgeschlossener Entwicklung l\u00e4dt der Entwickler diese Dateien auf den i.d.R. angemieteten Webspace bei seinem Provider hoch, von wo aus sie \u00fcber den bei Provider laufenden Webserver auf den Browser eines Webseitenbesuchers ausgespielt und dort angezeigt werden. H\u00e4ufig unter Verwendung des <code>http:\/\/<\/code>  Schemas. Dieser Fall s\u00e4he in der URL-Eingabezeile des Browsers eines Webseiten Besuchers im Prinzip so aus (dies ist nur ein <strong><em>nicht(!) funktionierendes<\/em><\/strong> Beispiel):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"88\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-16.47.12.png\" alt=\"\" class=\"wp-image-8276\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-16.47.12.png 584w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-16.47.12-300x45.png 300w\" sizes=\"auto, (max-width: 584px) 85vw, 584px\" \/><figcaption><em>URL-Eingabe bei Zugriff \u00fcber das Internet<\/em><\/figcaption><\/figure>\n\n\n\n<p>Nun ist der Software-Entwicklungsprozess in aller Regel ein hochgradig iterativer: Software-\u00c4nderungen (z.B. \u00c4nderungen an .html oder .js Dateien) werden vorgenommen, dann getestet, dann die Abweichungen zwischen dem gew\u00fcnschten und dem bisher realisierten Ergebnis bewertet, was in neuen \u00c4nderungen an den Quelldateien resultiert. Womit die n\u00e4chste Iteration im Entwicklungszyklus eingel\u00e4utet ist.<\/p>\n\n\n\n<p>Da das wiederholte Hochladen von halbfertigen Entwicklungsergebnissen auf den \u00f6ffentlich einsehbaren Webspace den interaktiven Entwicklungsfluss hemmt und man sich andererseits nicht beim Herumst\u00fcmpern vor aller Welt entbl\u00f6\u00dfen m\u00f6chte, bzw. durch zuf\u00e4llige Webseitenbesucher st\u00f6ren lassen m\u00f6chte, greift man als Entwickler w\u00e4hrend der Softwareentwicklung f\u00fcrs Web gerne auf die Vereinfachung zur\u00fcck, eine Datei vom lokalen Rechner direkt im eigenen Browser zu testen. In der Browser-URL-Eingabezeile sieht das z.B. so aus:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1016\" height=\"66\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.12.59.png\" alt=\"\" class=\"wp-image-8281\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.12.59.png 1016w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.12.59-300x19.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.12.59-768x50.png 768w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>URL-Eingabe bei Zugriff auf eine Quelldatei auf dem eigenen, lokalen Rechner<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Aus Bequemlichkeit war das bisher meine einzige Entwicklungsmethode gewesen.<\/p>\n\n\n\n<p>So verlockend einfach und direkt dieser Zugriff auch ist, so bietet er doch auch Nachteile, z.B. bei nur geringf\u00fcgig komplexeren Projekten, in denen z.B. eine auf dem lokalen Rechner vorliegende Haupt-Datei auf Ressourcen im Internet zugreifen m\u00f6chte. Durch die Mischung von  <code>file:\/\/\/<\/code> und <code>http:\/\/<\/code>  Schemata handelt man sich zuverl\u00e4ssig l\u00e4stige <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Cross-Origin_Resource_Sharing\" target=\"_blank\">CORS<\/a>-Probleme ein &#8211; ein weiterer Sicherheitsmechanismus heutiger Browser.<\/p>\n\n\n\n<p>Auch diese Probleme lassen sich umgehen, wenn man einen eigenen Webserver auf dem lokalen Rechner installiert und dann \u00fcber das <code>http:\/\/<\/code> Schema auf lokale Dateien zugreifen kann. Ich entschied mich, zu diesen Zweck den leichtgewichtigen Webserver lighttpd zu installieren, wie auch vom <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\/docs\/#manual\/en\/introduction\/How-to-run-things-locally\" target=\"_blank\">Three.js Projekt<\/a> empfohlen. <a rel=\"noreferrer noopener\" href=\"https:\/\/redmine.lighttpd.net\/projects\/1\/wiki\/HowToInstallOnOSX\" target=\"_blank\">Installation<\/a> und <a rel=\"noreferrer noopener\" href=\"https:\/\/redmine.lighttpd.net\/projects\/lighttpd\/wiki\/TutorialConfiguration\" target=\"_blank\">Minimal-Konfiguration<\/a> haben dank der verlinkten Schnellanleitungen problemlos funktioniert und waren nach maximal zehn Minuten abgeschlossen. Nach dem Start des lighttpd Webservers konnte ich nun per <code>http:\/\/<\/code> auf meine <strong>lokale Datei<\/strong> zugreifen:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"528\" height=\"64\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.37.25.png\" alt=\"\" class=\"wp-image-8284\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.37.25.png 528w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-14-um-17.37.25-300x36.png 300w\" sizes=\"auto, (max-width: 528px) 85vw, 528px\" \/><figcaption><em>URL-Eingabe bei lokalen Webserver zum Zugriff auf lokale Datei<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Hier die zugeh\u00f6rige und vollst\u00e4ndige Webserver Konfigurationsdatei <code>lighttpd.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server.document-root = \"\/Users\/chris\/Desktop\/Vielzutun\/SpaceNavigator\/Web\/\"\n\nserver.port = 3000\n\nmimetype.assign = (\n  \".html\" =&gt; \"text\/html\",\n  \".txt\" =&gt; \"text\/plain\",\n  \".jpg\" =&gt; \"image\/jpeg\",\n  \".png\" =&gt; \"image\/png\"\n)\n<\/code><\/pre>\n\n\n\n<p>Uff!!! Jetzt erst einmal verschnaufen und den erreichten Stand geniessen!  \ud83d\ude0e<\/p>\n\n\n\n<p>Denn nun geht es in der letzten Ausbaustufe noch darum, auf eine lokale Datei \u00fcber den lokalen Webserver mittels <code>http<strong><mark style=\"background-color:#ff675f\" class=\"has-inline-color has-dark-gray-color\">s<\/mark><\/strong>:\/\/<\/code> zugreifen zu k\u00f6nnen, um den im weiteren Entwicklungsverlauf ben\u00f6tigten &#171;<strong>secure context<\/strong>&#187; bereitzustellen.<\/p>\n\n\n\n<p>Im Internet werden verschiedene Methoden beschrieben, wie man das bewerkstelligen kann. Ich entschied mich f\u00fcr die <code><strong>mkcert<\/strong><\/code> Methode.<\/p>\n\n\n\n<p><code><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/FiloSottile\/mkcert\" target=\"_blank\">mkcert<\/a><\/strong><\/code> ist ein Tool, welches eine auf dem lokalen Rechner nutzbare <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Certificate_authority\" target=\"_blank\">Certification Authority <\/a>simuliert. Dessen Funktionsweise wird in der folgenden Grafik sehr anschaulich beschrieben:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1007\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-1024x1007.jpg\" alt=\"\" class=\"wp-image-8303\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-1024x1007.jpg 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-300x295.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-768x756.jpg 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-1536x1511.jpg 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert-1200x1181.jpg 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/mkcert.jpg 1600w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>Quelle f\u00fcr Grafik und sehr detaillierte Beschreibung: <a href=\"https:\/\/web.dev\/how-to-use-local-https\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/web.dev\/how-to-use-local-https\/<\/a><\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Nach korrekter Installation von <strong><code>mkcert<\/code><\/strong> findet sich in der lokalen &#171;Schl\u00fcsselbundverwaltung&#187; auf macOS der folgende Eintrag:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"363\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-1024x363.png\" alt=\"\" class=\"wp-image-8305\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-1024x363.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-300x106.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-768x272.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-1536x545.png 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37-1200x426.png 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.16.37.png 1956w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption><em>Frisch eingetroffen: die eigene Root-Zertifizierungsinstanz \ud83d\ude0e<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Nun muss nur noch die Konfiguration des Webserver <strong><code>lighttpd<\/code><\/strong> angepasst werden. Hier meine vollst\u00e4ndige <strong><code>lighttpd.conf <\/code><\/strong>, mit der ich endlich \u00fcber den gew\u00fcnschten &#171;secure context&#187; verf\u00fcge:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server.document-root = \"\/Users\/chris\/Desktop\/Vielzutun\/SpaceNavigator\/Web\/\"\n\nserver.modules   += ( \"mod_openssl\" )\n\n$SERVER&#91;\"socket\"] == \":443\" {\n  ssl.engine = \"enable\"\n  ssl.pemfile = \"\/Users\/chris\/Desktop\/Vielzutun\/SpaceNavigator\/Web\/mylocalhost.pem\"\n  ssl.ca-file = \"\/Users\/chris\/Library\/Application Support\/mkcert\/rootCA.pem\"\n}\n\nmimetype.assign = (\n  \".html\" =&gt; \"text\/html\",\n  \".txt\" =&gt; \"text\/plain\",\n  \".jpg\" =&gt; \"image\/jpeg\",\n  \".png\" =&gt; \"image\/png\"\n)<\/code><\/pre>\n\n\n\n<p><em>Die Pfade m\u00fcssen nat\u00fcrlich an die Verh\u00e4ltnisse beim jeweiligen Entwickler angepasst werden<\/em><\/p>\n\n\n\n<p>Nach einem Neustart von <strong><code>lighttpd <\/code><\/strong>ist das Ziel erreicht, das ersehnte Schloss-Symbol als Zeichen eines gelungenen Zugriffs \u00fcber <strong><code>https:\/\/<\/code><\/strong>  :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"488\" height=\"70\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.33.38.png\" alt=\"\" class=\"wp-image-8306\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.33.38.png 488w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-09.33.38-300x43.png 300w\" sizes=\"auto, (max-width: 488px) 85vw, 488px\" \/><figcaption><em>URL-Eingabezeile im Browser Chrome<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Ich habe diese Schritte so ausf\u00fchrlich beschrieben, weil sie einerseits f\u00fcr mich neu (und entsprechend aufwendig herauszufinden) waren und weil andererseits jeder Entwickler, der meine Ergebnisse f\u00fcr sich nutzen m\u00f6chte, nicht umhin kommen wird, diese auf seinem Rechner ebenfalls nachzuvollziehen. Ohne <code>https:\/\/<\/code> wird der weitere Verlauf jedenfalls nicht funktionieren, auch wenn andere Wege zur Aktivierung von <code>https:\/\/<\/code> beschritten werden k\u00f6nnen oder wurden.<\/p>\n\n\n\n<p>Jetzt, wo der Rucksack vollst\u00e4ndig gepackt ist, folgt die<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"WebHID\"><strong>4. Etappe &#8211; Auftritt WebHID<\/strong><\/p>\n\n\n\n<p>Die WebHID Entwickler stellen ebenfalls Code-Schnipsel bereit die die korrekte Nutzung dieser API demonstrieren. Diese nahm ich als Basis f\u00fcr meine erneuten Gehversuche:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>let deviceFilter = { vendorId: 0x046d };\nlet requestParams = { filters: &#91;deviceFilter] };\nlet outputReportId = 0x01;\nlet outputReport = new Uint8Array(&#91;42]);\n\nfunction handleConnectedDevice(e) {\n  console.log(\"Device connected: \" + e.device.productName);\n}\n\nfunction handleDisconnectedDevice(e) {\n  console.log(\"Device disconnected: \" + e.device.productName);\n}\n\nfunction handleInputReport(e) {\n  console.log(e.device.productName + \": got input report \" + e.reportId);\n  console.log(new Uint8Array(e.data.buffer));\n}\n\nnavigator.hid.addEventListener(\"connect\", handleConnectedDevice);\nnavigator.hid.addEventListener(\"disconnect\", handleDisconnectedDevice);\n\nnavigator.hid.requestDevice(requestParams).then((devices) =&gt; {\n  if (devices.length == 0) return;\n  devices&#91;0].open().then(() =&gt; {\n    console.log(\"Opened device: \" + device.productName);\n    device.addEventListener(\"inputreport\", handleInputReport);\n    device.sendReport(outputReportId, outputReport).then(() =&gt; {\n      console.log(\"Sent output report \" + outputReportId);\n    });\n  });\n});<\/code><\/pre>\n\n\n\n<p>Quelle: <a href=\"https:\/\/github.com\/WICG\/webhid\/blob\/main\/EXPLAINER.md\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/WICG\/webhid\/blob\/main\/EXPLAINER.md<\/a><\/p>\n\n\n\n<p>Erster m\u00f6glicher Fallstrick: die <code><strong>vendorID<\/strong><\/code>, als m\u00f6glicher Filter-Parameter beim Aufruf von:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>navigator.hid.requestDevice({ filters: &#91;{ vendorId: 0x046d }] )<\/code><\/pre>\n\n\n\n<p>Auch wenn mein SpaceNavigator deutlich sichtbar als ein &#171;3Dconnexion&#187; Produkt gelabelt ist (siehe einleitendes Foto), so identifiziert sich dieses Ger\u00e4t mit einer vendorID von <strong><code>0x046d<\/code><\/strong> als ein von &#171;Logitech Inc.&#187; stammendes Ger\u00e4t. Das Klebeetikett als Typenschildersatz am Anschlusskabel des Ger\u00e4ts, und speziell die macOS &#171;Systeminformationen&#187; geben hier eindeutige Auskunft:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"750\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/IMG_0258.jpg\" alt=\"\" class=\"wp-image-8334\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/IMG_0258.jpg 1000w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/IMG_0258-300x225.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/IMG_0258-768x576.jpg 768w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>3Dconnexion &#8211; a Logitech Company<\/em><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"880\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39-1024x880.png\" alt=\"\" class=\"wp-image-8332\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39-1024x880.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39-300x258.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39-768x660.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39-1200x1031.png 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-11.39.39.png 1410w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>Ma\u00dfgeblich ist die Hersteller-ID (vendorID)<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Da die Mutter aller Spacem\u00e4use, die SpaceMouse Classic eine Entwicklung der <a rel=\"noreferrer noopener\" href=\"https:\/\/www.dlr.de\/rm\/desktopdefault.aspx\/tabid-3808\/\" target=\"_blank\">DLR<\/a> ist und sich die Rechte an deren Vermarktung im Laufe der Jahrzehnte anscheinend mehrfach ge\u00e4ndert haben, sollte die M\u00f6glichkeit im Hinterkopf behalten werden, da\u00df Chargen derartiger Ger\u00e4te im Umlauf sein k\u00f6nnten die sich mit einer abweichenden vendorID identifizieren k\u00f6nnten! Da ich nur mein eigenes, ca. 15 Jahre altes Exemplar einer SpaceMouse \/SpaceNavigator zur Verf\u00fcgung habe, kann ich diese Vermutung weder best\u00e4tigen noch ausschlie\u00dfen.<\/p>\n\n\n\n<p>Wenn man die vendorID als Filterparameter richtig \u00fcbergeben hat, <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>navigator.hid.requestDevice({ filters: &#91;{ vendorId: 0x046d }] })\n.then((devices) =&gt; {\n\tif (devices.length == 0) return;\n\tdevice = devices&#91;0]\n\tif (!device.opened) device.open()\t\/\/ avoid re-opening an already open device\n\t.then(() =&gt; {\n  \t\tconsole.log(\"Opened device: \" + device.productName);\n  \t\tdevice.addEventListener(\"inputreport\", handleInputReport);\n\t})\n\t.catch(error =&gt; { console.error(error)\n\t})\n});<\/code><\/pre>\n\n\n\n<p>dann reagiert ein <a rel=\"noreferrer noopener\" href=\"https:\/\/caniuse.com\/webhid\" target=\"_blank\">kompatibler(!) Browser<\/a> mit einem Browser-generierten Auswahldialog, in dem der Anwender &#171;aus Sicherheitsgr\u00fcnden&#187; mit einer aktiven Benutzergeste (&#171;user gesture&#187;, d.h.: Klick, Touch) ein Ger\u00e4t zur Nutzung ausw\u00e4hlt:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"914\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-12.50.46-1024x914.png\" alt=\"\" class=\"wp-image-8342\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-12.50.46-1024x914.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-12.50.46-300x268.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-12.50.46-768x686.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-12.50.46.png 1028w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>Browser-generierter Auswahldialog<\/em><\/figcaption><\/figure>\n\n\n\n<p>Durch Klick auf die &#171;Verbinden&#187; Schaltfl\u00e4che wird dann das Device ge\u00f6ffnet und ein EventListener f\u00fcr f\u00fcr Events vom Typ &#171;inputreport&#187; installiert.<\/p>\n\n\n\n<p>An dieser Stelle eine Bemerkung zur Nomenklatur:<\/p>\n\n\n\n<p>Ein Universal Serial Bus (USB) ist ein durch den Host (das Computer-seitige Ende) kontrollierter Kommunikationskanal. S\u00e4mtlicher Datenaustausch \u00fcber diesen Bus wird vom Host durchgef\u00fchrt. Angeschlossene Ger\u00e4te, die in unregelm\u00e4\u00dfigen und nicht vorhersehbaren Intervallen Kommunikationsbedarf haben, k\u00f6nnen dies dem Bus &#171;anzeigen&#187;, im Sinne von:&#187;ich h\u00e4tte da etwas, kommst Du es Dir bitte bei Gelegenheit <strong>abholen<\/strong>?&#187; Das Device kann Daten also nicht zu beliebigen Zeiten in den Bus &#171;<strong>pumpen<\/strong>&#171;, sondern der Bus zieht die Daten vom Device ein, wenn er Zeit hat, sich darum zu k\u00fcmmern.<\/p>\n\n\n\n<p>Vor diesem Hintergrund, aus Sicht des Busses, bezeichnet &#171;<strong>input<\/strong>report&#187; ein Telegramm <strong>vom<\/strong> Device <strong>zum<\/strong> Host, etwa in der Folge einer Mausbewegung oder eines Tastendrucks, w\u00e4hrend ein &#171;<strong>output<\/strong>report&#187; einen Datenverkehr <strong>vom<\/strong> Host <strong>zum<\/strong> Device bezeichnet, etwa: &#171;schalte die LED ein\/aus&#187;. <\/p>\n\n\n\n<p>Der installierte Devicehandler f\u00fcr &#171;inputreport&#187; logged nun bereits munter Daten in die JavaScript console des Browsers, wenn man Tasten dr\u00fcckt und\/oder den Puck bewegt. Diese m\u00fcssen noch richtig interpretiert werden, was ich im wesentlich durch eine Kombination von &#171;scharf hinschauen&#187;, &#171;bereits verstandene Telegramme ausfiltern&#187; und &#171;trial&amp;error&#187; bew\u00e4ltigt habe. <\/p>\n\n\n\n<p>Entscheidend f\u00fcr das Verst\u00e4ndnis dieses Byte-Salats war die Erkenntnis, da\u00df Translation und Rotation als unterschiedliche Reports bereitgestellt werden, mit einer Reportl\u00e4nge von jeweils 6 Byte. Die Annahme, da\u00df f\u00fcr jede Achse jeweils ein vorzeichenbehafteter 2-Byte Wert zu ber\u00fccksichtigen sei, lag auf der Hand. Und so war es dann auch.<\/p>\n\n\n\n<p>Tasten-Events waren schnell entziffert. Weil sie sich leicht und in &#171;reiner&#187; Form vorhersehbar produzieren lassen.<\/p>\n\n\n\n<p>F\u00fcr das Toggeln der LED musste ich experimentieren. Einmal, weil dies der bisher erste <strong>output<\/strong>Report war, den ich also nicht in freier Wildbahn beobachten konnte. Zum Gl\u00fcck hatte ich die Wireshark-Telegramme, die auf ReportId = 4 f\u00fcr LED (gr\u00fcner Texthintergrund) hinwiesen:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"185\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-1024x185.png\" alt=\"\" class=\"wp-image-8536\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-1024x185.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-300x54.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-768x139.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-1536x278.png 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-2048x371.png 2048w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-20-um-10.35.24-1200x217.png 1200w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption><em>Byteweiser Vergleich von LED-Toggle-Telegrammen<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Auch gab es Fehlermeldungen bei Verwendung ung\u00fcltiger reportIds, und die Annahme, mit einem \u00fcbertragenen Wert von &#171;1&#187; bzw. &#171;0&#187; die LED ein- bzw. ausschalten zu k\u00f6nnen, war nicht mehr als eine begr\u00fcndete Vermutung. Die sich schnell als zutreffend erwies.<\/p>\n\n\n\n<p>Damit betrachte ich mein konkretes Problem:&#187;raw sensor data eines SpaceNavigator \/ SpaceMouse mittels JavaScript auslesen&#187; als vollst\u00e4ndig gel\u00f6st.<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Koordinatensystem\"><strong>Koordinatensystem<\/strong><\/p>\n\n\n\n<p>SpaceMouse bzw. SpaceNavigator liegen mit ihrem schweren, gummierten Fu\u00df rutschfest auf der horizontalen Schreibtischoberfl\u00e4che. Relativ hierzu bewegt der Anwender den federnd gelagerten und bez\u00fcglich aller sechs Freiheitsgrade leicht beweglichen Puck. Die Auslenkung des Pucks wird als Translation bzw. Rotation bez\u00fcglich jeder der drei Achsen vom device ausgewertet und als vorzeichenbehafteter Ganzzahlwert an den Bus \u00fcbergeben.<\/p>\n\n\n\n<p>Ich habe bei meinen Versuchen (Schriftzug &#171;3Dconnexion&#187; zum Anwender zeigend, USB-Kabel vom Anwender weg f\u00fchrend) das folgende Koordinatensystem und zugeh\u00f6rige Wertebereiche beobachtet:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Puck nach links &#8211; rechts bewegen:  Tx = [-340 .. +430]<\/li><li>Puck weg vom &#8211; zum Anwender bewegen: Ty =  [-430 .. +430]<\/li><li>Puck aus dem Tisch ziehen &#8211; in den Tisch dr\u00fccken:  Tz = [-410 .. +430]<\/li><li>Puck (Oberkante) nach vorne-hinten kippen: Rx = [-350 .. +370]<\/li><li>Puck (Oberkante) nach rechts &#8211; links kippen: Ry = [-340 .. +330]<\/li><li>Puck (Blick von oben) CCW &#8211; CW verdrehen: Rz = [-430 .. 370]<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"902\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1024x902.jpg\" alt=\"\" class=\"wp-image-8406\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1024x902.jpg 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-300x264.jpg 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-768x676.jpg 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1536x1352.jpg 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem-1200x1057.jpg 1200w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/SpaceMouseCoordinateSystem.jpg 2000w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption><em>SpaceMouse Koordinatensystem<\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Hardware\"><strong>Hardware Verf\u00fcgbarkeit<\/strong><\/p>\n\n\n\n<p>Das beschriebene Ger\u00e4t ist weiterhin unter dem aktuellen Namen &#171;SpaceMouse Compact&#187; als Neuware im aktuellen Angebot von 3Dconnexion erh\u00e4ltlich. Preis liegt in der Gr\u00f6\u00dfenordnung von ca. \u20ac150,- f\u00fcr Neuware, und deutlich g\u00fcnstiger im gebrauchten Zustand, z.B. bei Ebay.<\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Quellcode\"><strong>Quellcode<\/strong><\/p>\n\n\n\n<p>Wer eine SpaceMouse oder einen SpaceNavigator zur Verf\u00fcgung hat aber vor dem Stress zur\u00fcckschreckt, nur zum Ausprobieren einen eigenen Webserver inkl. secure context (https) aufzusetzen, f\u00fcr den habe ich den weiter unten bereitgestellten Quellcode auf meinen Webspace hochgeladen, von wo er \u00fcber die folgende URL direkt aufgerufen werden kann, ohne Modifikation des eigenen Rechners. Erfordert Google Chrome in einer Version &gt;= 100 . Der erforderliche secure context (https) wird bei Zugriff auf die u.a. URL direkt von meiner Webseite bereitgestellt:<\/p>\n\n\n\n<p><a href=\"https:\/\/vielzutun.ch\/wordpress\/public\/WebHID\/WebHID.html\">https:\/\/vielzutun.ch\/wordpress\/public\/WebHID\/WebHID.html<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Nachfolgend der vollst\u00e4ndige Quellcode zur Kommunikation mit einer SpaceMouse \/ SpaceNavigator in einem JavaScript-Kontext. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n   &lt;head&gt;\n      &lt;title&gt;WebHID Playground&lt;\/title&gt;\n      &lt;meta charset=\"utf-8\"&gt;\n      &lt;meta name=\"viewport\" content=\"width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0\"&gt;\n   &lt;\/head&gt;\n   &lt;body&gt;\n\n      &lt;div id=\"info\"&gt;\n         WebHID Playground by &lt;a href=\"https:\/\/vielzutun.ch\" target=\"_blank\" rel=\"noopener\"&gt;vielzutun.ch&lt;\/a&gt; &lt;br\/&gt;\n      &lt;\/div&gt;\n\n      &lt;button onclick=\"selectDevice()\"&gt;Request HID Device&lt;\/button&gt;\n      &lt;button onclick=\"ledOn()\"&gt;LED On&lt;\/button&gt;\n      &lt;button onclick=\"ledOff()\"&gt;LED Off&lt;\/button&gt;\n\n      &lt;script&gt;\n\nlet device;\n\nnavigator.hid.addEventListener(\"connect\", handleConnectedDevice);\nnavigator.hid.addEventListener(\"disconnect\", handleDisconnectedDevice);\n\nfunction handleConnectedDevice(e) {\n   console.log(\"Device connected: \" + e.device.productName);\n}\n\nfunction handleDisconnectedDevice(e) {\n   console.log(\"Device disconnected: \" + e.device.productName);\n   console.dir(e);\n}\n\nfunction selectDevice() {\n\n   navigator.hid.requestDevice({ filters: &#91;{ vendorId: 0x046d }] })\n   .then((devices) =&gt; {\n      if (devices.length == 0) return;\n      device = devices&#91;0]\n      if (!device.opened) device.open()\t\t\/\/ avoid re-opening an already open device\n      .then(() =&gt; {\n         console.log(\"Opened device: \" + device.productName);\n         device.addEventListener(\"inputreport\", handleInputReport);\n      })\n      .catch(error =&gt; { console.error(error)\n      })\n   });\n}\n\nfunction handleInputReport(e) {\n\n   switch ( e.reportId ) {\n   case 1:\t\t\/\/ translation event\n      const Tx = e.data.getInt16(0, true);\t\/\/ 'true' parameter is for little endian data\n      const Ty = e.data.getInt16(2, true);\n      const Tz = e.data.getInt16(4, true);\n      console.log(\"Tx: \" + Tx + \", Ty: \" + Ty + \", Tz: \" + Tz);\n      break;\n\t\t\t\t\n   case 2:\t\t\/\/ rotation event\n      const Rx = e.data.getInt16(0, true);\n      const Ry = e.data.getInt16(2, true);\n      const Rz = e.data.getInt16(4, true);\n      console.log(\"Rx: \" + Rx + \", Ry: \" + Ry + \", Rz: \" + Rz);\n      break;\n\t\t\t\t\n   case 3:\t\t\/\/ key press\/release event\n      const value = e.data.getUint8(0);\n\t\/*\n\t For my SpaceNavigator, a device having two (2) keys only:\n\t value is a 2-bit bitmask, allowing 4 key-states:\n\t value = 0: no keys pressed\n\t value = 1: left key pressed\n\t value = 2: right key pressed\n\t value = 3: both keys pressed\n\t *\/\n\tconsole.log(\"Left key \" + ((value &amp; 1) ? \"pressed,\" : \"released,\") + \"   Right key \" + ((value &amp; 2) ? \"pressed, \" : \"released;\"));\n\tbreak;\n\t\t\t\n   default:\t\t\/\/ just in case a device exhibits unexpected capabilities  8-)\n      console.log(e.device.productName + \": Received UNEXPECTED input report \" + e.reportId);\n      console.log(new Uint8Array(e.data.buffer));\n   }\n\n}\n\nfunction ledOn() {\n   const outputReportId = 4;\n   const outputReport = Uint8Array.from(&#91;1]);\n\t\t\n   device.sendReport(outputReportId, outputReport)\n   .then(() =&gt; {\n      console.log(\"Sent output report \" + outputReportId + \": \" + outputReport);\n   })\n   .catch(error =&gt; { console.error(error)\n   })\n}\n\nfunction ledOff() {\n   const outputReportId = 4;\n   const outputReport = Uint8Array.from(&#91;0]);\n\t\t\n   device.sendReport(outputReportId, outputReport)\n   .then(() =&gt; {\n      console.log(\"Sent output report \" + outputReportId + \": \" + outputReport);\n   })\n   .catch(error =&gt; { console.error(error)\n   })\n}\n\t\t\n      &lt;\/script&gt;\n   &lt;\/body&gt;\n&lt;\/html&gt;\n<\/code><\/pre>\n\n\n\n<p>Die dekodierten Daten werden in dem folgenden, f\u00fcr Menschen lesbaren Format ausschlie\u00dflich in der JavaScript-Konsole angezeigt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Tx: -28, Ty: 71, Tz: -14\nRx: -256, Ry: 190, Rz: -248\nLeft key pressed,   Right key released;\nLeft key pressed,   Right key pressed,\nLeft key released,   Right key pressed, \nLeft key released,   Right key released;\nSent output report 4: 1\nSent output report 4: 0<\/code><\/pre>\n\n\n\n<p>Da ich eingangs \u00fcber die absurde Komplexit\u00e4t des von 3Dconnexion bereitgestellten Beispiel-Codes gel\u00e4stert hatte, habe ich mich hier im Sinne einer Kontrastmaximierung so knapp wie m\u00f6glich gehalten. Ein minimales Error-Handling ist vorhanden, umfasst aber keine h\u00f6her-wertigen Absicherungen wie z.B. Verriegelung gegen\u00fcber einem Telegrammversand <strong>vor<\/strong> \u00d6ffnung des device. Hierf\u00fcr bitte ich um Verst\u00e4ndnis.<\/p>\n\n\n\n<p>In einem <a rel=\"noreferrer noopener\" href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8554\" target=\"_blank\">Folgebeitrag<\/a> stelle ich eine Muster-Demo f\u00fcr die Nutzung der rohen (raw) Sensordaten einer SpaceMouse f\u00fcr eine intuitive Kamerasteuerung in einem Three.js Programm vor.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"has-medium-font-size\" id=\"Einschr\u00e4nkungen\"><strong>Bekannte Einschr\u00e4nkungen<\/strong><\/p>\n\n\n\n<p>Die bedeutendste Einschr\u00e4nkung d\u00fcrfte sein, da\u00df die <strong>WebHID<\/strong> API nicht von allen Browsern unterst\u00fctzt wird und mancherorts sogar als &#171;experimentell&#187; bezeichnet wird. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"404\" src=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-1024x404.png\" alt=\"\" class=\"wp-image-8373\" srcset=\"https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-1024x404.png 1024w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-300x118.png 300w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-768x303.png 768w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-1536x606.png 1536w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-2048x808.png 2048w, https:\/\/vielzutun.ch\/wordpress\/wp-content\/uploads\/2022\/04\/Bildschirmfoto-2022-04-15-um-15.58.59-1200x474.png 1200w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption><em>Unterst\u00fctzende Browser lt.  <a rel=\"noreferrer noopener\" href=\"https:\/\/caniuse.com\/webhid\" target=\"_blank\">caniuse.com<\/a><\/em><\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Ich habe meine Entwicklung mit <strong>Google Chrome<\/strong> Version 100.0.4896.127 (Offizieller Build) (x86_64). durchgef\u00fchrt. Der vorliegende Erfolgsbericht basiert auf dem Einsatz von Chrome.<\/p>\n\n\n\n<p>Ich habe auch Tests mit <strong>Opera<\/strong> Version 85.0.4341.60 (x86_64) durchgef\u00fchrt. Obwohl Opera lt. Debugger \u00fcber die erforderliche <code><strong>navigator.hid<\/strong><\/code>  Erweiterung verf\u00fcgt, scheitert der identische Code leider bereits bei der Erkennung des Contexts einer &#171;User Gesture&#187;, also Klick oder Touch. Was ich f\u00fcr einen Fehler bei Opera halte, f\u00fcr den ich einen Fehlerbericht beim Opera Entwiclerteam eingereicht habe.<\/p>\n\n\n\n<p>Zu Microsoft Edge kann ich keine Aussage machen.<\/p>\n\n\n\n<p>Ich empfehle, vor einem Einsatz in einer Produktionsumgebung abzukl\u00e4ren, ob diese Einschr\u00e4nkung akzeptiert werden kann. Durch feedback an die Entwickler von WebHID kann man vermutlich dazu beitragen, da\u00df diese API weiterentwickelt und m\u00f6glicherweise von weiteren Browseranbietern adaptiert wird.<\/p>\n\n\n\n<p>Inzwischen habe ich das <a rel=\"noreferrer noopener\" href=\"https:\/\/discourse.threejs.org\" target=\"_blank\">Three.js Forum<\/a> auf meine Erkenntnisse aufmerksam gemacht, um meinen Teil zur Verbreitung beizutragen.<\/p>\n\n\n\n<p>Was meine SpaceMouse \/ SpaceNavigator betrifft:<\/p>\n\n\n\n<p>Ich habe in der aktuellen und auch fr\u00fcheren Versionen von 3DxWare eine Funktion gesehen zum &#171;Kalibrieren&#187; einer SpaceMouse. Dies war fr\u00fcher (SpaceMouse Classic (serial)) gelegentlich erforderlich, wenn es eine Drift bez\u00fcglich des Nullpunkt der SpaceMouse gab. Dann hat das device st\u00e4ndig geringe Verschiebungen reported, auch wenn der Puck garnicht ber\u00fchrt wurde. \u00dcber die &#171;Kalibrieren&#187; Funktion konnte man dann die aktuelle Neutralstellung des Pucks als neuen Nullpunkt definieren.<\/p>\n\n\n\n<p>Da ich diesen Zustand an meinem SpaceNavigator weder beobachten, noch k\u00fcnstlich hervorrufen konnte, konnte ich auch keine Abhilfe daf\u00fcr\/dagegen entwickeln. Ich \u00fcberlasse dies dem interessierten Leser, und w\u00fcrde mich \u00fcber jegliches Feedback freuen. Als Starthilfe: die Wireshark Protokolle weisen darauf hin, da\u00df zum Kalibrieren ein &#171;LED On&#187; Telegramm verschickt wird.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Also available in: English. In diesem Beitrag beschreibe ich die Entwicklung (inkl. Ver\u00f6ffentlichung des vollst\u00e4ndigenen Quellcodes, 1 Datei mit ca. 70 SLOC) einer low-level Schnittstelle f\u00fcr den Zugriff auf eine SpaceMouse bzw. SpaceNavigator unter JavaScript. Inhaltsverzeichnis: Einleitung Pleite beim Anbieter Auftritt Wireshark Auftritt WebUSB Entwicklungsumgebung Auftritt WebHID Koordinatensystem Hardware Quellcode Bekannte Einschr\u00e4nkungen Schau Dir auch &hellip; <a href=\"https:\/\/vielzutun.ch\/wordpress\/?p=8179\" class=\"more-link\"><span class=\"screen-reader-text\">&#8220;SpaceMouse auslesen unter JavaScript&#8221; <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_stc_notifier_status":"","_stc_notifier_sent_time":"","_stc_notifier_request":false,"_stc_notifier_prevent":false,"_stc_subscriber_keywords":"","_stc_subscriber_search_areas":"","footnotes":""},"categories":[184],"tags":[187,188,185,186,178,189],"class_list":["post-8179","post","type-post","status-publish","format-standard","hentry","category-computergrafik","tag-3dconnexion","tag-javascript","tag-spacemouse","tag-spacenavigator","tag-three-js","tag-webhid"],"_links":{"self":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8179","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8179"}],"version-history":[{"count":228,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8179\/revisions"}],"predecessor-version":[{"id":8637,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/8179\/revisions\/8637"}],"wp:attachment":[{"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8179"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8179"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vielzutun.ch\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8179"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}