USB-Fußtaster

Aus Hobbyelektronik.org
Wechseln zu: Navigation, Suche
Fertig aufgebauter Fußtaster

"Schau Mama, freihändig!"

Wer kennt das nicht? Man muss auf mehreren Leiterkarten verschiedene Spannungen messen.

Üblicherweise läuft es dann wie folgt ab:

  1. Messspitzen platzieren
  2. Wert vom Messgerät ablesen
  3. Messspitzen ablegen und zum PC umdrehen
  4. Messwert eintippen
  5. Zurückdrehen, Messspitzen wieder in die Hände nehmen
  6. Nochmal nachschauen, welcher Messpunkt jetzt dran ist
  7. Wiederholen

Ab und zu mischt sich dann das erneute Messen ein, weil ein Wert unplausibel ist und man entweder einen Zahlendreher drin oder schneller getippt als gedacht hat.

Unterm Strich ist es auf jeden Fall sehr zeitraubend und nervig.

Gleichzeitig baumeln die Füße unbeteiligt am Stuhl herunter.

Die meisten Messgeräte (und anderes Laborequipment) lassen sich über den PC kontrollieren, warum also nicht gleich die richtigen Werte z. B. nach Excel transferieren? Nur irgendwie muss man den Prozess anstoßen...

Die schnelle Lösung

Tastatur ist blöd, also muss eine andere Eingabemöglichkeit her. Ein Taster und eine Projektbox ist schnell gefunden, Loch in die Box und ein paar große Schrauben dazu (damit sie nicht gleich abhaut) und fertig ist der improvisierte Fußtaster. Nur wie kommt die Info in den PC?

Die serielle Schnittstelle hat neben Rx und Tx oft auch Steuerleitungen – aus den alten Tagen, in denen Modems noch so richtig langsam waren. Diese lassen sich in so ziemlich jeder Programmiersprache kontrollieren und abfragen. Ein FT232 liegt auch noch herum, also Taster angelötet und los geht’s!

Das Programm ist in Python geschrieben, pollt die CTS-Leitung und sobald diese low wird, wird per pyVisa den Multimeter der aktuelle Messwert entlockt, passend formatiert und mit dem Keyboard-Modul "ausgetippt". Damit die Augen am Mikroskop bleiben können, wird zudem ein kurzer Piepton ausgegeben.

Sehr viel Spaß für wenig Geld.

Leider muss man bei jeder Benutzung schauen, welcher COM-Port dem Adapter zugeordnet wurde und ob dieser auch wirklich im Script steht.

Die schöne Lösung

Eines Tages brachte mein Kollege einen kleinen Pappkarton vom Ramschmax mit, Inhalt: 420705. Ein Gusseiserner Fußtaster mit schönem Klick-Klack-Mikroschalter.

Der hat etwas Besseres als den schnöden UART-Adapter mit Zwillingslitze verdient – endlich wieder eine schöne Anwendung für V-USB!

Was soll das Teil können? Am besten natürlich so viel wie möglich, damit es vielleicht sogar als "Adaptive Controller" dienen kann. Deshalb habe ich mich dafür entschieden, einen Multidevice-Ansatz zu fahren. Das umfasst aktuell:

  • Generisches USB-HID
  • Einzeltasten-Tastatur
  • Makro-Tastatur
  • Joystick

Hardware

Die Hardware könnte nicht einfacher sein - sie entspricht größtenteils dem Easylogger-Beispielprojekt, nur dass anstelle der automatischen Oszillator-Kalibrierung ein externer Quarz verwendet wird. Hintergrund hierfür ist, dass das SNES-Joypad, das sich selbst kalibriert (oder zumindest sollte), am RetroPie beim Booten oft nicht erkannt wird. Kann aber auch gut sein, dass es sich um einen Bug in meiner Firmware handelt.

Die Bauteile sind - abgesehen vom Quarz und Mikrocontroller in SMD-Bauform. Als MCU werkelt fürs Erste ein ATtiny85, der später noch herunterskaliert werden kann. Die Wahl auf das DIP-Gehäuse fiel, um ohne Löten die Firmware herunterzuladen.

