In [1]:
import sys, os, time
import usb.core
import usb.util
from: http://www.digitalmihailo.com/usb-programming-with-python-on-linux-pyusb-version/
In [2]:
#Throw this into a module! import fl593b as ?
# DEVICE CONSTANTS
VENDOR_ID = 0x1a45
PRODUCT_ID = 0x2001
CONFIGURATION = 1
EP_ADDR_OUT = 0x01
EP_PACK_OUT = 20
EP_ADDR_IN = 0x82
EP_PACK_IN = 21
TIMEOUT = 100 # default timeout of read/write is 1000 ms
# WEISUB protocol constants
# end codes
ERR_OK = 0x00 # no error
ERR_DEVTYPE = 0x01 # device type field incorrect
ERR_CHANNEL = 0x02 # channel number out of range
ERR_OPTYPE = 0x03 # optype not supported
ERR_NITIMPL = 0x04 # opcode not implemented
ERR_PENDING = 0x05 # command received, but not completed [ignore data]
ERR_BUSY = 0x06 # device currently busy, has not performed requested op
ERR_DATA = 0x07 # data field content invalid for opcode
ERR_SAFETY = 0x08 # requested op not within safety specs of config
ERR_CALMODE = 0x09 # requested op only available in calibration mode!
# Channels
NUM_CHAN = 2 # (0: device, 1: channel 1, 2: channel 2)
# OpTypes
TYPE_READ = 0x01 # return OpCode quantity to host
TYPE_WRITE = 0x02 # write OpCode quantity to device
TYPE_MIN = 0x03 # return minimum of OpCode quantity to host
TYPE_MAX = 0x04 # return maximum of OpCode quantity to host
# General OpCodes
# r = read, w = write
CMD_MODEL = 0x00 # r
CMD_SERIAL = 0x01 # rw
CMD_FWVER = 0x02 # r
CMD_DEVTYPE = 0x03 # r
CMD_CHANCT = 0x04 # r
CMD_IDENTIFY = 0x05 # rw
CMD_SAVE = 0x0C # w
CMD_RECALL = 0x0D # w
CMD_PASSWD = 0x0E # rw
CMD_REVERT = 0x0F # w
# Specific OpCodes (codes > 0x10 device specific)
# r = read, w = write, mm = min/max
CMD_ALARM = 0x10 # r, Alarm flags
CMD_SETPOINT = 0x11 # rwm, Setpoint, units depend on device MODE!
CMD_LIMIT = 0x12 # rwm, current limit (Ampere, applies for CC, CP and analog modulation!)
CMD_MODE = 0x13 # rw, feedback mode
CMD_TRACK = 0x13 # rw, tracking configuration <- ??? what is this?
CMD_IMON = 0x15 # current monitor
CMD_PMON = 0x16 # power monitor
CMD_ENABLE = 0x17 # rw, output enable. Additionally requires the output enable switch
# on the board to be set to EN, and the NT pin to be pulled LOW
CMD_RPD = 0x19 # rwm, kOhm, photodiode feedback resistor for specified channel
CMD_CAL_ISCALE = 0xE2 # rw, current monitor calibration scaling value for selected channel
# ALARM FLAGS
ALARM_FLAG_DICT = {0: 'output', # output status. 0: off, 1: on; equals XEN*LEN*REN
1: 'XEN', # external enable flag, 0: J102 floating or HIGH, 1: LOW
2: 'LEN', # local enable flag, 0: S100 enabled, 0: disabled
3: 'REN', # remote enable flag, enable command state
4: 'MODE1', # feedback more channel 1, 0: CC, 1: CP
5: 'MODE2', # feedback more channel 1, 0: CC, 1: CP
6: 'PARA', # parallel mode, state of track command, 0: independently
7: 'IDENT', # status identify flag, 0: inactive, 1: active
8: 'WRITE', # write to non-volatile memory in progress following SAVE
9: 'CALMODE'} # 1: device is in calibration mode, entered with PASSWD,
# and left with REVERT
# DATA (data ignored for types read, min and max)
LEN_DATA = 16 # length data field in bytes
FLAG_OFF = 0x30 # ASCII 48, '0'
FLAG_ON = 0x31 # ASCII 49, '1'
In [3]:
def attach_fl593(configuration=1):
"""bConfigurationValue: 1"""
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
if dev is None:
raise ValueError('Device not found')
# Not necessary, as this is not an HID?
try:
dev.detach_kernel_driver(0)
except: # this usually mean that kernel driver has already been dettached
pass
dev.set_configuration(configuration)
show_descriptors(dev)
return dev
In [4]:
def show_descriptors(device):
"""Output all available configurations.
"""
for cfg in device:
sys.stdout.write(str(cfg.bConfigurationValue) + '\n')
In [5]:
def pack_request(devtype=0x0, channel=0x0, optype=TYPE_READ, \
opcode=CMD_MODEL, data=[0x0]*LEN_DATA,packlen=EP_PACK_OUT):
"""Pack bytes given as arguments into a string of length 'packlen' which
may vary depending on the targeted endpoint.
devtype can be ignored (there is only one device on the FL593B)
"""
packet = [devtype, channel, optype, opcode]
packet.extend(data)
# add padding if data not covering full length
if len(packet) < packlen:
packet.extend([0x00]*(packlen-len(packet)))
return ''.join([chr(c) for c in packet])
In [6]:
def log_error(functionName, ret):
"""Log error to standard error output
"""
sys.stderr.write(functionName + (" failed with return code %d\n" % ret))
In [7]:
def show_command(bytes):
"""Logs command onto standard output.
"""
sys.stdout.write('DevType: %d\nChannel: %d\nOpType: %d\nopCode: %d\nData: %s\n'%\
(ord(bytes[0]), ord(bytes[1]), ord(bytes[2]), ord(bytes[3]), bytes[4:]))
In [8]:
def show_response(bytes):
"""Logs response onto standard output.
"""
sys.stdout.write('DevType: %d\nChannel: %d\nOpType: %d\nopCode: %d\nEndCode: %d\nData: %s\n'%\
(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5:].tostring()))
In [9]:
def show_alarms(bytes):
sys.stdout.write('\nAlarm flag states: \n')
for i in range(10):
sys.stdout.write('%s: %s\n' % (ALARM_FLAG_DICT[i],
'ON' if bytes[i] == FLAG_ON else 'OFF'))
In [10]:
def roundtrip(cmd_packet):
# Write command packet
try:
bw = ep_out.write(cmd_packet)
print "\n-> OUT %d Bytes" % bw
show_command(cmd_packet)
except usb.USBError as error:
raise error
# Read back result
try:
resp_packet = ep_in.read(EP_PACK_IN, 100)
print "<- IN %d Bytes\n------------" % len(resp_packet)
show_response(resp_packet)
except usb.USBError as error:
raise error
return resp_packet
In [11]:
# USB device
dev = attach_fl593()
# configuration
cfg = dev.get_active_configuration()
# interface
intf = cfg[(0,0)]
# endpoint
ep_out, ep_in = usb.util.find_descriptor(intf, find_all=True);
In [12]:
# predefine useful packets for testing...
read_model = pack_request(optype=TYPE_READ, opcode=CMD_MODEL)
read_fwver = pack_request(optype=TYPE_READ, opcode=CMD_FWVER)
read_chanct = pack_request(optype=TYPE_READ, opcode=CMD_CHANCT)
read_devtype = pack_request(optype=TYPE_READ, opcode=CMD_DEVTYPE)
read_serial = pack_request(optype=TYPE_READ, opcode=CMD_SERIAL)
read_enable = pack_request(optype=TYPE_READ, opcode=CMD_ENABLE)
read_alarm = pack_request(optype=TYPE_READ, opcode=CMD_ALARM)
read_imon = {1: pack_request(optype=TYPE_READ, opcode=CMD_IMON, channel=1),
2: pack_request(optype=TYPE_READ, opcode=CMD_IMON, channel=2)}
read_imax = {1: pack_request(optype=TYPE_MAX, opcode=CMD_IMON, channel=1),
2: pack_request(optype=TYPE_MAX, opcode=CMD_IMON, channel=2)}
read_imin = {1: pack_request(optype=TYPE_MIN, opcode=CMD_IMON, channel=1),
2: pack_request(optype=TYPE_MIN, opcode=CMD_IMON, channel=2)}
# example:
roundtrip(read_model)
roundtrip(read_imon[2])
Out[12]:
In [19]:
write_enable = pack_request(optype=TYPE_WRITE, opcode=CMD_ENABLE, data=[FLAG_ON])
In [20]:
roundtrip(read_model)
Out[20]:
In [21]:
roundtrip(read_enable)
roundtrip(write_enable);
In [22]:
show_alarms(roundtrip(read_alarm)[5:])
In [23]:
del dev
In [ ]: