Pretty much all of the code I’ve written for the Raspberry Pi (and the BeagleBone for that matter) has been in Python. It’s widely regarded as the de facto language for the platform, not least because it is newbie-friendly. But does it have to be this way?
Ups and downs of Python
It’s not hard to enumerate a long list of advantages with Python. Being (mostly, kinda) interpreted, it’s easy to experiment and play around. Make a change. Run it. Make a change. Run it. Just like I did when I first learned Basic in the 1980s. I hardly ever bother with the REPL.
If you use an editing package with a built-in terminal – I use VS Code – then you have everything you need in one place without having to go the full-blown IDE route. It’s a lean and responsive coding environment.
You need to set up your environment, with $PYTHONPATH configured and libraries in predictable and reachable places. There’s also the option of working in virtual environments, but I think that’s more relevant to professional Python developers who need to be able to work with multiple versions of the language or its libraries, rather than hobbyists looking to create code for the Raspberry Pi.
(As an aside, Node.js has many of the same benefits. I’m only just starting to explore Node, but I like what I see – particularly NPM.)
Python is also supported by a very rich ecosystem of libraries and frameworks, including many specifically for the Raspberry Pi.
For me, though, there’s one downside to Python – you need to configure your Pi. What I mean by this is that you have to have Python and any libraries you use (including your own) installed on the Pi.
This isn’t an issue if you have one Raspberry Pi and you develop directly on the board. Neither of those applies to me.
I have more Raspberry Pi boards than I’m prepared to admit in a public place. And, for lots of very good reasons, I prefer to write the code on my Mac.
Not that these are huge problems. Here’s what I do:
- I have a Bash script that I run once on any new Pi. This installs Python 3 and a bunch of standard libraries, creates the necessary $PYTHONPATH environment variable, creates directories for user libraries, and installs some scripts, pulling them from the Mac.
- One of these scripts (run as root) uses rsync to download any new user libraries from the Mac and update existing ones.
- Another script uses rsync to download any new or updated userland code for a specific user on the Pi. I do this my keeping my code in specific directories on the Mac – one directory per target Pi, plus one directory that contains code intended for all Pis.
- While coding, I have two SSH shells open to the Pi – one as root and one as the user. Whenever I make a change to a library, I run the root script. After each set of changes to user code, I run the user script. Then I test the code on the Pi via the SSH shell.
It’s not as clumsy as it seems, but it does require having the right environment on the Pi. If I decide, for example, that I want a program using a not-yet-installed standard library, I have to remember to run pip on every Pi where I might want to run this code.
Some of these issues could be resolved by developing a container. But then you still need to ensure Docker is installed on every Pi and you have to push out the images somehow. I mean, I am looking at doing that, but it’s not a simple solution.
Go was designed to be simple. Not only can you achieve a lot in a few lines of code, it doesn’t take long before you can absorb most of the language and feel you have a grasp – at least at a conceptual level – of the whole thing. With Python, I always have this nagging suspicion that what I’m struggling to achieve could be realised by a part of the language which, alas, still remains a dark secret to me. Often, that’s literally true, but that doesn’t take away from my point that it can be hard to feel as though you’re in control.
The package manager for Go is marvellous. Go is a highly opinionated language and that extends to the directory structure concerning where you put your code files. But that also makes possible very easy installation of third-party libraries and simplified management of your own libraries. To get an example of how simple it is, I recently installed a serial library, and to do that I simply typed at my shell’s command line:
go get github.com/tarm/serial
That’s it. Installed. Note the lack of a full URL. The Node.js NPM is also quite magical, and pip is easy enough, but in terms of simplicity I think Go has the edge. Also, you can see where everything is – it’s not installed into some far distant directory.
To use that package, I use:
import github.com/tarm/serial and from there all the packages methods and types are available via the
Dependencies are resolved through the
import function. No make files and weird compilation flags as you have with C. As I’m doing all development in one place, on the Mac, libraries need to be installed in only one place. (For the record, I actually do my Go development on two machines – a laptop and a desktop. But as everything you need for Go – code and libraries – is installed in one directory structure, I simply put this in a directory that’s shared via NextCloud, so my development environments on the two machines automatically stay in sync.)
At least for the kind of programs I’m writing, testing your program is as simple as typing
go run <program_name>.go in VS Code’s terminal. It quickly builds and executes – every bit as easy as using Python.
But here’s the big bonus with Go, as far as I’m concerned. As I wrote in a previous post, I can develop and compile the code on my Mac and simply push the executable to the Pi. The executable has no runtime dependencies. It’s all statically compiled. That executable will run on any Pi with no worries about having the right environment. And being compiled code, it’s fast.
C what I did there
If you want fast, compiled code, there’s always C/C++, of course. (At least a few purists will be incensed by my lumping them together like that.) C is often described as ‘portable assembler’ and its ability to get close to the metal is why it’s so ubiquitous in the microcontroller world, squeezing maximum performance out of resource-limited hardware.
But there’s an ugly side to C, including all that nonsense with make files and separate compilation and dependencies. And yeah, I’ve done some C++ coding for AVR microcontrollers where most (not all) of that tedious side of C/C++ is taken care of by an IDE (Atmel Studio, which is really Microsoft Visual Studio). And I’ve also done a little C/C++ development for the Raspberry Pi using a cross-compilation environment running on Linux. But while I can acknowledge the power and flexibility of C/C++, I can’t bring myself to love it. It’s not a language I enjoy using. More often than not, when something I’ve written in C/C++ works, it’s the fact that it works that feels like the achievement, not the thing that the software is actually doing.
Go offers the benefits of easily deployed, compiled code without having to use a bloated IDE. I use VS Code with the Go plugins and have my code automatically formatted for me via
gofmt (the language is very opinionated about formatting). If I refer to a standard library without having imported it, the editor will automatically add it to the import list (and remove any packages I’ve imported but that aren’t used).
For me, Go offers the level of simplicity and ease I get from Python when coding for the Pi, plus many of the benefits of C/C++.
So, am I switching from Python to Go when coding for the Pi?
At least, not entirely.
While there are some useful libraries allowing access to stuff like the GPIOs, the support for single board computers in general, and the Raspberry Pi in particular, isn’t nearly as strong as it is with Python. This doesn’t mean you can’t do everything in Go that you can do in Python – just that you’ll be doing more of the coding yourself. This will change over time.
I’m going to be writing more and more in Go, leaning on Python just when I need or want to use a particular library – when I can’t work out how to write the necessary code myself or don’t feel like doing it because I want to get something done quickly.
Over time, though, I can see myself edging more and more towards Go, especially as my own libraries start to build. For me, Go is going in the right direction.