Mit Quarz als Taktquelle bleibt noch ein "richtiger" IO übrig (sofern man den Reset-Pin nicht deaktiviert) - mehr braucht man nicht.

Eine eigens designte Leiterkarte ist aufgrund der Einfachheit der Schaltung völlig unnötig, aber es war noch Platz auf dem Panel...

Menge Referenz Wert Package Reichelt Bestellcode
1 SV3 MA03-1
1 SV1 MA04-1 GC 2212-EU030
1 C1 100n C0603 X7R-G0603 100N
1 C4 10u C0805 KEM X7R0805 10U
1 Q1 12M HC49U-V 12,0000-HC49U-S
1 R11 150 R0603 RND 0603 1 150
1 C5 150n C0603 X7R 0603FCE 150N
2 C2, C3 22p C0603 RND 150MT18N2202
1 R3 2k2 R0603 RND 0603 1 2,2K
1 R10 4k7 R0603 RND 0603 1 4,7k
2 R1, R2 68 R0603 RND 0603 1 68
1 IC1 ATTINY 85-20 PU DIL08 ATTINY 85-20 PU
2 D1, D2 BZX84C3V6 SOT23 SMD ZD 3,6

Leider stellte sich erst zu spät heraus, dass die Leiterkarte ein bisschen zu groß ist, um sie an der eigentlich vorgesehenen Stelle zu montieren. Man kann sie zwar ein bisschen schräg anschrauben und von der Ecke noch etwas Material wegnehmen, aber dann ist die USB-Leitung auch wieder im Weg. Das Design ist also nicht so richtig geglückt...

Aber kein Problem, dass man nicht mit ein bisschen Schrumpfschlauch lösen könnte:

Der Schirm ist übrigens auf ein Stück Leitung gecrimpt, das wiederum an die Halterung des Tasters gelötet ist.

Firmware

Firmware

AVR
Typ ATtiny85
Takt 12 MHz
Fuses
High 0xD7
Low 0xDE
Extended 0xFF
Engbedded com logo.png Details

Die Firmware ist mehr oder weniger viergeteilt, entsprechend den verschiedenen Modi.

Hierfür gibt es mehrere Gründe:

  • die freien VID/PID-Paare sind auf die verschiedenen Funktionen beschränkt und es lässt sich nicht zweifelsfrei sagen, ob Multi-Endpoint-Konfigurationen erlaubt sind
  • Die Fragmentierung macht den Code (teilweise) einfacher und übersichtlicher
  • Keinerlei Treiberprobleme (es gibt wohl Betriebssysteme, die sich an Multi-Endpoint verschlucken)
  • Es ist nicht ganz so üblich und in der Implementierung interessanter

Nach dem Reset liest die Firmware den zu verwendenden Modus aus dem EEPROM. Ist diese ungültig oder der IO des Tasters auf Masse gezogen, wird der auf Generisches HID gesetzt. Gegenüber des USB-Treibers wird nun auch der Pointer für die USB-Deskriptoren gesetzt (die sich wiederum im Flash befinden).

Anschließend läuft die Initialisierung der unterschiedlichen Modi und es wird in die entsprechende Endlosschleife gesprungen.

Als USB VID/PID-Paare werden von obdev frei zur Verfügung gestellten verwendet:

  • HID: 0x16C0/0x27D9
  • Tastatur: 0x16C0/0x27DB
  • Gamepad: 0x16C0/0x27DC

Im Grunde ist das alles kein Hexenwerk, allerdings haben die "Dynamisierung" der USB-Deskriptoren einiges an (auf dem Attiny schwierige) Fehlersuche gekostet. Über Manche Dinge, wie z. B. wie man die verschiedenen Deskriptoren den Anfragen zuordnet, ist die Dokumentation leider etwas vage (oder ich habe sie nicht richtig gelesen/falsch gesucht).

Auch lässt sich die Firmware noch ziemlich optimieren, das gesteckte Ziel wäre, dass sie auf den ATtiny45 passt. Aktuell ist hauptsächlich die RAM-Belegung ein Problem (und noch nicht optimal).

Konfiguration

