golang on the raspberry pi, part 2

raw_ht16k33 from William Beebe on Vimeo.

This is a bit more Go and Gobot-based sophisticated application. This application opens and writes using “raw” calls to Adafruit FeatherWing displays using the HT16K33 I2C display driver chip. It has the ability to check to make sure the device is really there, and you can pass more than one device address on the command line. To make the video, I executed “raw_ht16k33 0x70 0x71” and the application dutifully turned on every segment on the display at address 0x70, then 0x71.

package mainimport ("fmt""log""time""os""os/signal""strconv""syscall""gobot.io/x/gobot/drivers/i2c""gobot.io/x/gobot/platforms/raspi")const DEFAULT_ADDRESS int = 0x70// Commands to send the HT16K33//const (HT16K33_SYSTEM_SETUP byte = 0x20HT16K33_OSCILLATOR_ON byte = 0x01HT16K33_DISPLAY_SETUP byte = 0x80HT16K33_DISPLAY_ON byte = 0x01HT16K33_BLINK_OFF byte = 0x00HT16K33_BLINK_2HZ byte = 0x02HT16K33_BLINK_1HZ byte = 0x04HT16K33_BLINK_HALFHZ byte = 0x06HT16K33_CMD_BRIGHTNESS byte = 0xE0)var address = DEFAULT_ADDRESSvar device i2c.Connectionfunc initialize() (device i2c.Connection, err error) {adapter := raspi.NewAdaptor()adapter.Connect()bus := adapter.GetDefaultBus()// Check to see if the device actually is on the I2C buss.// If it is then use it, else return an error.//if device, err := adapter.GetConnection(address, bus) ; err == nil {if _, err := device.ReadByte() ; err == nil {fmt.Printf(" Using device 0x%x / %d on bus %d\n", address, address, bus)} else {return device, fmt.Errorf(" Could not find device 0x%x / %d", address, address)}}device, _ = adapter.GetConnection(address, bus)// Turn on chip's internal oscillator.device.WriteByte(HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR_ON)// Turn on the display. YOU HAVE TO SEND THIS.device.WriteByte(HT16K33_DISPLAY_SETUP | HT16K33_DISPLAY_ON)// Set for maximum LED brightness.device.WriteByte(HT16K33_CMD_BRIGHTNESS | 0x0f)return device, nil}func lightAll() {// First four digits for Alphanumeric and 8x16 Matrix// FeatherWing Displays.//// The 'digit' address is the address/offset into the// HT16K33's internal eight byte array. Each bit// represents a segment or LED, each address a section// within an entire device//// Digit 0//device.WriteWordData(0, 0xFFFF)// Digit 1//device.WriteWordData(2, 0xFFFF)// Digit 2//device.WriteWordData(4, 0xFFFF)// Digit 3//device.WriteWordData(6, 0xFFFF)// Rest of the bytes for the// Adafruit 0.8" 8x16 LED Matrix FeatherWing Displaydevice.WriteWordData(8, 0xFFFF)device.WriteWordData(10, 0xFFFF)device.WriteWordData(12, 0xFFFF)device.WriteWordData(14, 0xFFFF)}func darkenAll() {// Turn off every segment on every digit.//var data []byte = make([]byte, 16)device.WriteBlockData(0, data)}func main() {// Hook the various system abort calls for us to use or ignore as we// see fit. In particular hook SIGINT, or CTRL+C for below.//signal_chan := make(chan os.Signal, 1)signal.Notify(signal_chan,syscall.SIGHUP,syscall.SIGINT,syscall.SIGTERM,syscall.SIGQUIT)// We can pass zero to many addresses on the command line.// For example, 'raw_ht16k33 0x70 0x71' will turn on both// displays, one after the other, if they are both attached.// If either one is not there or unreachable, the application// will abort.//var addresses []intfor _, arg := range os.Args[1:] {if newAddress, err := strconv.ParseInt(arg, 0, 32); err == nil {addresses = append(addresses, int(newAddress))} else {log.Fatal(err)}}// If nothing passed on the command line then use the default// address///if len(addresses) == 0 {addresses = append(addresses, DEFAULT_ADDRESS)}// We want to capture CTRL+C to first clear the display and then exit.// We don't want to leave the display lit on an abort.//go func() {for {signal := <-signal_chanswitch signal {case syscall.SIGINT:// CTRL+Cfmt.Println()darkenAll()device.Close()os.Exit(0)default:}}}()// Iterate over all the addresses passed on the command line (or not),// aborting if any of the devices are unreachable.//for _, addr := range addresses {address = int(addr)dev, err := initialize()if err != nil {log.Fatal(err)}device = devdarkenAll()lightAll()time.Sleep(2 * time.Second)darkenAll()device.Close()}}

