More Go on the BeagleBone Black: Simple Framebuffer Graphics

In the previous post, we got a build of the Go compiler tools running on the BeagleBone Black and wrote a simple “Hello, World!” program. Text is great, but one of the prominent new features of the BeagleBone Black over the original BeagleBone is the addition of a micro-HDMI port for graphics display, allowing the device to work as a full-fledged desktop system out of the box with no need for additional “capes”. Wouldn’t it be cool if we could draw some graphics through that port? Well, let’s do that.

There are quite a number of ways to display graphics on Linux systems and I’m likely to cover some more of these in future posts, but for now we’re going to stick with the simple dumb framebuffer interface exposed by Linux’s fbdev. fbdev has long been supported across a wide array of Linux devices, it is conceptually simple and best of all it can be programmed against using a memory-mapped device file which lets us easily utilize it from pure Go code without having to resort to CGO bindings.

We start by implementing a FrameBuffer struct type with some simple methods for drawing and just two members, one to hold a “backing” Go image.RGBA which will be used for client code to draw on, and the other a simple in-memory data buffer that represents the memory-mapped contents of the /dev/fb0 file. When the Flush method is called on the FrameBuffer, the contents of the backing image will be drawn to the memory-mapped framebuffer file, converting the RGBA data of the image to RGB555 to match the native framebuffer layout of the BeagleBone Black. The pixel conversion code isn’t particularly efficient, there are lots of ways it could be enhanced at the cost of readability (if you’re interested in the nitty gritty of really fast software blitting, I highly recommend checking out the C code of SDL or Enlightenment), but it is good enough for now.

A lot of the code below is simply Go definitions for things that are defined in /usr/include/linux/fb.h for C/C++ development. Since we’re ultimately calling system calls which expect pointers to structures with a specific alignment of members, it is important to ensure the Go struct type matches the C/C++ struct in terms of sizes for all the members. In this simple introduction we don’t really do much with the fbinfo data made available to us, though it is nice to have for future enhancements.

framebuffer.go
————–

package main

import (
        "fmt"
        "image"
        "image/draw"
        "os"
        "syscall"
        "unsafe"
)

// should match value of FBIOGET_VSCREENINFO in /usr/include/linux/fb.h
const fbioget_VSCREENINFO = 0x4600

// should match layout of fb_bitfield struct as defined in /usr/include/linux/fb.h
type bitfield struct {
        offset   uint
        length   uint
        msbRight uint
}

// should match layout of fb_var_screeninfo struct as defined 
// in /usr/include/linux/fb.h
type screenInfo struct {
        width         uint
        height        uint
        widthVirtual  uint
        heightVirtual uint
        offsetX       uint
        offsetY       uint
        bitsPerPixel  uint
        grayscale     uint
        red           bitfield
        green         bitfield
        blue          bitfield
        transp        bitfield
        nonstd        uint
        activate      uint
        heightMM      uint
        widthMM       uint
        accelFlags    uint
        pixclock      uint
        leftMargin    uint
        rightMargin   uint
        upperMargin   uint
        lowerMargin   uint
        hsyncLen      uint
        vsyncLen      uint
        sync          uint
        vmode         uint
        rotate        uint
        reserved      [5]uint
}

type FrameBuffer struct {
        img  *image.RGBA
        data []byte
}

func OpenFrameBuffer(devName string) (fb *FrameBuffer, err error) {
        var file *os.File

        if file, err = os.OpenFile(devName, os.O_RDWR, 0660); err != nil {
                return
        }

        defer file.Close()

        fb = new(FrameBuffer)

        var fbInfo screenInfo
        var errnop syscall.Errno

        if _, _, errnop = syscall.Syscall(syscall.SYS_IOCTL, uintptr(file.Fd()),
                uintptr(fbioget_VSCREENINFO),
                uintptr(unsafe.Pointer(&fbInfo))); errnop != 0 {
                err = fmt.Errorf(
                   "Unable to read framebuffer screeninfo (err %v).", errnop)
                return
        }

        if fb.data, err = syscall.Mmap(int(file.Fd()), 0,
                int(fbInfo.width*fbInfo.height*
                        (fbInfo.bitsPerPixel/8)),
                syscall.PROT_WRITE, syscall.MAP_SHARED); err != nil {
                return
        }

        // create RGBA backing image...
        fb.img = image.NewRGBA(image.Rectangle{image.Point{0, 0},
                image.Point{int(fbInfo.width), int(fbInfo.height)}})

        return
}

func (fb *FrameBuffer) Width() int {
        return fb.img.Bounds().Dx()
}

func (fb *FrameBuffer) Height() int {
        return fb.img.Bounds().Dy()
}

