Bus Pirate

Bus Pirate is an incredible piece of hardware for digital electronics tinkerers. It was designed as an interactive sniffer/communication bridge, but has evolved over time to become a Swiss Army Knife for hardware/firmware developers. Besides the original text-based command prompt interface, the newer versions of Bus Pirate firmware have a Binary Mode interface that makes it simpler to program. pyBusPirateLite is a Python library that communicates with the Bus Pirate and allows you to program applications in Python that use the Bus Pirate. To my knowledge, this is the first such tutorial.

Step 1 – Add Firmware Support for Binary Mode (for BP Firmware < 2.3)

I first tried Binary Mode (binmode) 6 months ago, and I just couldn’t get any responses back after I set the device to SPI mode. So, I gave up on binmode and wrote my C# application to use the text-based interactive console (I also have a Python script that uses the interactive prompt, so if anyone is interested in that code, please leave me a comment). But when I tried binmode on firmware 5.8 a few days ago, I did get the expected responses in SPI mode. So, I’d suggest programming the latest firmware on to¬† your Bus Pirate. Programming firmware v4.0 onwards requires a new bootloader to be programmed first, so follow the tutorials listed below, as needed.

  1. Upgrade bootloader
  2. Program latest firmware

Step 2 – Download pyBusPirateLite source code

pyBusPirateLite is available from 2 sources – the Bus Pirate Google Code repository and audiohacked’s Github repository. I believe the latter is the latest, but I’ve never used Github before, so I went with the more familiar SVN interface at Google Code. The URL for the Bus Pirate SVN repository can be copied from Source->Checkout page of the-bus-pirate Google Code project. In TortoiseSVN (Windows), you can right click on a folder and select TortoiseSVN->Repo-browser and paste in the SVN URL to see the repository. I checked out the whole ’scripts’ directory, but you could selectively get just the pyBusPirateLite directory as well.
BusPirateSVNRepo

Step 3 – Changes required to run pyBusPirateLite in Windows

pyBusPirateLite has been written for Linux, and it doesn’t appear that anyone else has tried running it in Windows. When you try to run the spi_test.py script, you will get a cryptic error message that says “select.error: (10093, 'Either the application has not called WSAStartup, or WSAStartup failed')“. What is happening is that some parts of the pyBusPirateLite code are using a ’select’ system call to time out a read operation. This works fine for Posix based operating systems, but Windows only supports the ’select’ system call on Winsock network sockets.
Fortunately, pySerial (the underlying serial communications library used by pyBusPirateLite) already handles timeouts, so the changes to the Python files outlined below will get you up and running on Windows.

  • BitBang.py – Locate the ‘timeout‘ function in the class BBIO. The code looks like this:
        def timeout(self, timeout=0.1):
            select.select([], [], [], timeout)

    Change it to the following:

        def timeout(self, timeout=0.1):
            if sys.platform != 'win32':
                select.select([], [], [], timeout)
            else:
                self.port.timeout = timeout
            # endif

    Also, you will need to add import sys at the top of the file. After that, replace every occurence of self.timeout(0.1) with:

            if sys.platform != 'win32':
                self.timeout(0.1)
            # endif

    Of course, if you don’t intend to use the code on Linux at all, you can simply delete all references to self.timeout(0.1). In fact, I’m not certain if it is needed in Linux either given pySerial is already handling timeouts. If anyone has comments on that, please contribute.

  • SPI.py – The same changes as above for self.timeout(0.1)

Step 4 – Initialization sequence

To initialize the Bus Pirate using pyBusPirateLite, you need to make at least 2 function calls. The first would be a call to initialize the serial port, but you need to use the class initializer for whatever communication mode you will use. I am using SPI, so the first call looks like this:

        # Open Buspirate Port
        if not self.spiPort:
            self.spiPort = SPI(comPortName, 115200)
        else:
            logging.debug("Port already open")
        # endif

The port object will be stored in self.spiPort and is used later for all operations on the Bus Pirate. I haven’t looked into what exceptions could be thrown from that function call, but if you want to make your code robust, you will need to catch the appropriate exceptions.
The next step is to switch the Bus Pirate into Binary Mode. This is done below:

        # Enter binmode
        if not self.spiPort.BBmode():
            logging.error("Couldn't start Bitbang Mode")
            return False
        # endif

And since I’m using the SPI mode, I need to make one more call:

        # Enter SPI mode
        if not self.spiPort.enter_SPI():
            logging.error("Couldn't start SPI Mode")
            return False
        # endif

If your code has successfully completed those steps, you are ready to configure your SPI communication parameters.

Step 5 – Running SPI : Configuration

There are 5 SPI parameters you can configure. Details of each are available at the BSMSPI Wiki Page.

  • Speed : Configured using set_speed(). Valid arguments are SPISpeed._30KHz, SPISpeed._125KHz, SPISpeed._250KHz, SPISpeed._1MHz, SPISpeed._2MHz, SPISpeed._2_6MHz, SPISpeed._4MHz, SPISpeed._8MHz
  • Output Type : Configured using cfg_spi(). Supply the argument SPICfg.OUT_TYPE to select 3.3V mode, otherwise it stays in HiZ mode
  • Clock Idle State : Configured using cfg_spi(). Supply the argument SPICfg.IDLE to select ‘clock idle phase high’, otherwise it’s ‘clock idle phase low’
  • Clock Edge : Configured using cfg_spi(). Supply the argument SPICfg.CLK_EDGE to select ‘clock edge active to idle’, otherwise it’s ‘clock edge idle to active’
  • Sample Time : Configured using cfg_spi(). Supply the argument SPICfg.SAMPLE to select ’sample at end’, otherwise it’s ’sample in middle’

You can also configure Pull Up, Power Supply and AUX line state, but that’s secondary to SPI communications so I’ll let you explore those yourself. Look at the class PinCfg in BitBang.py to find out your options. You will need to call cfg_pins() to perform the configuration.

Step 6 – Running SPI : Writing

Now all we need to do to write data to an SPI peripheral is assert the CS pin, write the data, and release CS. Mind you, standard SPI configuration requires CS to be pulled low to assert Chip Select, and high to release Chip Select. If your peripheral uses inverted CS logic, just switch the assert and release calls in the sample below.

        self.spiPort.CS_Low()
        self.spiPort.bulk_trans(2, [0x05, 0x00]) # Position MSB = 0
        self.spiPort.CS_High()

As you can see, you write data using the bluk_trans() function call. You have to pass it 2 arguments – 1. The number of bytes in the transfer array and 2. the data to be transferred as an array. In the code above, the number of bytes is 2 and the array is [0x05, 0x00]. You can transfer at most 16 bytes in a single bulk_trans() call.

Step 7 – Running SPI : Reading

Reading from a peripheral is simply a matter of sending it the read command followed by as many zeroes as the number of bytes you want to read. Sorry, you are limited to reading a total of 16 – {size of read command} bytes in one read operation.

        self.spiPort.CS_Low()
        response = self.spiPort.bulk_trans(2, [0x81, 0x00])
        self.spiPort.CS_High()

response is a Python List type object, and in this case, it will be of length 2.

That’s all you need to know on using the Bus Pirate with pyBusPirateLite to communicate with an SPI peripheral. For a real-world example of a few scripts that do that, read my post “Controlling MAX7456 OSDisplay using Bus Pirate“.