Beaglebone Coding 101: SPI Output

It’s been too long since my last “Beaglebone Coding 101″ article.  Perhaps many of you have graduated to Beaglebone Coding 201 by now, but I’m going to continue with the 101 level for us less gifted students.

I’ve been experimenting with SPI and I2C on my Beaglebone lately.  When I first tried using them at the beginning of the year, I found they required a level of Linux Kung Fu that was beyond me.  However, both of these protocols are now supported from “userland” applications  in both Angstrom and Ubuntu.  A Google search turns up plenty of working examples for SPI and I2C on the Beaglebone, along with some more advanced projects.  I think the water’s safe safe for the rest of us.

I’m going to begin with SPI.  Ultimately I’ll be using a graphical LCD, specifically an Adafruit ST7565-based LCD, but in this article I’m going to use a simpler character LCD backpack, also from Adafruit, that uses the popular and well-documented 74HC595 SPI chip.

Beaglebone Connected to SPI LCD
Beaglebone Connected to SPI LCD

My coding language of choice is Python.  I really wanted to do this in Bonescript instead, since I like Jason Kridner’s idea of using it as a way for Arduino coders to ease into the embedded Linux world. Unfortunately, I haven’t found the time to get the hang of coding in node.js.  Bonescript has added SPI support through a shiftOut method.  If you’re interested in learning how to use SPI with Bonescript, I’d recommend Ken Keller’s blog and Github code library.

Updating the Kernel

SPI has been supported in the Angstrom kernel for awhile now, and was added to Ubuntu’s kernel relatively recently.  A quick way to check if you have SPI support is to look in the /dev directory:

ubuntu@omap:~$ ls /dev/spi*

Note that bus 2, device 0 is the only option available to “userland” code in the current Angstrom and Ubuntu kernels.  Multiple SPI buses and devices are possible, but require that you build a custom kernel.

If you don’t see an SPI device in the /dev directory, you’ll need to upgrade your kernel.  If you are running Angstrom, either download a new image (I use the top entry on the Beaglebone demo files page), or follow the instructions in the “Enabling PWM Support in the Kernel” section of my Buttons and PWM article.

[Update Sept 11: I just noticed that Angstrom kernel 3.2.28 was automatically enabled on my Beaglebone when installed via opkg. I didn’t have to manually copy the uImage file onto the boot partition, just reboot.   Whoa!  

I’m using the 2012.08.14 image from the Beaglebone demo files page.  I’m not sure yet whether this feature is only to be found in newer Beaglebone images. If you find that the latest kernel isn’t automatically installed for you, then you’ll have to manually update it as described in the Buttons and PWM article.]

If you’re running Ubuntu, see my recent article on updating the kernel.

