I prefer to write code on my Mac and then push it to the Pi. That way I don't have to try to find the Pi version of development tools and editors that I like to use. It also keeps junk off the Pi leaving it lean and mean.
This is fine for interpreted languages. But when I want to write code in C or C++ this becomes a problem. If I compile code on my Mac or even in a Linux box I will run into issues. The Pi has an incompatible chipset. I have to use tools that will compile code that works on the Pi.
Pi Tools
The folks at Raspberry Pi have a repo out on GitHub that has the tools needed for cross compiling.
Here is where you can find the x64 tools that work on Linux:
If you look in the bin folder you will find a number of tools. For this demo I'm going to use the gcc tool located in the subfolder at bin/arm-linux-gnueabihf-gcc.
Ubuntu
From what I've read a number of people have had trouble installing the tools and getting them to work on a Mac. It looked easier to get things working on Ubuntu (a popular flavor of Linux).
There are a few ways to install and run the tools on Ubuntu:
- Create a virtual machine using free tools like VirtualBox
- Create a Docker container
I've done quite a lot of work in virtual machines based on Ubuntu using VirtualBox. I wouldn't be opposed to using it. I could install Eclipse on it and use that to build C++ apps that work on the Pi. But for this demo this seemed like overkill.
All I need to do is this:
- Create a C++ project
- Push it to Ubuntu and have it use the Pi tools to compile it
- Get the resulting executable and push it to the Raspberry Pi
If I edit on my Mac and have a way to get the executable back, I only need Ubuntu for the second step. All it needs to do is run a make file and give me back the result. There has to be a more efficient way then having to do this:
- Launch VirtualBox
- Start the Ubuntu Virtual Machine
- Login
- Launch Eclipse
- Open the project
- Edit some files
- Compile the program
- Push or pull the executable from the VM to the Pi or the Mac
This doesn't even cover installing Eclipse, setting up the plugins or mapping drives, setting up git repo ssh keys, etc. I've also had issues with VirtualBox images imploding. So after all the hassles of setting up I would need to make sure I take snapshots and backup the image on a regular basis.
There must be an easier way.
Docker
Tools like VirtualBox are for creating virtual machines. Docker is used to create containers.
As explained in the InfoWorld article Containers 101: Linux container and Docker explained.
'[containers are] something that feels like a virtual machine, but sheds all the weight and startup overhead of a guest operating system.'
The other thing I like about them is that they are great for running headless. You can fire them up from the command line. Then you can either set them to continue running, or do a job, then exit. You can even interact with them at the command line level.
To compile code all it needs to do is to start, compile my code and then exit.
What about the executable?
I could setup the container to push the resulting executable to a certain location. But that won't make it very generic. If I don't do that and I don't leave it running, where do I get the executable? For that matter, how would a generic container know where to find my code to compile it in the first place?
Shared Volume
One of the neat tricks about Docker is you can start (run) an image with a path parameter using the run -v flag. You can tell it to map a folder on your hard-drive to a folder within the container.
For example here is a docker run command that will map the the folder ~/raspberry/hello on your Mac to a folder called /build inside the image.
docker run -it -v ~/raspberry/hello:/build mitchallen/pi-cross-compile
If you place a C++ project with a Makefile in ~/raspberry/hello on your Mac, you can compile it within an Ubuntu image using this command:
make -f /build/Makefile
That's assuming you are using a Docker image setup to work like this, which I have. I'll show you how you can use it too.
See how it works
To demonstrate how this works you will need to do the following:
- Install Docker
- Pull the image I created for cross compiling from their hub
- Use git to make a local copy of my Raspberry Hello demo
- Run the docker container to build the demo
- Copy the resulting executable to the Pi
- Test it on the Pi to make sure it works
Step 1: Install Docker
Go to https://docs.docker.com/engine/installation/ and install Docker.
Step 2: Pull the image
You can find the special docker image that I created for cross compiling here:
You can bring it down to your system by opening up a terminal window and issuing a docker pull command:
docker pull mitchallen/pi-cross-compile
Step 3: Clone my Raspberry Hello demo
Clone the project into ~/raspberry/hello.
mkdir ~/raspberry
cd ~/raspberry
git clone https://github.com/mitchallen/pi-hello-cross-compile.git --depth=1 hello
Step 4: Build the demo
docker run -it -v ~/raspberry/hello:/build mitchallen/pi-cross-compile
A new executable should now exist:
ls -l ~/raspberry/hello/bin/hello
Inspect the file to see if it was compiled for ARM (the Raspberry Pi chip):
file ~/raspberry/hello/bin/hello
The output should look like this:
bin/hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, not stripped
Step 5: Copy the executable to your Pi
To copy the executable to your Pi you will need a way of getting the file on to it. One way is to install Samba and share a folder.
Login to your Pi and do the following:
Get the latest updates
$ sudo apt-get update
Install Samba
$ sudo apt-get install samba samba-common-bin
Configure Samba
$ sudo nano /etc/samba/smb.conf
Find the line that says read only and set it equal to no.
Create a Samba password for remote access as user pi.
$ sudo smbpasswd -a pi
Stop and restart the Samba server
$ sudo /etc/init.d/samba stop
$ sudo /etc/init.d/samba restart
Copy the file to your Pi
In Finder wait a bit and you should see your Pi show up under Shared in the left hand column. Connect as user pi using the samba password that you just created.
Copy the executable to your pi via Finder.
Step 6: Run the executable on your Pi
Login to your Pi. Find the executable and run it like this:
./hello
It should print the hello message to the console.
The Makefile
You can view the Raspberry Hello project here:
At the top of the Makefile you will see this line:
CC = /pitools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-gcc
When I created the Docker container I set it up to clone the Raspberry Pi Tools to the folder /pitools.
If you want to create a make file to compile your project with my Docker image you will need to reference the tools like that.
Create your own container
How to create Docker containers is beyond the scope of this article. But if you would like to see how I did it you can find the Dockerfile here: https://bitbucket.org/mitchallen/pi-cross-compile.
References
- Build your own images (docs.docker.com)
- Code Craft Cross Compiling for Raspberry Pi -- another way to do it which also covers debugging
- How to Push Builds to a Raspberry Pi using Balena (Resin)
About the Author
Mitch Allen works for a robotics company in New England.