#!/usr/bin/env python # -*- coding: utf-8 -*- # # || ____ _ __ # +------+ / __ )(_) /_______________ _____ ___ # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # # Copyright (C) 2011-2013 Bitcraze AB # # Crazyflie Nano Quadcopter Client # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. """ USB driver for the Crazyflie. """ import logging import os import usb __author__ = 'Bitcraze AB' __all__ = ['CfUsb'] logger = logging.getLogger(__name__) USB_VID = 0x0483 USB_PID = 0x5740 try: import usb.core pyusb_backend = None if os.name == 'nt': import usb.backend.libusb0 as libusb0 pyusb_backend = libusb0.get_backend() pyusb1 = True except Exception: pyusb1 = False def _find_devices(): """ Returns a list of CrazyRadio devices currently connected to the computer """ ret = [] logger.info('Looking for devices....') if pyusb1: for d in usb.core.find(idVendor=USB_VID, idProduct=USB_PID, find_all=1, backend=pyusb_backend): if d.manufacturer == 'Bitcraze AB': ret.append(d) else: busses = usb.busses() for bus in busses: for device in bus.devices: if device.idVendor == USB_VID: if device.idProduct == USB_PID: ret += [device, ] return ret class CfUsb: """ Used for communication with the Crazyradio USB dongle """ def __init__(self, device=None, devid=0): """ Create object and scan for USB dongle if no device is supplied """ self.dev = None self.handle = None self._last_write = 0 self._last_read = 0 if device is None: devices = _find_devices() try: self.dev = devices[devid] except Exception: self.dev = None if self.dev: if (pyusb1 is True): self.dev.set_configuration(1) self.handle = self.dev self.version = float( '{0:x}.{1:x}'.format(self.dev.bcdDevice >> 8, self.dev.bcdDevice & 0x0FF)) else: self.handle = self.dev.open() self.handle.setConfiguration(1) self.handle.claimInterface(0) self.version = float(self.dev.deviceVersion) def get_serial(self): # The signature for get_string has changed between versions to 1.0.0b1, # 1.0.0b2 and 1.0.0. Try the old signature first, if that fails try # the newer one. try: return usb.util.get_string(self.dev, 255, self.dev.iSerialNumber) except (usb.core.USBError, ValueError): return usb.util.get_string(self.dev, self.dev.iSerialNumber) def close(self): if (pyusb1 is False): if self.handle: self.handle.releaseInterface() else: if self.dev: usb.util.dispose_resources(self.dev) self.handle = None self.dev = None def scan(self): # TODO: Currently only supports one device if self.dev: return [('usb://0', '')] return [] def set_crtp_to_usb(self, crtp_to_usb): if crtp_to_usb: _send_vendor_setup(self.handle, 0x01, 0x01, 1, ()) else: _send_vendor_setup(self.handle, 0x01, 0x01, 0, ()) # Data transfers def send_packet(self, dataOut): """ Send a packet and receive the ack from the radio dongle The ack contains information about the packet transmition and a data payload if the ack packet contained any """ try: if (pyusb1 is False): self.handle.bulkWrite(1, dataOut, 20) else: self.handle.write(endpoint=1, data=dataOut, timeout=20) except usb.USBError: pass def receive_packet(self): dataIn = () try: if (pyusb1 is False): dataIn = self.handle.bulkRead(0x81, 64, 20) else: dataIn = self.handle.read(0x81, 64, timeout=20) except usb.USBError as e: try: if e.backend_error_code == -7 or e.backend_error_code == -116: # Normal, the read was empty pass else: raise IOError('Crazyflie disconnected') except AttributeError: # pyusb < 1.0 doesn't implement getting the underlying error # number and it seems as if it's not possible to detect # if the cable is disconnected. So this detection is not # supported, but the "normal" case will work. pass return dataIn # Private utility functions def _send_vendor_setup(handle, request, value, index, data): if pyusb1: handle.ctrl_transfer(usb.TYPE_VENDOR, request, wValue=value, wIndex=index, timeout=1000, data_or_wLength=data) else: handle.controlMsg(usb.TYPE_VENDOR, request, data, value=value, index=index, timeout=1000) def _get_vendor_setup(handle, request, value, index, length): if pyusb1: return handle.ctrl_transfer(usb.TYPE_VENDOR | 0x80, request, wValue=value, wIndex=index, timeout=1000, data_or_wLength=length) else: return handle.controlMsg(usb.TYPE_VENDOR | 0x80, request, length, value=value, index=index, timeout=1000)