func (fb *FrameBuffer) DrawImage(src image.Image, r image.Rectangle) {
        draw.Draw(fb.img, r, src, image.ZP, draw.Src)
}

func (fb *FrameBuffer) Flush() {
        // write current contents of our backing image.Image
        // to the memory our framebuffer file is mapped to,
        // convering 32-bit RGBA layout to 16-bit RGB555
        data16 := *(*[]uint16)(unsafe.Pointer(&fb.data))
        img32 := *(*[]uint32)(unsafe.Pointer(&fb.img.Pix))
        dst := 0

        width := uint(fb.img.Bounds().Dx())
        height := uint(fb.img.Bounds().Dy())

        for y := uint(0); y < height; y++ {
                for x := uint(0); x < width; x++ {
                        data16[dst] = uint16((img32[dst]>>3)<<11 |
                                ((img32[dst]>>8)&0xFF>>2)<<5 |
                                (img32[dst]>>16)&0xFF>>3)
                        dst++
                }
        }
}

And now let’s add some code to utilize this FrameBuffer struct:

main.go
——-

package main

import (
        "image"
        "image/color"
        "image/png"
        "log"
        "net/http"
        "time"
)

func main() {
        bburl := "http://beagleboard.org/static/beaglebone/a3/Docs/beagle.png"
        gourl := "http://golang.org/doc/gopher/bumper480x270.png"

        var (
                fb  *FrameBuffer
                err error
        )

        if fb, err = OpenFrameBuffer("/dev/fb0"); err != nil {
                log.Fatalf("err: %v\n", err)
        }

        // Draw solid red to the framebuffer
        drawBackground(fb, 255, 0, 0)
        time.Sleep(3 * time.Second)

        // Draw solid green to the framebuffer
        drawBackground(fb, 0, 255, 0)
        time.Sleep(3 * time.Second)

        // Draw solid blue to the framebuffer
        drawBackground(fb, 0, 0, 255)
        time.Sleep(3 * time.Second)

        // Draw Beaglebone image
        if err = drawUrl(fb, bburl); err != nil {
                log.Fatalf("err: %v\n", err)
        }

        time.Sleep(3 * time.Second)

        // Draw solid green to the framebuffer
        drawBackground(fb, 0, 255, 0)

        // Draw Go image
        if err = drawUrl(fb, gourl); err != nil {
                log.Fatalf("err: %v\n", err)
        }

        time.Sleep(3 * time.Second)

        // Draw solid red to the framebuffer
        drawBackground(fb, 255, 0, 0)
}

func drawBackground(fb *FrameBuffer, r, g, b uint8) {
        fb.DrawImage(&image.Uniform{color.RGBA{r, g, b, 255}},
                image.Rect(0, 0, fb.Width(), fb.Height()))
        fb.Flush()
}

func drawUrl(fb *FrameBuffer, url string) (err error) {
        var (
                resp *http.Response
                img  image.Image
        )

        if resp, err = http.Get(url); err != nil {
                return
        }

        defer resp.Body.Close()

        if img, err = png.Decode(resp.Body); err != nil {
                return
        }

        // draw retrieved image, centered in the framebuffer

        imgWidth, imgHeight := img.Bounds().Dx(), img.Bounds().Dy()
        drawX, drawY := (fb.Width()-imgWidth)/2, (fb.Height()-imgHeight)/2
        fb.DrawImage(img, image.Rect(drawX, drawY, 
                drawX+imgWidth, drawY+imgHeight))

        fb.Flush()

        return
}

Place the contents of both of these into files in the same directory, “go build” in that directory and you should end up with a program that will draw to the framebuffer, first a blank red screen, a blank green screen, a blank blue screen, followed by the BeagleBone logo image (on blue) and a Go logo image (on green), and then back to a blank red screen. To actually see this when you run it your BeagleBone will need to be attached to a display monitor via the micro-HDMI port. You’ll also want to shut down X11 (which runs by default on the BeagleBone Angstrom distro), you can do that by typing, as root:

# systemctl stop gdm.service

After executing that you should see your BeagleBone’s display switch from X11 to a standard tty-style text console. You may also want to shut down the cursor blinking so as not to be fighting that when our program draws to the framebuffer. You can do that by executing, at root:

# echo 0 > /sys/class/graphics/fbcon/cursor_blink

In the video below (apologies for the dark “HackerCam”) I have a BeagleBone Black hooked up to a Motorola Lapdock (bought for $45 back when they were being liquidated, quite nice!) as a dumb HDMI display device, it shows what you should expect to see upon running the code presented here.

Go on the BeagleBone Black

I started by setting up the Beaglebone Black using the instructions here.

If you happen to be using Windows 8 x64 as your desktop OS (as I am), you’ll have to turn off driver signing enforcement to successfully install the BeagleBone drivers available on that page, you can find more information about that process here.