Um die Konfiguration des Fußtasters ändern zu können, muss er sich in Modus 2 (Generisches HID) befinden. Sollte dieser Modus nicht aktiv sein, kann er erreicht werden indem der Taster gedrückt wird, während der USB-Stecker verbunden wird.

In den Report-Beispielen werden stets hexadezimale Zahlen verwendet. Angaben mit xx sind beliebig bzw. müssen ignoriert werden.

Keycodes

Bevor auf die verschiedenen Funktionen im Detail eingegangen wird, ein paar Worte zu den Keycodes:

Tastaturen sind seit jeher ziemlich einfach gestrickt. So "weiß" der Controller der Tastatur zwar an welcher Position eine Taste gedrückt wird, aber nicht welches Layout sie vertritt - dies wird erst durch das Betriebssystem umgesetzt. Das hat zum Beispiel den Effekt, dass mit einer deutschen Tastatur an einem auf Englisch eingestellten PC u. a. Y und Z vertauscht sind (und man beim Raspberrz Pi das Passwort mindestens einmal falsch eingibt).

Diese Eigenschaft betrifft natürlich auch den Fußtaster.

Die Keycodes für englische Tastaturen können in der Spezifikation "Universal Serial Bus HID Usage Tables" in der aktuellen Version ab Seite 53 (Kapitel 10 Keyboard/Keypad Page (0x07)]) nachgeschlagen werden.

Für das deutsche Layout befindet sich eine Tabelle im Download-Bereich (keine Garantie auf Vollständigkeit und Korrektheit).

Modus 0: Generisches HID

Die Kommunikation findet über Report ID 0 mit 8 Datenbytes statt. Beim Senden von Reports definiert das erste Byte den Befehl, in den weiteren befinden sich die Parameter dessen:

Nummer Befehl Beschreibung
0 NOP Keine Operation, dieser Code wird bei den Antworten für den Status des Tasters verwendet
1 Restart Neustart des Mikrocontrollers (ersetzt das Aus- und Einstecken nachdem der Modus umgestellt wurde)
2 Lesen Modus Gibt den Interface-Modus zurück
3 Setzen Modus Setzt den Interface-Modus
4 Lesen Einzeltaste Gibt den Keycode der Einzeltasten-Tastatur zurück
5 Setzen Einzeltaste Setzt den Keycode der Einzeltasten-Tastatur
6 Lesen Tastensequenz Gibt 6 Byte der Sequenz des angegebenen Blocks (in Byte 1) zurück
7 Setzen Tastensequenz Setzt die 6 Bytes der Sequenz des angegebenen Blocks (in Byte 1)

Jeder Befehl wird beantwortet, wobei dem ersten Byte eine Statusinformation mit bitweise Oder hinzugefügt wird:

Nummer Status Beschreibung
0x00 None Wird nicht verwendet
0x40 Busy Der Befehl wurde nicht ausgeführt, weil der Controller beschäftigt ist
0x80 Ok Der Befehl wurde erfolgreich ausgeführt, die optionale Antwort befindet sich in den restlichen Bytes
0xC0 Error Beim Ausführen des Befehls ist ein Fehler aufgetreten

Tastenstatus

Für jede Änderung des Tastenstatus wird ein Report übertragen:

< 00 BB xx xx xx xx xx xx

  • "Befehl": Status der Taste
  • Status der Taste - 0: nicht gedrückt, 1: gedrückt

Modus 1: Einzeltasten-Tastatur

In diesem Modus verhält sich der Fußtaster wie die Taste einer Tastatur.

Die zu drückende Taste kann über Modus 2 festgelegt werden.

Leider ist es aktuell noch nicht möglich, Multimedia-Tasten (Wiedergabesteuerung, Lautstärke, Anwendungssteuerung) zu verwenden.

Lesen

Zum Zurücklesen der verwendeten Taste muss folgender Report gesendet werden:

> 04 xx xx xx xx xx xx xx

  • Befehl: Lesen Einzeltaste

Die Antwort entspricht folgendem Muster:

