ESC SV 2015 – USSSSSB: Talking USB From Python

At ESC 2015 SV I gave a talk on using USB From Python, see the talk description here. This blog post is serving as a placeholder to allow me to update links to software used during the live demo.

For SuperCon 2015, there is a Project Page with these details too. You can also ask questions on the project page.

Download Slides

There is two versions of the slides. Use the SuperCon slides, but I left a copy of the ESC ones here in case you wanted the original for some reason.

Download Slides from Hackaday SuperCon 2015 (Newer Version for SAMD21) [PDF, 10MB]

Download Slides from ESC2015 (Older version for SAMD11) [PDF, 10MB]

Tools to Install

  1. Atmel Studio 6.2
  2. WinPython-2.7
  3. libusb-win32-devel-filter (NB: No need to open the filter install wizard when done)
  4. USBView

SAMD11 Errata

For ESC I used a SAMD11 device, which needs a bit of a hack.

There is a bit of an “oopsie” in the SAMD11 devices. This bug isn’t in the official errata yet, and I’ve been told it’s limited to engineering sample devices (which were used in some of the early dev boards).

Basically the 48MHz oscillator calibration byte is wrong, and you need to manually tune this. You’ll know this problem exists as the device won’t be detected by Windows:

usbbad

The work-around isn’t super-fun. First, use the programming interface to see the starting value of the DFLL48M_COARSE_CAL fuse:

pllbad

Next, search in the source code for reference to the dfll_conf.coarse_value variable. You will find where it is being setup, and you can override the value. Basically you have to experiment a bit to find a working value:

pllhack

 Reference Code

USB Test – Slide 87

import usb.core
dev = usb.core.find(idVendor=0x03eb, idProduct=0x2402)
print dev

If you get “None”, make sure you installed the “Filter Driver” using the LibUSB tools!

Control Endpoint Read – Slide 94

import usb.core
dev = usb.core.find(idVendor=0x03eb, idProduct=0x2402)
dev.set_configuration()

data = dev.ctrl_transfer(0b10100001, 0x01, 3<<8, 0, 4)

print data

If you get a “device is not functioning” error just skip this one…

Sending Output Report

import usb.core
dev = usb.core.find(idVendor=0x03eb, idProduct=0x2402)
print dev

dev.set_configuration()

data = [ord('1'), ord('1'), 0, 0, 0, 0, 0, 0]
dev.write(0x02, data)

Receiving Input Data (Press button to see change)

import usb.core
dev = usb.core.find(idVendor=0x03eb, idProduct=0x2402)
print dev

dev.set_configuration()

for i in range(0, 10):
    while True:
        try:
            test = dev.read(0x81, 8, timeout=50)
            break
        except usb.core.USBError, e:            
            if str(e).find("timeout") >= 0:
                pass
            else:
                raise IOError("USB Error: %s"%str(e))
     
    print test

 Full GUI Example

#Public domain - simple USB GUI Example by Colin O'Flynn

from PySide.QtCore import *
from PySide.QtGui import *
import usb.core
import sys

class USBForm(QDialog):
    def __init__(self, parent=None):
        super(USBForm, self).__init__(parent)
        self.setWindowTitle("ESC 2015 Demo")

        layout = QVBoxLayout()
        self.setLayout(layout)

        self.pbConnect = QPushButton("Connect")
        self.pbConnect.clicked.connect(self.con)
        self.isConnected = False

        self.pbLED = QPushButton("LED Blinking")
        self.pbLED.setCheckable(True)
        self.pbLED.clicked.connect(self.changeLED)
        self.pbLED.setEnabled(False)

        layout.addWidget(self.pbConnect)
        layout.addWidget(self.pbLED)

        self.swStatus = QLineEdit()
        self.swStatus.setReadOnly(True)
        layout.addWidget(self.swStatus)

        self.butTimer = QTimer(self)
        self.butTimer.timeout.connect(self.pollButton)


    def con(self):
        if self.isConnected == False:
            #Do USB Connect Here
            self.dev = usb.core.find(idVendor=0x03eb, idProduct=0x2402)
            self.dev.set_configuration()

            #Sync changeLED
            self.changeLED()
            
            self.isConnected = True
            self.pbConnect.setText("Disconnect")
            self.pbLED.setEnabled(True)
            self.butTimer.start(100)
        else:
            self.isConnected = False
            self.pbConnect.setText("Connect")
            self.pbLED.setEnabled(False)
            self.butTimer.stop()

    def changeLED(self):
        if self.pbLED.isChecked():
            #Send command to make LED on
            self.dev.write(0x02, [ord('1'), ord('1'), 0, 0, 0, 0, 0, 0])

            self.pbLED.setText("LED On")            
        else:
            #Send command to make LED blink
            self.dev.write(0x02, [ord('0'), ord('1'), 0, 0, 0, 0, 0, 0])
            self.pbLED.setText("LED Blinking")

    def pollButton(self):
        try:
            data = self.dev.read(0x81, 8, timeout=50)
            if data[0]:
                self.swStatus.setText("Button Pressed")
            else:
                self.swStatus.setText("Button Released")
                
        except usb.core.USBError, e:
            if str(e).find("timeout") >= 0:
                pass
            else:
                raise IOError("USB Error: %s"%str(e))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = USBForm()
    form.show()
    sys.exit(app.exec_())

Leave a Reply

Your email address will not be published. Required fields are marked *