Hardware, COM-Ports und Python
Aus ist es mit .NET, Windows und grafischen Oberflächen! Easysurfer is Python-Begeistert und steuert Hardware an! Weg mit dem Klickibunti-Kram, gebt mir einen Lötkolben und ein paar Drähte! Nein, ganz so schlimm hat es mich nicht erwischt. Dennoch folgt jetzt ein Beitrag, der sich dieses mal etwas hardwarenäher abspielt.
Ich gebe zu dass ich von Elektrotechnik keine Ahnung habe. Vielleicht finde ich es gerade deshalb so toll einen Geldscheinprüfer in der Hand zu haben und ein Protokoll zu implementieren. Man schickt ein Befehl und der Prüfer fängt an zu Blinken, zu surren und schließlich einen Geldschein zu verschlucken. Toll!
Genug der Euphorie, es stellt sich die Frage: Woher hat er so ein Ding? Und wozu braucht er es überhaupt? Ein Freund und ich bauen mit minimalistischer Hardware ein Art Bitcoin-Transfer-Automat. Die Idee dahinter ist simple: Der Kunde wählt einen Betrag und eine Bitcoin-Adresse aus, der Automat frisst die Scheine und transferiert den entsprechenden Betrag in Bitcoin an die eingelese Adresse. Es wird zu gegebener Zeit einen vollen Artikel zu diesem Projekt geben, aber heute ist der Geldscheinprüfer dran.
Es handelt sich um einen Apex 7000 der Firma Pyramid Technologies. Die genauen Spezifikationen interessieren uns gerade wenig, wir wollen das Ding anschließen und Programmieren. Im Lieferumfang waren 2 Kabel dabei: Ein USB zu 6-Pin-Kabel mit welchem man die Firmware flashen und einstellen kann, sowie ein USB zu 18-Pin Kabel, welches noch zusätzlich mit Strom über ein 4 Pin-Kabel vom Netzteil versorgt wird. Wer sich jetzt unter meinem untechnischen gebrabbel nichts vorstellen kann hat hier noch ein frisch fotografiertes Bild:
Nachdem der PC aufgeschraubt, ein Stromkabel geklaut und die Treiber installiert sind kann es losgehen. Angesprochen wird das Teil über einen seriellen COM-Port. Das Gerät sendet keine Daten raus, sofern wir sie nicht anfordern (pollen). Daher schauen wir erstmal ein Blick in die PDF, in welcher das Transferprotokoll beschrieben ist: RS-232 Serial Interface Specifications.
Erstellen wir in .NET ein neues COMPort-Objekt mit den angegebenen Übertragungsdaten für das Protokoll:
// Neuer SerialPort mit COM4 als Anschluss und BaudRate von 9600 SerialPort port = new SerialPort("COM4", 9600); // Übertragungsintere Daten setzen port.DataBits = 7; port.StopBits = StopBits.One; port.Parity = Parity.Even; // Los gehts port.Open(); |
Wir basteln uns ein einfaches Packet um den Gerätstatus abzufragen. Beginnen muss jedes Packet mit 0×02, gefolgt von einer Length und dann einem MSG-Type mit ACK-Nummer. Die Länge setzen wir wenn wir das Packet fertig haben. Da wir vom Master zum Acceptor senden, setzen wir das 4te Bit auf 1 und erhalten so zunächst die Zahl 0×10. Fehlt noch das ACK Bit, das jeweils zwischen 0 und 1 wechselt. Da hierfür die Bits 0-3 reserviert sind, setzen wir es durch “Packet OR IsAckBitSet”. Damit haben wir bis dahin:
0x02 0xXX (0x10 | IsAckBitSet)
Nun folgt der etwas spannendere Part, das basteln der eignetlichen Daten. Hierfür können wir soviele Bytes verwenden wie wir wollen, solange wir es in der Length angeben. Im Protokoll sind jeweils 3 definiert, also halten wir uns auch daran. Das erste Byte gibt an, welche Scheine wir überhaupt akzeptieren. Da wir ein Euro-Modell des Prüfers haben, sind wegen der Breite nur 5, 10 und 20€ Scheine unterstützt. So setzen wir die ersten 4 Bits dieses Bytes und erhalten 0x0F. Das nächste Byte hat einige reservierte Flags drin die es zu setzen gilt, interessant ist aber das “ESCROW”-Bit welches den Geldautomaten zum Einziehen auffordert. Somit erhalten wir das Byte 10000 und somit 0×10. Sobald der Geldschein eingezogen ist, müssen wir dieses Bit auf “STACK” (0×20) oder “RETURN” (0×40) ändern. Die Bits des dritten Bytes sind reserviert und von uns daher alle auf 0 gesetzt. Damit ist der Data-Teil abgeschlossen und “END OF MESSAGE”-Byte (0×03) gefolgt. Das letzte Byte des Packets ist eine XOR-Checksum aus der Länge, Type und Data. Damit haben wir bis dahin:
0x02 0x08 (0x10 | IsAckBitSet) 0x0F (0x10 ODER 0x20) 0x00 0x03 ((0x08 | IsAckBitSet) ^ 0x0F ^ (0x10 ODER 0x20) ^ 0)
Easy, nicht? In Code sogar noch übersichtlicher:
bool isAckSet = false; while (Console.ReadLine() == "") // Durchläuft jedes mal wenn wir ENTER drücken { byte[] mySendBuffer = new byte[8]; mySendBuffer = new byte[] { 0x02, 0x8, 0x10, 0x0F, 0x10, 0x00, 0x03, 0x00 }; mySendBuffer[2] = (byte)(0x10 | Convert.ToByte(isAckSet)); // ACK_SET isAckSet = !isAckSet; // XOR Checksum mySendBuffer[7] = (byte)(mySendBuffer[1] ^ mySendBuffer[2]); mySendBuffer[7] ^= mySendBuffer[3]; mySendBuffer[7] ^= mySendBuffer[4]; mySendBuffer[7] ^= mySendBuffer[5]; port.Write(mySendBuffer, 0, mySendBuffer.Length); // Absenden! } |
Damit sind wir auch “schon” beim Auswerten der Antwort. Diese ist vom Aufbau ganz ähnlich, allein die Daten (6 Bytes statt 3) unterscheiden sich. Damit haben wir eine Antwortlänge von 11 Byte: Das erste dieser Datenbytes enthält den Status der Maschiene. Hier wird z.B. angegeben ob gerade ein Geldschein eingezogen wird oder wieder ausgeschmissen. Wir werten jedes Bit einzeln aus:
byte statusByte = buffer[3]; if ((byte) (statusByte & 0x1) == 1) currentStatus += "IDLE..."; if ((byte)(statusByte & 0x2) == 0x2) currentStatus += "ACCEPTING"; if ((byte) (statusByte & 0x4) == 0x4) { // Wir müssen das Einziehen auswerten. Endweder Einziehn oder Auswerfen currentStatus += "ESCROWED"; isEscorwed = true; } else { isEscorwed = false; } if ((byte)(statusByte & 0x8) == 0x8) currentStatus += "STACKING"; if ((byte)(statusByte & 0x10) == 0x10) currentStatus += "STACKED"; if ((byte)(statusByte & 0x20) == 0x20) currentStatus += "RETURNING"; if ((byte)(statusByte & 0x40) == 0x40) currentStatus += "RETURNED"; |
Wenn isEscorwed gesetzt wird, so ist es an uns zu handeln und endweder ein “Stack” oder ein “Return” Befehl zu senden. Diagnostik-Flags und das Auslesen der Firmware-Version erspare ich euch
Fehlt nur noch das Auswerten der Scheine: Dieses sendet uns die Maschiene im dritten Daten-Byte in den Bits 3-5. Dass wir nur diese Werte bekommen müssen wir also (BYTE3 AND 111000) machen und im Anschluss diese Bits wiederrum auswerten. Fertig ist unser Geldzähler
Hätte nicht gedacht das solch ein Projekt richtig Spass machen kann. Hab die letzen 5 Minuten damit verbracht meine Ersparnisse durch den Automat “zählen” zu lassen und bin richtig glücklich wie das Funktioniert. Tolles Spielzeug!
Greez
Sehr geil, bitte mehr!
Ambitioniertes Projekt, aber wird wohl nur als Spielzeug enden, da solche Automaten ganz anderen Kriterien genügen müssen, als ihr implementieren könntet.
Dennoch lese ich 0 zum Thema Python