1- import pywinusb . hid as hid
1+ import hid # hidapi
22import struct
33import time
44
55CMDTYPE_WRITE = 0x00
66CMDTYPE_READ = 0x01
77CMDTYPE_WRITEADR = 0x03
88CMDTYPE_READADR = 0x04
9+ CMDTYPE_ACK = 0x0A
10+ CMDTYPE_ERR = 0x07
911
10- # Pywinusb version
12+ # HIDAPI version
1113
12- class OpenFFBoard ():
14+ class OpenFFBoard_base ():
1315 @staticmethod
1416 def findDevices (vid = 0x1209 ,pid = 0xFFB0 ):
15- return hid .HidDeviceFilter (vendor_id = vid ,product_id = pid ).get_devices ()
17+ devices = []
18+ for dev in hid .enumerate ():
19+ if (dev ["vendor_id" ] == vid and dev ["product_id" ] == pid ):
20+ devices .append (dev )
21+
22+ return devices
1623
17- def __init__ (self ,device : hid .HidDevice ):
18- self .device = device
19- self .callback = self .printData
20- self .device .set_raw_data_handler (self .readDataCallback )
24+ def __init__ (self ,devdict : dict ):
25+ self .devdict = devdict
26+ self .device = hid .device ()
2127
22- def printData (cmdtype ,cls ,instance ,cmd ,val ,addr ):
23- print (f"Type: { cmdtype } , Class: { cls } .{ instance } : cmd: { cmd } , val: { val } , addr: { addr } " )
28+ def printData (cmdtype ,cls ,inst ,cmd ,val ,addr ):
29+ """Helper callback to print data nicely"""
30+ print (f"Type: { cmdtype } , Class: { cls } .{ inst } : cmd: { cmd } , val: { val } , addr: { addr } " )
2431
2532 def open (self ):
26- self .device .open ()
33+ """Opens the device"""
34+ self .device .open (self .devdict ["vendor_id" ],self .devdict ["product_id" ])
35+
2736
2837 def close (self ):
2938 self .device .close ()
3039
31- def registerReadCallback (self ,callback ):
32- self .callback = callback
40+
41+ def make_command (self ,cmdtype ,cls ,inst ,cmd ,data = 0 ,adr = 0 ):
42+ """Generates a command packet"""
43+ buffer = bytearray ()
44+ buffer += bytearray (struct .pack ('B' ,0xA1 )) # HIDCMD
45+ buffer += bytearray (struct .pack ('B' ,cmdtype )) # type. (0 = write, 1 = read)
46+ buffer += bytearray (struct .pack ('<H' ,cls ))
47+ buffer += bytearray (struct .pack ('B' ,inst ))
48+ buffer += bytearray (struct .pack ('<L' ,cmd ))
49+ buffer += bytearray (struct .pack ('<q' ,data ))
50+ buffer += bytearray (struct .pack ('<q' ,adr if adr else 0 ))
51+ return buffer
52+
53+ def parse_command (self ,data ):
54+ """Returns a parsed packet as a dict
55+ Entries: "cmdtype":cmdtype,"cls":cls,"inst":instance,"cmd":cmd,"val":val,"addr":addr
56+ """
57+ cmdtype = int (data [1 ])
58+ cls = int (struct .unpack ('<H' , bytes (data [2 :4 ]))[0 ])
59+ instance = int (data [4 ])
60+ cmd = int (struct .unpack ('<L' , bytes (data [5 :9 ]))[0 ])
61+ val = int (struct .unpack ('<q' , bytes (data [9 :17 ]))[0 ])
62+ addr = int (struct .unpack ('<q' , bytes (data [17 :25 ]))[0 ])
63+ return {"cmdtype" :cmdtype ,"cls" :cls ,"inst" :instance ,"cmd" :cmd ,"val" :val ,"addr" :addr }
3364
3465
35- # Callback in format callback(cmdtype,cls,instance,cmd,val,addr)
36- def readDataCallback (self ,data ):
37- if (data [0 ] == 0xA1 ):
38- self .done = True
39- cmdtype = int (data [1 ])
40- cls = int (struct .unpack ('<H' , bytes (data [2 :4 ]))[0 ])
41- instance = int (data [4 ])
42- cmd = int (struct .unpack ('<L' , bytes (data [5 :9 ]))[0 ])
43- val = int (struct .unpack ('<Q' , bytes (data [9 :17 ]))[0 ])
44- addr = int (struct .unpack ('<Q' , bytes (data [17 :25 ]))[0 ])
45- self .callback (cmdtype ,cls ,instance ,cmd ,val ,addr )
66+ class OpenFFBoard (OpenFFBoard_base ):
67+ def __init__ (self ,devdict ):
68+ """devdict is one entry of hid api enumerate list"""
69+ OpenFFBoard_base .__init__ (self ,devdict )
70+ self .callback = None
4671
47- def writeData (self ,cls ,inst ,cmd ,data ,adr = None ,wait = True ):
48- self .sendCommand (CMDTYPE_WRITE if adr == None else CMDTYPE_WRITEADR ,cls = cls ,inst = inst ,cmd = cmd ,data = data ,adr = adr ,wait = wait )
72+ def writeData (self ,cls ,inst ,cmd ,data ,adr = None ,timeout = 100000 ):
73+ """Sends data to the FFBoard. Returns True on success"""
74+ reply = self .sendCommand (CMDTYPE_WRITE if adr is None else CMDTYPE_WRITEADR ,cls = cls ,inst = inst ,cmd = cmd ,data = data ,adr = adr ,timeout = timeout )
75+ return reply ["cmdtype" ] != CMDTYPE_ERR
4976
50- def readData (self ,cls ,inst ,cmd ,adr = None ,wait = True ):
51- self .sendCommand (CMDTYPE_READ if adr == None else CMDTYPE_READADR ,cls ,inst ,cmd ,0 ,adr ,wait = wait )
77+ def registerReadCallback (self ,callback ):
78+ """Register a callback to also call this function on every received reply"""
79+ self .callback = callback
80+
81+ def readData (self ,cls ,inst ,cmd ,adr = None ,timeout = 100000 ):
82+ """Returns a value from the FFBoard.
83+ Returns int for single value replies or a tuple for cmd and addr replies"""
84+ reply = self .sendCommand (CMDTYPE_READ if adr is None else CMDTYPE_READADR ,cls ,inst ,cmd ,0 ,adr ,timeout = timeout )
85+ if reply :
86+ if reply ["cmdtype" ] == CMDTYPE_READ :
87+ return reply ["val" ]
88+ elif reply ["cmdtype" ] == CMDTYPE_READADR :
89+ return reply ["val" ],reply ["addr" ]
90+
91+ def sendCommand (self ,cmdtype ,cls ,inst ,cmd ,data = 0 ,adr = 0 ,timeout = 100000 ):
92+ self .device .set_nonblocking (False )
93+ buffer = self .make_command (cmdtype ,cls ,inst ,cmd ,data ,adr )
94+ self .device .write (buffer ) # Send raw packet
95+
96+ found = False
97+ while not found and timeout : # Receive all reports until correct one is found.
98+ timeout -= 1
99+ reply = self .device .read (25 )
100+ if reply [0 ] == 0xA1 :
101+ found = True
102+ repl = self .parse_command (reply )
103+ if (repl ["cls" ] == cls and repl ["inst" ] == inst and repl ["cmd" ] == cmd ):
104+ found = True
105+ if found :
106+ if self .callback :
107+ self .callback (repl ["cmdtype" ],repl ["cls" ],repl ["inst" ],repl ["cmd" ],repl ["val" ],repl ["addr" ])
108+ if repl :
109+ return repl
52110
53- def sendCommand (self ,type ,cls ,inst ,cmd ,data = 0 ,adr = 0 ,wait = True ):
54- reports = self .device .find_output_reports (0xff00 ,0x01 )
55- if (reports ):
56- self .done = False
57- report = reports [0 ]
58- report [hid .get_full_usage_id (0xff00 , 0x01 )]= type # type. (0 = write, 1 = read)
59- report [hid .get_full_usage_id (0xff00 , 0x02 )]= cls # cls (axis)
60- report [hid .get_full_usage_id (0xff00 , 0x03 )]= inst # instance
61- report [hid .get_full_usage_id (0xff00 , 0x04 )]= cmd # cmd (power)
62- report [hid .get_full_usage_id (0xff00 , 0x05 )]= data # data
63- report [hid .get_full_usage_id (0xff00 , 0x06 )]= adr if adr else 0 # adr
64- report .send ()
65- while not self .done and wait :
66- time .sleep (0.001 )
67-
0 commit comments