| 
     
	  
	View the ADU series of USB based Data Acquisition Products 
     
    Introduction
						 
						
						Communicating with USB devices via software involves a 
						few simple steps. Unlike RS232 based devices which are 
						connected to physical COM ports, USB devices are 
						assigned a logical
						
						handle 
						by operating systems when they are first plugged in. 
						This process is known as
						
						enumeration. 
						Once a USB device has been
						enumerated, it is ready for use by the 
						host computer software. For the host application 
						software to communicate with the USB device, it must 
						first obtain the handle assigned to the USB device 
						during the enumeration process. The handle can be 
						obtained using an 
						open function 
						along with some specific information about the USB 
						device. Information that can be used to obtain a handle 
						to a USB device include,
						
						serial number,
						
						product ID, 
						or 
						vendor ID.
						 
						
						Once we obtain a USB device handle, we can read and 
						write information to and from the USB device via our 
						application. Once the application has finished with all 
						communication with the USB device, the handle is closed. 
						The handle is generally closed when the application 
						terminates. 
						
						USB devices have defined
						
						interfaces 
						which relate to their functionality. For example, a USB 
						keyboard with built in LEDs may have an interface for 
						sending key presses and an interface for controlling the 
						lights on the keyboard. Interfaces as defined as a set 
						of 
						endpoints. 
						Endpoints are used as communication channels to and from 
						the device and host and can either be IN or OUT. They 
						are defined relative to the host - OUT endpoints 
						transport data to the device (write) and IN endpoints 
						transport data to the host (read). 
						
						Once we obtain a USB device handle, we must
						
						claim the interface 
						we want to use. This will allow us to read and write 
						information to and from the USB device via our 
						application. Once the application has finished with all 
						communication with the USB device, the handle is closed. 
						The handle is generally closed when the application 
						terminates. 
						
						The 
						sample source code outlines the basics of communicating 
						directly with an ADU device on Linux and Windows using 
						Python and libusb. Basics of opening a USB device 
						handle, writing and reading data, as well as closing the 
						handle of the ADU usb device is provided as an example. 
						The suggested way of working with ADU devices in Python 
						and Linux is with the HIDAPI module (see: ). For working 
						with Python and ADU devices in Windows, it's preferred 
						to use the AduHid module (see: )  
						
						All 
						source code is provided so that you may review details 
						that are not highlighted here. 
						
						NOTE: See also
				
				
				Python and HIDAPI library with ADU 
				Devices for alternate method of USB communication using 
						HIDAPI 
						 
						
						Lets have a look at the code......
						
						This example illustrates the basics of reading and 
						writing to ADU devices using the libusb library. 
						
						NOTE: When 
						running the example, it must be run with root privileges 
						in order to access the USB device. 
						
						
						libusb is 
						a library that provides low level access to USB devices 
						(https://pypi.org/project/libusb/). 
						We will need a 
						vendor ID and product 
						ID in order to open the USB device. The 
						VENDOR_ID define will always remain the same as this is 
						OnTrak's USB vendor ID, however, PRODUCT_ID must be set 
						to match the product that is connected via USB. See this 
						link for a list of OnTrack product IDs:
						
						https://www.ontrak.net/Nodll.htm. 
						
						First we'll import the libusb library. If you haven't 
						yet installed it, you may do so in the command line via
						pip install libusb 
						or by installing via requirements.txt (pip 
						install -r requirements.txt). 
						
						We'll declare OnTrak's vendor ID and the product ID for 
						the ADU device we wish to use (in our case 200 for 
						ADU200). 
						
import usb.core
import usb.backend.libusb1
VENDOR_ID = 0x0a07 
PRODUCT_ID = 200 
 
						
						
						Next, we'll open the connected USB device that matches 
						our vendor and product ID. This device will be used for 
						all of our interactions with the ADU via libusb 
						(opening, closing, reading and writing commands). 
						
device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
if device is None:
    raise ValueError('ADU Device not found. Please ensure it is connected to the tablet.')
    sys.exit(1)
usb.util.claim_interface(device, 0) 
						
						Now that we have successfully opened our device and 
						claimed an interface, we can write commands to the ADU 
						device and read the result. 
						
						Two convenience functions have been written to help 
						properly format command packets to send to the ADU 
						device, as well as to read a result from the ADU device: 
						write_to_adu() and read_from_adu(). We'll cover the 
						internals of these functions at the end of this page. 
						
bytes_written = write_to_adu(device, 'SK0') 
bytes_written = write_to_adu(device, 'RK0') 
 
						
						In order to read from the ADU device, we can send a 
						command that requests a return value (as defined in our 
						product documentation). Such a command for the ADU200 is 
						RPK0. This requests the value of relay 0, which we 
						previously set with the RK0 and SK0 commands in the 
						above code block. 
						
						We can then use read_from_adu() to read the result. 
						read_from_adu() returns the data read string format on 
						success, and None on failure. A timeout is supplied for 
						the maximum amount of time that the host (computer) will 
						wait for data from the read request. 
						
bytes_written = write_to_adu(device, 'RPA') 
data = read_from_adu(device, 200) 
if data != None:
    print("Received string: {}".format(data))
    print("Received data as int: {}".format(int(data))) 
 
						
						When we are finished with the device, we should release 
						any interfaces we have claimed (in our case interface 0) 
						and then close the device. This is gennerally done when 
						the application terminates.
usb.util.release_interface(device, 0)
device.close()
 
						
						Further Details
						 
						
						If you're interested in the internals of write_to_adu() 
						and read_from_adu() as well as how to structure a 
						command packet, the details are below. 
						
						All ADU commands have their first byte set to 0x01 and 
						the following bytes contain the ASCII representation of 
						the command. The ADU command packet format is described 
						here:
						
						https://www.ontrak.net/Nodll.htm. As described in 
						the link, the remaining bytes in the command buffer must 
						be null padded (0x00). 
						
						We'll use device.write() to write our command to the 
						device. 
						
def write_to_adu(dev, msg_str):
    print('Writing command: {}'.format(msg_str))
    
    
    
    
    
    
    byte_str = chr(0x01) + msg_str + chr(0) * max(7 - len(msg_str), 0)
    num_bytes_written = 0
	try:
		
        num_bytes_written = dev.write(0x01, byte_str)
    except usb.core.USBError as e:
        print (e.args)
    return num_bytes_written
 
						
						If the write is successful, we should now have a result 
						to read from the command we previously sent. We can use 
						read_from_adu() to read the value. The arguments are the 
						USB device and a timeout. device.read() should return 
						the data read from the device. 
						
						If reading from the device was successful, we will need 
						to extract the data we are interested in. The first byte 
						of the data returned is 0x01 and is followed by an ASCII 
						representation of the number. The remainder of the bytes 
						are padded with 0x00 (NULL) values. We can construct a 
						string from the second byte to the end and strip out the 
						null '\x00' characters.  
						
def read_from_adu(dev, timeout):
    try:
		
        data = dev.read(0x81, 64, timeout)
    except usb.core.USBError as e:
        print ("Error reading response: {}".format(e.args))
        return None
    byte_str = ''.join(chr(n) for n in data[1:]) 
    result_str = byte_str.split('\x00',1)[0] 
    if len(result_str) == 0:
        return None
    return result_str 
						 
						
						 
						
						 
						 
    					 |