This is also one of those little posts where I get to pull together a few other skills I’ve picked up along the way. The video was recorded with an Olympus E-M5 and mZuiko 17mm f/1.8 lens in manual focus mode. The video was quickly put together on my iPad Pro Mk 1, using iMovie, then pushed up to Vimeo where it’s linked above.

golang on the raspberry pi

Some months back I wrote about receiving a new pair of Raspberry Pi 3 Model B+ cards. These are the latest, with better wireless and a slight bump in processor speeds to 1.2GHz. There were a number of little changes that made the B+ a much better little computer to work on directly. And thus, I started to really dig into programming on the machine and manipulating its hardware ports, first in C++, and later in Go.

I’ve been tempted by Go for a while now, in part because of its tool chain, and in part because it’s a clean version of C, if you will. I’m doing my best to write clean idiomatic Go code, and getting pretty close, but finding that old coding C-isms keep creeping into my code. It’s hard to forget a language you’ve been writing in since the early 1980s; Lifeboat C on an IBM PC XT.

But I believe I’m making good progress. First of all, I’ve moved over entirely to Raspbian as my Linux distribution of choice. After moving back and forth several times between Arch Linux for ARM and Raspbian, I’m now over in the Raspbian camp permanently. The Raspbian graphical desktop is lightweight and very fast, and the version of Chromium that now ships is quite the little performer. The version shipping with the latest version of Raspian: Version 65.0.3325.181 (Official Build) Built on Raspbian , running on Raspbian 9.4 (32-bit).

As far as a development environment is concerned, I have VIM installed and configured to do a decent job of writing Go code.

And to make things even easier I’m using Gobot (https://gobot.io/) which makes writing code to work with hardware on the GPIO pins or on the I2C buss a snap.

Here is a simple application I wrote in Go 1.10.2.

package mainimport ("fmt""os""os/signal""syscall""gobot.io/x/gobot/platforms/raspi")func main() {// Hook the various system abort calls for us to use or ignore as we// see fit. In particular hook SIGINT, or CTRL+C for below.//signal_chan := make(chan os.Signal, 1)signal.Notify(signal_chan,syscall.SIGHUP,syscall.SIGINT,syscall.SIGTERM,syscall.SIGQUIT)// We want to capture CTRL+C to first clear the display and then exit.// We don't want to leave the display lit on an abort.//go func() {for {signal := <-signal_chanswitch signal {case syscall.SIGINT:// CTRL+Cfmt.Println()os.Exit(0)default:}}}()// Go find an I2C buss and open it.//adapter := raspi.NewAdaptor()adapter.Connect()bus := adapter.GetDefaultBus()// Now iterate across all I2C device addresses.// If we successfully read a byte from an address,// then we have detected a device. Print out the// hex address of that device.//for i := 0 ; i < 128 ; i++ {if device, err := adapter.GetConnection(i, bus) ; err == nil {if _, err := device.ReadByte() ; err == nil {fmt.Printf( " Found device at 0x%x / %d on I2C bus %d\n", i, i, bus)}}}}

The purpose of this simple utility is to find the active I2C bus and then to find the active devices on that bus. This is meant to be far simpler than i2cdetect, the utility that is available to Raspbian via its repository. You just type ‘detect’ at the shell command line, it it will produce output similar to this:

 Found device at 0x20 / 32 on I2C bus 1 Found device at 0x70 / 112 on I2C bus 1 Found device at 0x71 / 113 on I2C bus 1

In this example detect found three devices on I2C bus 1 at addresses x020, 0x70, and 0x71. That’s because I have an MCP23017 port expander at 0x20, and a pair of Adafruit FeatherWing four character alphanumeric displays (https://www.adafruit.com/product/3130) at 0x70 and 0x71. Unlike i2cdetect, I don’t have to tell it the I2C buss to use, nor give it a ‘y’ on the command line. It detects the bus, and then detects the devices on that bus. Great for when I want to just know if the devices I plugged in are available or not.