< 84 MM KK xx xx xx xx xx

  • Befehl: Lesen Einzeltaste (OK)
  • Bitmaske Modifier (siehe oben)
    • 0x01: Steuerung links
    • 0x02: Umschalttaste links
    • 0x04: Alt links
    • 0x08: GUI links (u. a. Windows-Taste)
    • 0x10: Steuerung rechts
    • 0x20: Umschalttaste rechts
    • 0x40: Alt rechts (müsste Alt+Gr sein)
    • 0x80: GUI rechts (müsste Kontextmenü sein)
  • Keycode Taste

Schreiben

Zum Setzen einer Taste folgender Report gesendet werden:

> 05 MM KK xx xx xx xx xx

  • Befehl: Setzen Einzeltaste
  • Bitmaske Modifier (siehe oben)
  • Keycode Taste

Die restlichen Bytes im Report werden ignoriert.

Die Bestätigung wird wie unter "Modus 2" beschrieben gesendet. Da keine Readback stattfindet, wird immer mit "OK" geantwortet:

< 85 xx xx xx xx xx xx xx

Modus 2: Makro-Tastatur

Über die Makro-Tastatur können längere Tastenfolgen ausgegeben werden. Für diesen Zweck sind 128 Byte im EEPROM vorgesehen, was einer maximalen Folge von etwa 63 Anschlägen entspricht.

Aktuell kann nur eine Taste gleichzeitig gedrückt werden kann - ein Rollover ist dementsprechend nicht möglich.

Die Ausgabe erfolgt mit maximaler Geschwindigkeit, also mit etwa 100 Anschlägen pro Sekunde, wobei zwischen gleichlautenden Anschlägen sowie nach dem letzten Zeichen automatisch ein Loslassen der Taste eingefügt wird, was zu einer kleinen Verzögerung führt und damit das Timing verändert

Beispiel: Bei der Ausgabe des Wortes Hallo folgende Reports übertragen:

  • Umschalttaste drücken, Taste "H" drücken
  • Umschalttaste loslassen, Taste "A" drücken
  • Taste "L" drücken
  • Taste "L" loslassen
  • Taste "L" drücken
  • Taste "O" drücken
  • Taste "O" loslassen

So dauert die Übertragung des Wortes - bei einem Report-Intervall von 10 ms - etwa 70 ms statt den zunächst anzunehmenden 50 ms.

Datenformat

Die Daten werden im Muster Befehl + Daten gespeichert, wobei die Datenlänge dynamisch (aber anhand des Befehls ersichtlich) ist.

Dementsprechend sind die Befehle teilweise als Flags aufgebaut, zum besseren Verständnis hier im Binärformat:

  • 0b11111111: Ende der Sequenz (entspricht nicht beschriebenen Speicherzellen), keine Daten
  • 0b1ppppppp: Pause in 5 ms-Schritten, p = 0 ... 126, t = (p + 1) * 5 ms, maximal 635 ms, keine Daten
  • 0b0mmxxxxx: Verhalten der Modifier
    • 0b00: Modifier beibehalten, keine Daten
    • 0b01: Modifier für nachfolgende Taste drücken (anschließend loslassen), +1 Byte Daten
    • 0b10: Modifier dauerhaft halten, +1 Byte Daten
    • 0b11: Modifier loslassen, keine Daten
  • 0b0xxkkxxx: Verhalten der Taste
    • 0b00: Taste beibehalten, keine Daten
    • 0b01: Taste einmal drücken (anschließend loslassen), +1 Byte Daten
    • 0b10: Taste dauerhaft halten (bis eine andere Taste gedrückt wird), +1 Byte Daten
    • 0b11: Taste loslassen, keine Daten

Sollen Modifier und Taste mit einem Befehl verändert werden, wird zuerst der Modifier erwartet, dann die Taste:

0b01001000 0b00000010 0b00001011

Drückt und hält die linke Umschalttaste und drückt die Taste H (0x0B) einmal.

Beispiel: Es soll die Zeichenfolge <code>Hallo WELT mit anschließendem Zeilenumbruch ausgegeben werden, mit einer Pause von 500 ms nach "Hallo".

Die Sequenz sieht nun wie folgt aus (Binär und Hex gemischt, Hex für Keycodes):

