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.
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.
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
- 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.
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?
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
$ sudo apt-get install samba samba-common-bin
$ 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
Restart the Samba server using this command:
$ sudo /etc/init.d/smbd restart
$ sudo /etc/init.d/samba stop
$ sudo /etc/init.d/samba restart
But those commands are now obsolete. Listing here in case you are using an old version.
Mount the Pi folder
- Launch Finder
- Select from the main menu Go / Connect to Server
- Enter this address (substituting pi4 for for your pi hostname):
- Click Connect
- When prompted, remember to enter your Pi username (i.e. pi) and the Samba share password
- For volume selection, you may have only one option (pi)
- Click OK
The new volume should now appear in Finder.
You should now be able to access and transfer files to and from the Pi from your Mac.
You can do that either through Finder or from the command line.
Copy the file to your Pi via the command line
When you connected via Finder, it mapped the remote pi Folder to /Volumes/pi.
In a Terminal window on your Mac, copy the compiled binary to the Pi:
$ cp ~/raspberry/hello/bin/hello /Volumes/pi $ ls /Volumes/pi
The hello binary should now be in the home folder of user pi on the Raspberry Pi.
For more information on how to setup Samba, see my article Raspberry Pi Remote File Access Using Samba.
Step 6: Run the executable on your Pi
Login to your Pi. Find the executable and run it like this:
It should print the hello message to the console.
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.
- Raspberry Pi Remote File Access Using Samba
- Raspberry Pi Resource Guide - checkout my resource guide for links to my most popular Raspberry Pi articles, as well as useful hardware