I highly recommend flashing a new BeagleBone to the latest image of Angstrom, available here.

There are quite a few alternate distros for the BeagleBone Black already available (links to them can be found on the BeagleBone Getting Started page), but for now I’m sticking with Angstrom. The particulars of getting Go up and running will differ depending upon which distro you use, but if you’re the type of person who instantly installs your favorite distro on a new board, you’re probably going to know what the differences are already.

For Angstrom, the setup is pretty straightforward. Because pre-built Linux/ARM packages for Go1.1 (currently in RC1 status as of this writing) aren’t readily available and because many enhancements and performance improvements have been made to the language since Go1.0.3, I recommend building the Go tools from source.

I tend to use the latest Go source code for my hobby projects, so having Mercurial (the source code control manager used by the Go project) on a system I’m installing the Go tools on is ideal for me. Building Mercurial for the Angstrom distro for the BeagleBone Black is fairly easy to do. First, a couple of Python modules required for building Mercurial must be acquired, you can do this using the opkg package manager. As root (this assumes you have your BeagleBone Black successfully attached to a network connection with Internet access)::

# opkg update
# opkg install python-distutils
# opkg install python-compile

For the rest of this, I recommend creating a non-root user via the adduser command, and logging into the device as that user, but you can use root if you really want to. To build Mercurial 2.6:

$ mkdir ~/pkgs
$ cd ~/pkgs
$ wget http://mercurial.selenic.com/release/mercurial-2.6.tar.gz
$ tar xvf mercurial-2.6.tar.gz
$ cd mercurial-2.6
$ make local

Assuming everything goes well you should now have an executable named ‘hg’ (‘hg’ is the command-line tool that acts as the front-end for Mercurial’s functionality) in the current directory. Let’s add the current directory to the shell PATH so this command can be found:

$ export PATH=$PATH:~/pkgs/mercurial-2.6

Now, to get and build the latest Go tools:

$ cd ~/pkgs
$ hg clone http://code.google.com/p/go
$ cd go/src
$ ./make.bash

The make.bash script does not run all of the automated tests available when installing a new set of Go tools, for that you can use the all.bash script (in the same go/src directory), but keep in mind doing that will take considerably longer to run.

At this point you should have a usable build of Go on the BeagleBone Black, and you can add this directory to your path so that the Go executable can be found:

$ export PATH=$PATH:~/pkgs/go/bin

Test:

$ go version

You should see output similar to this:

go version devel +112ea225794f Fri May 03 14:22:34 2013 -0700 linux/arm

Let’s do a quick Hello World sanity test:

$ mkdir ~/godev
$ cd ~/godev
$ nano hello.go

Of course, you’re free to substitute nano with vim, or whatever your favorite editor is, though the set of available editors that ship on Angstrom without installing a new package is somewhat limited.

hello

package main

import "fmt"

func main() {
        fmt.Printf("Hello, World!\n")
}

Run it:

$ go run hello.go

And hopefully it prints out “Hello, World! as expected.

If you expect to be using this Go setup often, it is probably a good idea to add the Mercurial and go/bin directories to your permanent PATH variable by editing the .profile file in your home directory and adding the following line:

export PATH=$PATH:~/pkgs/go/bin:~/pkgs/mercurial-2.6

I2C Programming in Go

I2C is a two-wire serial bus interface historically used to communicate between different components of an electronics device on the same circuit board.  Amidst the rise in hobbyist/open electronics and the “Internet of Things”, I2C has also become popular as a well-defined communication technology between off-the-shelf electronic project components of the type commonly sold at Adafruit, Sparkfun, and Newark.  Accelerometers, NFC chips, LED displays, and many other types of devices are available using this interface, allowing hackers to easily communicate bidirectionally with special-purpose components from their micro-controller or full-fledged computer projects.

One example of such a component that is fun to play with is the Adafruit 8×8 LED Matrix w/I2C Backpack.  This device is pretty much exactly what it sounds like, 64 LEDs arranged 8×8 that can be individually controlled over an I2C interface via an HT16K33 integrated circuit.  Starting with a few of these devices and a Raspberry Pi, I decided to delve into what it would take to drive the LEDs from my favorite language, Go.

I started by soldering the LED matrix devices to their backpacks as described here:

http://learn.adafruit.com/adafruit-led-backpack/0-8-8×8-matrix

and hooking one up to the Raspberry Pi as described here:

http://learn.adafruit.com/matrix-7-segment-led-backpack-with-the-raspberry-pi/overview