Code Befehl Parameter
0b00101000 0b00000010 0x0B Modifier einmal drücken, Taste einmal drücken Linke Umschalttaste, Taste H
0b00001000 0x04 Modifier beibehalten, Taste einmal drücken Taste A
0b00001000 0x0F Modifier beibehalten, Taste einmal drücken Taste L
0b00001000 0x0F Modifier beibehalten, Taste einmal drücken Taste L
0b00001000 0x12 Modifier beibehalten, Taste einmal drücken Taste O
0b11100011 Pause oder Ende der Sequenz Pause mit 99 Zeiteinheiten: t = (99 + 1) * 5 ms = 500 ms
0b00001000 0x2C Modifier beibehalten, Taste einmal drücken Leertaste
0b01101000 0b00000010 0x1A Modifier drücken und halten, Taste einmal drücken Rechte Umschalttaste, Taste W
0b00001000 0x08 Modifier beibehalten, Taste einmal drücken Taste E
0b00001000 0x0F Modifier beibehalten, Taste einmal drücken Taste L
0b00001000 0x17 Modifier beibehalten, Taste einmal drücken Taste T
0b01000000 Modifier loslassen, Kein Tastendruck -
0b00001000 0x28 Modifier beibehalten, Taste einmal drücken Return-Taste
0b11111111 Pause oder Ende der Sequenz Ende der der Sequenz

Lesen

Wie bereits in der Befehlsübersicht erwähnt, erfolgt das Lesen und Schreiben der Sequenzdaten in Blöcken:

> 06 CC xx xx xx xx xx xx

  • Befehl: Lesen Sequenz
  • Blocknummer: 0 ... 21 (bei 128 Byte Speicher)

Als Antwort bekommt man dann die Daten des jeweiligen Blocks:

< 86 CC DD DD DD DD DD DD

  • Befehl: Lesen Sequenz (OK)
  • Blocknummer: 0 ... 21
  • 6 Datenbytes des Blocks

Ist der letzte Block nicht "füllend", sind die restlichen Bytes zufällig, ebenso kann über das "End of Sequence" hinaus gelesen werden.

Schreiben

Das Schreiben von Blöcken ist der Antwort vom Lesen sehr ähnlich:

> 07 CC DD DD DD DD DD DD

  • Befehl: Schreiben Sequenz
  • Blocknummer: 0 ... 21 (bei 128 Byte Speicher)
  • 6 Datenbytes des Blocks

Sobald die Daten geschrieben wurden, antwortet der Mikrocontroller mit:

< 06 xx xx xx xx xx xx xx

  • Befehl: Schreiben Sequenz (OK)

Modus 3: Gamepad

Es ist keine Konfiguration notwendig, das Gamepad meldet sich mit einer Taste und kann in Anwendungen wie gewohnt zugeordnet werden.

Leider erkennt weder Firefox 73 noch Chromium 82 das Pad im html5gamepad-Tester.

Anzumerken ist, dass wie bei der Einzeltasten-Tastatur Reports nur bei Zustandsänderung der Taste gesendet werden. Bis jetzt konnte ich dadurch keine Einschränkungen feststellen.

PC-Software

Aktuell gibt es eine kleine Python-Lib und ein paar Beispiele dazu.

Siehe Downloads.

Anmerkungen

  • Die Firmware auf dem Tiny85 zu entwickeln war grauenvoll, da man nur sehr schlecht debuggen kann. Das nächste Mal lieber auf einem Mikrocontroller mit deutlich mehr IOs und Hardware-UART entwickeln und anschließend auf ein kleineres Geschwister portieren
  • micronucleus ist ein USB-Bootloader basierend auf V-USB, den ich bis jetzt noch nicht verwendet habe, in Zukunft aber sollte
  • Der Quellcode steht, wie von objective development gefordert, unter GPLv2

Leiterkarten

Es gibt noch unbestückte Leiterkarten. Wer eine will, kann sich gerne bei mir melden.

Download

  • Datei:Footswitch.zip Schaltplan & Layout in EAGLE 7.7.0, Firmware & Sourcen in Atmel Studio 7, Beispielanwendung in Python 3.7

Weblinks