Go on Raspberry Pi: simple cross-compiling

Go – or Golang if you prefer – is a powerful programming language, applicable to virtually any platform, from single-board computers (SBCs) such as the Raspberry Pi to supercomputer clusters.

I tend to work at the lower end of that scale. And I was motivated to check out Go because it has a reputation for being fast – unlike, say, Python – while not requiring the hairy-chested self-flagellation that comes with C.

However, I’m not a fan of developing directly on the Raspberry Pi. It’s a tad slow to say the least. I don’t know if VS Code, my current favourite editor, runs on the platform, but I bet it doesn’t run well.

When coding in Python, I program on my iMac, with the files being in a directory dedicated to a specific Raspberry Pi. I then have a standard script on the Pi that pulls across new or changed files using rsync. So the workflow is:

  • Make changes to the code on the iMac. Save the file.
  • Switch to a terminal I have open connected to the Raspberry Pi via SSH and run the rsync script.

It’s slightly clunky, but works well enough.

I’ve been doing a bit of C programming for the Pi using Eclipse running on a Linux box. This works very well and even allows direct debugging. It’s great, although it means using a different machine to my daily driver iMac. (I have this environment also set up in a Linux VM on the Mac.)

So what about Go? I could install Go on the Raspberry Pi and code there, but this has two disadvantages:

  • There’s the aforementioned feebleness of the Pi as a development environment.
  • Having to install Go on every Pi where I want to run the code.

Better solution

A better solution is to develop on the iMac (or whatever machine you use as your primary computer, although I have no experience of doing any of what follows on Windows and have no clue how you would) and cross-compile for the Pi. Fortunately, Go makes this very easy.

I’m not going to go into how you install Go and set up your environment ($GOPATH and the language’s directory structure etc). If you want a good introduction to all that, I’ll put a video and a couple of links at the end of the post.

My solution is simple. Much like the way I work with Python, I have an SSH terminal open and logged into the target Pi.

On my Mac, I have a script in the same directory as the Go file that contains the main function (often called main.go or <name_of_project>.go). We’ll get to the contents of that script in a moment.

Another prerequisite is an SSH key pair. I use the same key pair on both the Mac and all my Raspberry Pi boards precisely for this kind of operation. The private key is call dev-key and the public key dev-key.pub. The public key is also in the authorized_keys file that’s copied to the ~/.ssh directory on all the machines as a standard part of my setup routine for a new Raspberry Pi.

Build & Go

To build Go code for an architecture different to the one you’re currently working on is largely a matter of passing some environment variables to Go’s build command.

Let’s say I’m working on a project called ‘helloworld’. By Go convention (and this is essential to my solution), the directory in which the files for this project are kept is also called helloworld. That’s also where I place my Bash script called up.

In our terminal, assume we’ve CD’d to the helloworld folder where our code is in the file helloworld.go.

Normally, we’d build this using: go build. But that would build it for the local architecture. To build it for the Raspberry Pi, we use: env GOOS=linux GOARCH=arm GOARM=7 go build.

That line configures the target OS as Linux, the architecture as ARM and the ARM version as 7, which is good for the Raspberry Pi 2 and 3 boards. For other versions of the Pi – A, A+, B, B+ or Zero – you’d using GOARM=6.

The resulting executable won’t run on the host machine, but will run on the Raspberry Pi once you’ve moved it there. So that’s the next job.

Of course, so long as the code doesn’t contain anything Raspberry Pi-specific – such as libraries to access the GPIOs – then there’s nothing to stop you building and testing it on the local machine without pushing it to the Pi.

Sending to the Pi

I simply use scp to transfer the executable file to the Pi. A basic version of this – assuming an executable name of ‘helloworld’, a Pi with an IP address of 10.0.0.20 and a user called ‘pi’ – would be to type:

scp helloworld pi@10.0.0.20:

This would copy the file to the home directory of the user ‘pi’. But it will require typing in that user’s password every time you do this, which is annoying. Also, both build and scp commands require quite a lot of typing. So this is an obvious candidate for a shell script. Here’s the Bash script I used, called ‘up’:

#!/bin/bash
TARGET_USER=pi
TARGET_HOST=10.0.0.20
TARGET_DIR=code
ARM_VERSION=7

# Executable name is assumed to be same as current directory name
EXECUTABLE=${PWD##*/} 

echo "Building for Raspberry Pi..."
env GOOS=linux GOARCH=arm GOARM=$ARM_VERSION go build

echo "Uploading to Raspberry Pi..."
scp -i ~/.ssh/dev-key $EXECUTABLE $TARGET_USER@$TARGET_HOST:$TARGET_DIR/$EXECUTABLE

As you can see, I’ve configured a few things at the start, including the fact that the target directory on the Raspberry Pi won’t be the user’s home dir but a folder called ‘code’ just below it.

The name of the executable we want to transfer is assumed to be the same as the enclosing folder name, which is obtained using ${PWD##*/}. In a shell, $PWD gives the current directory, but with a full path. The ##*/ part retrieves just the last part and removes any trailing slashes using parameter expansion. I don’t fully understand this, but it works.

Next comes the build command, as discussed earlier. Then we upload to the target Raspberry Pi using scp with the -i flag pointing to where scp can find the SSH key on my Mac. This copies the file with no need for passwords.

And that’s pretty much it. I can see ways in which this script can be improved: for example, it could test for any parameters that have been passed in case we want to build a file other than the main one. But at my stage of learning Go (ie, right at the beginning) it’s very convenient to be able to simply type ./up into the terminal with VS Code and have the code built and pushed to the Raspberry Pi.

Further information

I promised some more information about getting set up with Go, so here you are. Brad Traversy has a great (if long) video about the basics. And the Go project itself has some excellent intros.

 

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.