Adafruit provides great support for controlling these devices and others that they carry from within Python code (available via GitHub (https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code) and this code works well for spot-checking that the device has been successfully connected and is in working order.  All of my soldering went okay and the device worked (though I did have to modify the Python code slightly because at the time it assumed it would be driving a LED matrix on I2C bus 0, but it should have been using bus 1 because the I2C bus number designations flipped around the time the newer 512MB Raspberry Pi devices became available and my Pi is a 512MB model).

Python is a fine language for many things, but my current go-to language for new projects is Go.  As someone who programmed in C and C++ professionally for a number of years and then moved to other languages like C#, Java, and others, Go has reintroduced me to the simplicity in programming that I didn’t fully realize I was missing, while doing away with the bits I never cared much for like make/autoconf hell and always worrying about the specifics of memory allocations and deallocations.  The fact that it has fantastically clean support for multithreading out of the box is an additional benefit, though for this specific use case multithreading isn’t much of a factor.

On the initial software setup side, it is pretty easy to set up a Go compiler environment on the Raspberry Pi.  Dave Cheney has excellent instructions here:

http://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi

I recommend skipping the ./all.bash step and just doing ./make.bash (also mentioned on that page) as it probably isn’t necessary for you to run all of Go’s unit tests and doing so can take a considerable amount of time on the Pi.

Linux has excellent support for the I2C protocol within kernel drivers, which is how the Adafruit Python code and other I2C-driving applications talk to the external components from a full SoC-type device like the Raspberry Pi.  C language interfaces for this functionality can be found described in various Linux header files such as <linux/i2c.h>, <linux/i2c-dev.h> and an API exists as part of the lm-sensors I2CTools package.  Using CGO, we could use these API interfaces directly from C, and then link that to our Go code, but I generally prefer to write code entirely in Go when possible. Pure Go code keeps things simpler overall and makes it easier to cross-compile code as Go has fantastic support for native cross-compiling, but CGO is currently unsupported when cross-compiling..

So, what other options do we have?   Well, like most UNIX derivatives, Linux has a history of allowing device control through access to files (residing in the /dev directory) that appear to be ordinary file system files.   While this isn’t quite as universal as say Plan9, where basically everything is a file, it does make it pretty easy for a language like Go to communicate with devices easily, so long as the language is capable of reading and writing files (which Go certainly is).

Luckily for us, modern Linux systems with I2C support usually export this control out to device files.  The files reside in /dev as expected and there is one per I2C adapter bus, taking the naming convention /dev/i2c-(bus_number), so on a device like the Pi with two I2C busses available the files are /dev/i2c-0 and /dev/i2c-1  Read/write access to the  bus is accomplished via standard read/write calls to these files along with some special ioctls available on them.  Go can read and write files and has ioctl support via the syscall package, so it should be pretty easy to write a pure Go library to handle this communication!

Note: On Rasbian, which I think is the most popular distro for the Pi, you’ll need to load a couple of driver modules before the /dev/i2c- device files will appear.  You can do this using the modprobe command as shown below. On Adafruit’s Occidentalis distro, these drivers should automatically be loaded without needing to modprobe them in.


sudo modprobe i2c-dev
sudo modprobe i2c-bcm2708

Using the documentation for the Linux I2C interface at http://www.kernel.org/doc/Documentation/i2c/dev-interface and reading through the associated header files, I came up with the following Go code for reading from and writing to the I2C bus adapters:

i2c_bus.go

Those familiar with Go may wonder why this code doesn’t use channels, one of the signature features of Go.   I certainly considered using channels here as they would make handling the potential threading issues very easy, but ultimately channels were a bad fit for reads (though writes would have worked well).  In order to keep the API for reads and writes consistent, I went with more traditional mutexes to control access to the singleton-like I2CBus devices to make sure address sets were handled atomically with device reads and writes.

Using this code, clients can trivially request a pointer to an I2CBus via the adapter bus number and then read and write to devices via addresses and device registers.

On top of this I2CBus base code, I wrote an HT16K33 device class based on the Adafruit Python code allowing application code to write to a buffer consisting of 8 unsigned 16-bit integers which represent a data block that can be sent to and from the HT16K33 over I2C describing the current state of the LED pixels on the device.

I then implemented some control code (also based largely on the existing Python code) to act as  simple interface to an 8×8 LED matrix array (since the HT16K33 can be used to control various configurations of LED devices).

The end result of this is the HT16K33.EightByEight class which can be instantiated and then read from or written to using the Pixel and SetPixel functions:

// Create a new instance of an 8x8 LED matrix class
// corresponding to a device on I2C adapter 1 with a
// device address of 0x70 and turn on the LED at
// location 0, 0
grid, err := HT16K33.NewEightByEight(0x70, 1)

if err != nil {
    log.Panicf("uh oh! %v\n", err)
}

grid.SetPixel(0, 0, true)


(Continued on Page 2)