(If you’re running Android or another OS, sorry, dunno :-(.)

Adding an SPI Extension Module to Python

If you Google Python SPI, you’ll find various references to a C extension for Python, py-spi.  It was written back in 2009 by Volker Thoms, but as far as I can tell its still the best method for giving Python full access to SPI on Linux.

The module consists of  C code with Python bindings, spimodule.c.  It limits the amount of data being sent to 32 bytes, and the speed to 8 Mhz — both unnecessarily low limits on the Beaglebone — but for now we’ll use it as-is.  We’ll need to lift those limitations later when we use it with an SPI graphical LCD.

(If you’d rather avoid building the extension on your Beaglebone, I’ve built binaries for Angstrom and Ubuntu.  See the Python Extension Binaries section below).

Before we can install it, we need the python-dev package.

On Angstrom

opkg update
opkg install python-dev

On Ubuntu

sudo apt-get update
sudo apt-get install python-dev

To install the py-spi module, you’ll need to download a few files into any directory on your Beaglebone:


Note: if you have problems downloading these files (I downloaded them without problems a few weeks ago, but got 0-length files when I tried today), you can download my slightly customized versions instead:


If you download the original, you’ll need to make a couple of changes to the file.  (You can skip this if you download my customized version). The original looks like this

#!/usr/bin/env python
from distutils.core import setup, Extension
setup( name="spi",
 description="Python bindings for Linux SPI access through spi-dev",
 author="Volker Thoms",
 maintainer="Volker Thoms",
 ext_modules=[Extension("spi", ["spimodule.c"])])

We’ll need to change the include_dirs line to match the correct location of the Linux include files, /usr/include.  Otherwise, when you compile on Angstrom you’ll get an error “no include path in which to search for limits.h“.  Incidentally, this appears to be one of those error messages for which Google returns 1000 explanations, none of them being this one.  I hate it when Google makes me think for myself!

We’ll also remove the scripts line, since we aren’t using those files.  The should look like this:

#!/usr/bin/env python
from distutils.core import setup, Extension
setup( name="spi",
 description="Python bindings for Linux SPI access through spi-dev",
 author="Volker Thoms",
 maintainer="Volker Thoms",
 ext_modules=[Extension("spi", ["spimodule.c"])])

To compile and install the Python extension, enter the following as root:

python install

On Ubuntu, you should get the following.  (The warnings won’t be there if you use my customized version – 10 points for Gryffindor!):

 running install
 running build
 running build_ext
 building 'spi' extension
 creating build
 creating build/temp.linux-armv7l-2.7
 gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall 
  -Wstrict-prototypes -fPIC -I/usr/include -I/usr/include/python2.7
 -c spimodule.c -o build/temp.linux-armv7l-2.7/spimodule.o
 spimodule.c: In function ‘SPI_readbytes’:
 spimodule.c:156:7: warning: unused variable ‘addr’ [-Wunused-variable]
 spimodule.c: In function ‘SPI_xfer’:
 spimodule.c:204:10: warning: unused variable ‘ret’ [-Wunused-variable]
 creating build/lib.linux-armv7l-2.7
 gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,
  -Bsymbolic-functions -Wl,-z,relro 
  -o build/lib.linux-armv7l-2.7/
 running install_lib
 copying build/lib.linux-armv7l-2.7/ -> 
 running install_egg_info
 Removing /usr/local/lib/python2.7/dist-packages/spi-1.1.egg-info
 Writing /usr/local/lib/python2.7/dist-packages/spi-1.1.egg-info

An Extra Step on Angstrom

On Angstrom, you’ll run into a problem:

 python install
 running install
 running build
 running build_ext
 building 'spi' extension
 arm-angstrom-linux-gnueabi-gcc -march=armv7-a -fno-tree-vectorize
   -mthumb-interwork -mfloat-abi=softfp -mfpu=neon 
  -mtune=cortex-a8 -D__SOFTFP__ 
  -fno-strict-aliasing -O2 -pipe -g 
  -feliminate-unused-debug-types -DNDEBUG -g -O3 -Wall 
  -Wstrict-prototypes -fPIC -I/usr/include 
  -I/usr/include/python2.7 -c spimodule.c 
  -o build/temp.linux-armv7l-2.7/spimodule.o
 arm-angstrom-linux-gnueabi-gcc -march=armv7-a -fno-tree-vectorize
   -mthumb-interwork -mfloat-abi=softfp -mfpu=neon 
  -mtune=cortex-a8 -D__SOFTFP__ 
  -shared -Wl,-O1 -Wl,--hash-style=gnu -Wl,
  --as-needed build/temp.linux-armv7l-2.7/spimodule.o 
  -L/usr/lib -lpython2.7 -o build/lib.linux-armv7l-2.7/
  this linker was not configured to use sysroots
 collect2: ld returned 1 exit status
 error: command 'arm-angstrom-linux-gnueabi-gcc' failed with exit status 1

OK,  whose idea was it to use sysroots??  Anyone?

Actually, I have no idea who or what is telling to include that “sysroot=..” parm (another seemingly unGoogleable solution), so I just created stripped the sysroot part out of the compile and link commands:

arm-angstrom-linux-gnueabi-gcc -march=armv7-a -fno-tree-vectorize -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -mtune=cortex-a8 -D__SOFTFP__ -fno-strict-aliasing -O2 -pipe -g -feliminate-unused-debug-types -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -I/usr/include -I/usr/include/python2.7 -c spimodule.c -o build/temp.linux-armv7l-2.7/spimodule.o
arm-angstrom-linux-gnueabi-gcc -march=armv7-a -fno-tree-vectorize -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -mtune=cortex-a8 -D__SOFTFP__ -shared -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed build/temp.linux-armv7l-2.7/spimodule.o -L/usr/lib -lpython2.7 -o build/lib.linux-armv7l-2.7/

Having generated the object file, you can rerun python install to complete the process.

 python install

 running install
 running build
 running build_ext
 running install_lib
 copying build/lib.linux-armv7l-2.7/ -> /usr/lib/python2.7/site-packages
 running install_egg_info
 Writing /usr/lib/python2.7/site-packages/spi-1.1-py2.7.egg-info

Python Extension Binaries

If above process left you totally confused, or simply didn’t work, you can try downloading a binary of my customized version of the Python extension.

If you’re running Ubuntu on your Beaglebone:


If you’re running Angstrom:


Writing to an SPI Device  from the Python Shell

Before running some Python code to write to an SPI-attached LCD, we can test the setup using an 74HC595 chip.  (If you don’t have a 74HC595 chip, you can skip ahead to the example Python commands: they would be basically the same for any SPI-controlled device).

The 74HC595 is commonly used as a GPIO-extender: by using the 3 SPI pins on the Beaglebone, we get 8 extra GPIO pins.  This isn’t a particularly attractive tradeoff on the GPIO-rich Beaglebone, but the 74HC595 makes a good testbed for playing with SPI before moving on to more sophisticated (and finicky) devices.

The HC74595 pinout looks like this, as taken from the datasheet.  You can find a thorough and easy-to-understand explanation of this chip on the Arduino web site.  Note that the latch pin, aka the Chip Select pin, is aaka the “storage register clock input” pin here.  Don’t ask me!

An easy way to test SPI output is by hooking by LEDs (through resistors) to each of the 8 GPIO pins on the 74HC595.

The following diagram shows this kind of setup. I used an LED bar instead of individual LEDs.  If you’re lazy and/or don’t have an LED bar or 8 LEDs, you can hook up a couple of LEDs — I’d suggest Q0 and Q 1 — and still verify that everything is working OK.

The Beaglebone pins involved in SPI Bus 1 are as follows:

  • Port 9 Pin 28 – Chip Select – connected to pin 12 of the HC74595 with yellow wire
  • Port 9 Pin 29 – Data 0 (aka MISO – Master In Slave Out) – unused in this project, but could be connected to pin 9 of the HC74595
  • Port 9 Pin 30 – Data 1 (aka MOSI – Master Out Slave In) – connected to pin 14 of HC74595 with green wire
  • Port 9 Pin 31 – Clock – connected to pin 11 of the HC74595 with white wire

The “master”, in this case, is the Beaglebone.  Since at this point we are only writing, not reading, we’ll leave Pin 29 unconnected.  This gives us the liberty of connecting the 74HC595’s voltage pin, Pin 16 (to the right of the chip’s semicircular imprint), to 5V if we want to make those LEDs sweat.  More usefully, it also allows us to safely connect the Beaglebone to SPI write-only devices that run at 5V, as we will with the LCD.  Do not connect Pin 29 to an SPI device running at 5V!

Note that the spidev2.0 device is identified as SPI1 in the System Reference Manual.  Note also that the pinmuxing for the SPI1 pins all default to SPI mode (mode 3).

Now that we have things hooked up, let’s fire up Python and send a command to the 74HC595.   Note that this needs to be done as root, so run sudo python if you are on Ubuntu:

root@beaglebone:# python
 Python 2.7.2 (default, Apr 27 2012, 09:45:45)
 [GCC 4.5.4 20120305 (prerelease)] on linux2
 Type "help", "copyright", "credits" or "license" for more information.

 >>> from spi import SPI
 >>> leds = SPI(2, 0)
 >>> leds.writebytes([0x00])

Even if you don’t know Python, you probably figured out that this should turn all of the LEDs off (by setting all of the 74HC595 pin’s low).  To turn the LEDs on:

>>> leds.writebytes([0xFF])

If that’s not working for you, check the wiring: aside from the GPIO pins, there are a bunch of pins on the 74HC595 that need to be connected to ground or to voltage, as shown in the figure above.

Let’s see which pins correspond to which bits:

>>> leds.writebytes([0x80])

This will set high the lowest GPIO pin on the 74HC595, Q0, which is connected to the leftmost LED in the circuit diagram and photograph.  Similarly, if we had written 0xC0, it would set Q0 and Q1 to high.  So, the data is sent LSB first — the opposite of what you might expect (but the same as the Arduino, by the way).  We’ll have to keep this in mind when coding.

Beaglebone connected to 74HC595. Yellow is SPI CS, green is Data, white is Clock.
Beaglebone connected to 74HC595. Yellow is SPI CS, green is Data, white is Clock.

Although we won’t need it when dealing with the 74HC595, sending multiple bytes of data is pretty simple from Python:

>>> leds.writebytes([0x01, 0x02, 0x03, 0x04])

Technically, that command turns on each of pins Q0 to Q3 in  turn, though of course it occurs too quickly to see any but the last one.

There are a bunch of other commands that are supported by the spi Python extension:

>> dir(SPI)
 ['__class__', '__delattr__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', 'bpw', 'close', 
'cshigh', 'loop', 'lsbfirst', 'mode', 'msh', 'open', 
'readbytes', 'threewire', 'writebytes', 'xfer', 'xfer2']

I haven’t experimented with most of these, but 2 that are worth pointing out are:

  • writeBytes – writes a list of byte data, as shown in the example above.  The original spimodule.c had a hard-coded limit of 32 bytes, which I increased to 1024.  I don’t know what the practical limit is: 1024 is enough for my current uses.
  • msh – Sets the clock frequency.  For example, the following displays the clock rate being used in our connection to the 74HC595, then changes it to 1 Mhz.
>>> leds.msh
 >>> leds.msh = 1000000

The default of 8 Mhz is actually the maximum clock rate allowed by the original spimodule.c code, though I removed that in the version I customized.

Having said all that, changing the clock rate is completely unnecessary for this project, either with the LEDs or the LCD. The 74HC595 is very flexible about the frequency (it supports up to 100 Mhz, and anything from 1 Mhz up worked fine when I tried it), and neither the LEDs nor  a character LCD require much bandwidth.  The speed setting will be more useful when dealing with graphical LCDs.

Writing to an I2C/SPI LCD Backpack from Python

Adafruit’s I2C/SPI LCD Backpack is a handy accessory for testing SPI and I2C.   It uses 2 common GPIO extenders ( the 74HC595 and, for I2C, the MCP20008) to connect to a standard character LCD, allowing you to test SPI and I2C without much wiring.  It’s also the cheapest LCD connector that I’ve seen: $10. I’ll be using it again in a future article about using I2C with the Beaglebone.

Assembly instructions for the backpack are at  Note that, by default, it uses I2C, so you’ll need to put a blob of solder on the SPI bridge for use with the code below.

The 5 screw terminals on the backpack should be connected to the Beaglebone as follows:

  • LAT – Port 9 Pin 28 (i.e. Chip Select)
  • DAT – Port 9 Pin 30
  • CLK – Port 9 Pin 31
  • 5V – Port 9 Pin 5 or 6
  • GND – Port 9 Pin 1 or 2

Beaglebone connected to Adafruit I2C/SPI Backpack.  White is SPI CS (Latch), yellow is data, green is Clock.
Beaglebone connected to Adafruit I2C/SPI Backpack. White is SPI CS (Latch), yellow is data, green is Clock.

I’ve written some Python class modules to make it easy to write to a backpack-connected LCD:


You will also need the Python extension, either installed as a Python library or as a file in the same directory as the other files. If you don’t have it, go back to the section Adding an SPI Extension Module to Python section above for instructions.

Once you have all the files, run the following as root to write some text to the LCD

root@beaglebone:# python
Python 2.7.3 (default, Aug 1 2012, 08:09:58)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from spilcd import spilcd
>>> lcd = spilcd(2, 0)
>>> lcd.begin(16, 2)
>>> lcd.setBacklight(True)
>>> lcd.home()
>>> lcd.Print("Hello world")
>>> lcd.setCursor(0, 1)
>>> lcd.Print("Hello line 2")

(You should, of course, use begin(20, 4) for a 4-row, 20-character LCD.)

As you can see, the syntax is based on the Arduino LiquidCrystal library.  I’ve implemented many (but not all) of the same methods: enter help(spilcd) from Python to get the list.

I’ve also written a plant light controller application which makes use of the spilcd class, along with the Arduino-like bone class for accessing the GPIO .  It uses the LCD to display the current date and time, the sunrise and sunset times, and the plant light settings.  While it doesn’t break any new ground in terms of Beaglebone functionality, you might want to refer to it for sample code on how to use my Python classes for SPI and GPIO access.

You can download the plant light controller code from here.

Wrapping Up

This article covers one of the simpler uses of SPI, but this protocol opens the door to a lot of inexpensive LCDs.  I’m currently working on a Beaglebone project that uses an Adafruit ST7565-based 128×64 LCD, and Adafruit has an assortment of other SPI-based LCDs for sale.  SPI-based input devices are less common: I2C is the standard protocol used by sensors.  I’ll look at I2C in upcoming “Beaglebone Coding 101″ article.

This entry was posted in Electronics, Programming and tagged , , , . Bookmark the permalink.

16 Responses to Beaglebone Coding 101: SPI Output

  1. Nice work Dan, I’m a big fan of Beaglebone and Embedded Linux – i’d love to see this talk to nimbits.

    – Ben

    • dwatts says:

      Hi Ben,

      Yes, good idea. The Beaglebone is a great match for Nimbits. It’s easy to connect the two.

      The Python code that I’ve used to post data to Nimbits in past articles ( and runs on the Beaglebone without any changes. It makes for a very stable data reporting platform.

      When I write an article on using I2C sensors with the Beaglebone, I’ll definitely include the ability to post that data to Nimbits.

      Someday I’d like to do a project where the Beaglebone reports Nimbits data rather than just posting data, displaying graphs on a graphical LCD. It would be a convenient way of monitoring data in the home. The Nimbits side of that project should be easy: it’s just a matter of writing Python code to handle the graphing.


  2. cyril says:

    Thank you for the how to on SPI. Do you plan on writing on how to connect a 4×4 matrix keypad using I2C any time soon ?

    Thanks again for your Beaglebone Coding 101

    • dwatts says:


      Did you have a particular model of I2C keypad in mind?

      I have a generic matrix keypad (from an electronics surplus store) that could be connected to an I2C IO expander like the MCP23008. I could write about that if it would help.


      • Cyril says:

        Thanks Dan,
        Sorry about my late reply. I currently have a 4×4 grayhill keypad.
        Connecting it to an I/O expander will help. I appreciate if you can do and how to on it.

        Thanks again.


  3. Pingback: SPI Output with a Beaglebone « adafruit industries blog

  4. PeterC says:

    Thanks for the guide, I can write to the port, but I need to set the bits per word to 9, however if I set it to anything other than 8 (the default) it just hangs the system.

    Can you test this and see if you get the same results:

    x = SPI(0,0)
    x.bpw = 8

    x.bpw = 9

    • dwatts says:


      Wow, yeah, that crashes it alright. Not just the Python session — the whole Beaglebone. Required a reset to get things going again. (I actually used SPI(2, 0), not SPI(0, 0) — you probably did too.)

      Sending 0x1FF must overflow a buffer, since that function expects a byte array. It crashes the Beaglebone even in its default setting of 8 bpw. Coding it as writebytes[0x01, 0xFF) is OK.

      If you set bpw to 9, then send 2 full bytes (e.g. writebytes[0x01, 0xFF]), it won’t crash, but it appears to behave exactly the same as when bpw to is set to 8, at least with a 74HC595. (Sending just 1 byte when bpw = 9 does crash, so at least we know that bpw has some effect).

      I have to admit I’m out of my depth here, since I don’t know when a bpw setting other than 8 is needed, and what effect it should have.

      It appears that spimodule.c just sends the bpw setting over to the device.

      ioctl(self->fd, SPI_IOC_WR_BITS_PER_WORD, &bits)

      Other than that, it doesn’t use the bpw setting.

      I’ve only used the default setting of 8 bpw until now. Do you have a device that requires a bpw setting of 9?


      • j105rob says:

        I have a LCD that requires 9 bit. Will passing 2 bytes in the array just whack off the other 7? I also need MSBFirst. Thoughts?


        • dwatts says:


          Unfortunately I don’t have a 9-bit SPI device to test with, so I’m not sure whether the SPI C module correctly supports them.

          The C module does have a method for specifying MSB, x.lsbfirst = 0

          For both the LSB/MSB setting and the BPW setting, the C module just turns around and passes the setting to the SPI device. The C code won’t alter the bits being passed to the device based on these settings (i.e. it won’t truncate everything after the 9th bit, or reverse the sequence of the bits). So, I would assume that using MSB first with 9 bits should require that you send 2 bytes with the 1st 9 bits set correctly, and the last 7 bits are unimportant.

          What’s the model of the LCD you’re using, by the way?



  5. Cyril says:

    Hello Dan,

    i had expressed intrest in 4×4 matrix keypad over I2C, would you be able to do an article on , how to ?


  6. Steve says:

    Thanks a ton! I’m new to Python and couldn’t get the code to compile on Angstrom for some reason. The binary you provided worked though!

  7. Pingback: Beaglebone Coding 101: I2C | GigaMegaBlog

  8. Bradley Petersen (URI) says:

    I want to spare anyone who has a problem with clock frequency:

    I had a problem where I would try a frequency such as 2MHz or 8MHz and I would measure an incorrect frequency such as 1.5MHz or 6MHz, respectfully.

    This is how I solve it:
    I noticed default SPI frequency is 12MHz. (I changed some of code around in spimode.c so I don’t know if its 12MHz or 8MHz for default). The BeagleBone only likes frequencies that are [ 12MHz/(2^x) ].

    So that means BeagleBone likes frequencies of:
    12MHz, 6MHz, 3MHz, 1.5MHz, 750kHz, 375kHz, etc…..

    I posted some pictures of me and an oscilloscope with me sending [0b10101010] at this website:!msg/beagleboard/Lo0GEl1RdbU/2D4n4b9bSrMJ

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>