I recently bought a chumby one from Woot. It’s an extremely hacker-friendly device with a 454 MHz Freescale i.MX233 ARM processor, 3.5 inch touchscreen, USB port, accelerometer, speaker, internal USB wi-fi module, and an internal microSD card. It boots from the microSD card, so it’s pretty much un-brickable as long as you keep a backup of the original SD card contents.
It’s an awesomely cool device out of the box, but all of the GUI stuff is based on Flash. Now that’s wonderful and all, but I’m just not much of a Flash guy. I really like working with Qt on embedded devices, so I got a cross compiler up and running, allowing me to design stuff on my desktop computer (running Ubuntu 10.04) and deploy it onto the chumby. I have learned during my time as a developer to document what I did when I do things like this! Two years later it’s hard to remember exactly what I did. I learned this the hard way and now I always document a procedure like this as I’m going through it. I figured while I’m documenting it, I might as well share it with the world. These instructions will walk you through creating a modern cross compiler toolchain for the i.MX233 (compatible with the chumby’s libc), using that toolchain to compile Qt/Embedded 4.7.2, and finally, creating apps on your build machine and running them on the chumby.
The basics
Start out with an Ubuntu 10.04 (“Lucid Lynx”) installation. This procedure will probably work in newer and older versions, but I’m assuming you’re using 10.04. I’m going ahead and using an amd64 install of Ubuntu, but it should work fine in an i386 install as well. Once you have that installed (however you want to do it — directly on the computer, Wubi, in a virtual machine, or whatever other crazy install method you can conjure up), we’re ready to begin.
First of all, we need to install some prerequisite packages for various purposes. In a terminal window, type:
sudo apt-get install build-essential bison flex texinfo automake libtool cvs libncurses5-dev subversion
crosstool-NG
After getting all this stuff downloaded and installed, we’re ready to start creating the cross compiler. Download crosstool-NG (I used version 1.10.0) and unpack it somewhere. crosstool-NG is absolutely amazing. It saves so much trouble while creating cross compilers. You just go through a menuconfig interface similar to the Linux kernel’s menuconfig interface, telling it exactly what you want. If you’re lucky, you just tell it to work and it grabs all the source code, patches it, and compiles it automatically, leaving you with a fully-functional cross compiler after a half-hour or so. In this case, I have verified that the configuration I’m going to give you will work. In other cases, you may have to do some trial and error because some versions of C libraries and binutils don’t work with some versions of gcc. Generally you want to use tools that came from the same time period, as this will give you a better chance of everything working together. Anyway, go into the crosstool-ng directory in a terminal, and type:
./configure --local make
The –local option tells crosstool that we will be running it directly out of the directory we unpacked it to. Otherwise it would install itself into /usr/local or somewhere like that. I prefer it this way rather than putting it into /usr/local. This should create a script called ct-ng. We will be using this script to configure and create the toolchain.
So let’s start setting up the toolchain! I’d just give you the config file, but I’d rather walk you through setting up crosstool so you better understand how it works. Type:
./ct-ng menuconfig
After a few things finish setting up, you’ll be greeted with graphical interface in the terminal. You can move up and down with the arrow keys. The enter key will go to the selected item. Hit the Esc key twice to go back. Let’s go through the sections:
Paths and misc options
It turns out I’m going to break my own rules here. I’m going to choose newer compilers and binutils with an older glibc. It happens to work in this case, so no harm done. I’m using an older glibc because the glibc on the chumby’s root file system is version 2.8, so I’d like our version to match. If we use a newer version, some binaries will not be compatible with the older glibc, so we’d have to replace chumby’s glibc, and I’d rather not.
Highlight Use obsolete features and press the space bar to select it. This will allow us to choose an older glibc that is probably not compatible. (We’re actually going to use eglibc, which plays better with our setup than glibc)
Scroll down and you’ll notice that Prefix directory is set to “${HOME}/x-tools/${CT_TARGET}. This means it’ll create a folder called x-tools in your home directory which will store all cross toolchains you create. I left this alone, but if you’d like it elsewhere, go ahead and change it now.
Keep scrolling down until you find Number of parallel jobs. This one will save you some time if you have a dual- or quad-core processor. While creating the toolchain, this will allow some of the files to be compiled concurrently, making better use of your multiple-core CPU. For you techies out there, it’s letting you specify the -j option passed to the make commands that will run. I generally set this to the number of cores I have available. Since my 4-core Core i7 has hyperthreading, I normally choose 8. In a VMware virtual machine where I have given the VM two cores to work with, I’d choose 2. Different people have different opinions on what’s the best value here, but I’d say stick with the number of cores you have available and you’ll be fine. If you pick too high of a value your entire system will slow to a halt, and if you pick too low of a value, you’ll be under-utilizing your CPU.
Okay, sorry about that long explanation. Now we’re ready to move to the next section. Hit Esc twice to go back to the menu. Move down to Target options and hit enter.
Target options
Highlight Target Architecture (alpha) and hit enter. Scroll down to arm and hit the space bar. We are telling crosstool-NG that we’re building a cross compiler that will target the ARM architecture, which is what the chumby uses.
chumby uses a Freescale i.MX233, which is an ARM926EJ-S. We need to tell crosstool which CPU we’re targeting, so scroll down to Emit assembly for CPU and hit enter. Type “arm926ej-s” (without the quotes) and hit enter.
Scroll down to Floating point and hit enter. Scroll down to software and hit the space bar. The i.MX233 does not have a hardware floating point unit, so we need to choose softfloat here (otherwise, the kernel emulates hardware floating point instructions, and it gets terrible performance).
We’re done here, so hit Esc twice, move down to Toolchain options, and hit enter.
Toolchain options
Our toolchain will be called “arm-chumby-linux-gnueabi”. The “chumby” part of this is called the vendor string, so we need to configure it as such. The vendor string makes no technical difference–it’s purely a cosmetic thing. Scroll down to Tuple’s vendor string, hit enter, backspace until you have erased “unknown”, type “chumby”, and press enter.
Exit this menu and go to the Operating System menu.
Operating System
Change Target OS to linux. You’ll notice that the kernel version is set at 2.6.37. It may not make a huge difference in some cases, but in this case, we need to pick a kernel version closer to the chumby’s kernel (2.6.28-chumby). Otherwise, the touchscreen library won’t work. We could probably get a tarball of the chumby’s kernel and be exact, but rather than bother with that, I set it at 2.6.27.57. Once you’ve made that change, exit this screen and go to Binary utilities.
Binary utilities
We’re not going to change anything here, but this is where you would change the version of binutils. Leave it at version 2.20.1.
Exit again and go to C compiler.
C compiler
Leave gcc at version 4.4.5. You need to scroll down under Additional supported languages to C++ and hit the space bar to make sure the toolchain is enabled for compiling C++. Now move down to C-library.
C-library
The C library is already set as eglibc, which is what we want. Change eglibc version to 2_8 (OBSOLETE). Normally this would be a bad idea to use an old version of eglibc combined with newer binutils and gcc, but we need version 2.8 in order to be compatible with the chumby’s provided glibc, and it works OK with the newer binutils and gcc. This is the sole reason we had to enable Use obsolete features when we were starting out. We also need to add an extra option here to fix a problem I encountered when I first tried to build this toolchain. Go to extra target CFLAGS and set it to “-U_FORTIFY_SOURCE”. Exit this screen and head over to Debug facilities.
Debug facilities
Go ahead and enable gdb (leaving it at version 6.8). We probably won’t use it, but it won’t hurt to have it. Leave this screen.
All done configuring
We don’t need to mess with anything else, so hit Esc twice and you will be asked if you want to save your new configuration. Hit enter, and you’re ready to build your toolchain!
Type:
./ct-ng build
crosstool-NG will update you on the status of your build as it keeps going on, but for now you can sit back and relax. If you’d like, skip ahead and start downloading some of the other stuff you will need to have as you progress further through these instructions. Depending on the speed of your computer, this compilation might take 15 minutes to an hour.
If all goes well, crosstool-NG will finish by saying “Finishing installation (may take a few seconds)…” followed by a shell prompt. At this point, your cross toolchain is complete! Assuming you left it to be installed at the default location, you can now compile programs for the chumby by using:
~/x-tools/arm-chumby-linux-gnueabi/bin/arm-chumby-linux-gnueabi-gcc
I made a quick test app:
chumbytest.c:
#include <stdio.h> int main(int argc, char *argv[]) { printf("It works!\n"); }
compiled it:
~/x-tools/arm-chumby-linux-gnueabi/bin/arm-chumby-linux-gnueabi-gcc chumbytest.c -o chumbytest
and transferred it to the chumby with scp to make sure it worked before advancing any further (assuming ssh has been enabled on the chumby):
scp chumbytest root@MyChumbysName.local:/tmp
to run it on the chumby over ssh:
ssh root@MyChumbysName.local /tmp/chumbytest exit
As long as it worked as expected, you’re ready to start working on getting Qt compiled. Well, kind of.
tslib
We need to do one prerequisite in order to get the touchscreen working, and it’s a library called tslib. Download it and extract it.
Start out inside the tslib directory and type:
./autogen.sh
Let’s go ahead and add our cross compiler to our path:
export PATH=~/x-tools/arm-chumby-linux-gnueabi/bin:$PATH
Now we’re ready to configure tslib to be cross compiled, and we’re going to tell it to install itself into a temporary directory inside our current directory. There are two hacks we have to do here–the first is we define a variable ac_cv_func_malloc_0_nonnull to be yes to avoid a compile error, and we also add -U_FORTIFY_SOURCE to the CFLAGS to avoid other errors, the same way we had to during the compilation of eglibc. Here’s the complete command:
ac_cv_func_malloc_0_nonnull=yes ./configure --prefix=$PWD/tslib --host=arm-chumby-linux-gnueabi CFLAGS=-U_FORTIFY_SOURCE
Compile it:
make -j 2 (or 4 or 8 or whatever, depending on how many cores you have)
Install it:
make install
Now there should be a tslib subdirectory inside the tslib directory, containing the compiled library, plugins, and a config file in etc. We really need to put the tslib library and include files into the appropriate place in our cross compiler’s sysroot so we can use it when compiling. Type:
sudo cp -R tslib/lib/* ~/x-tools/arm-chumby-linux-gnueabi/arm-chumby-linux-gnueabi/sysroot/usr/lib
to copy all the libraries into the cross compiler. We should also copy the include files in there:
sudo cp tslib/include/tslib.h ~/x-tools/arm-chumby-linux-gnueabi/arm-chumby-linux-gnueabi/sysroot/usr/include
This will simply make it easier in the future when compiling, so we don’t have to specify where the libraries are located. Now, let’s get the touchscreen working on the chumby before we compile Qt. Transfer the compiled tslib subdirectory to the chumby’s storage:
tar -cf - tslib | ssh root@MyChumbysName.local tar -xf - -C /mnt/storage
Now we need to get a shell into the chumby, quit the chumby control panel, and set up a few environment variables so we can calibrate the touchscreen:
ssh root@MyChumbysName.local /usr/chumby/scripts/stop_control_panel export LD_LIBRARY_PATH=/mnt/storage/tslib/lib:$LD_LIBRARY_PATH export TSLIB_TSDEVICE=/dev/input/by-id/soc-noserial-event-ts export TSLIB_CALIBFILE=/mnt/storage/tslib/etc/pointercal export TSLIB_CONFFILE=/mnt/storage/tslib/etc/ts.conf export TSLIB_PLUGINDIR=/mnt/storage/tslib/lib/ts
Edit /mnt/storage/tslib/etc/ts.conf and uncomment the “module_raw input” line.
You’re now ready to calibrate the touchscreen! Type:
/mnt/storage/tslib/bin/ts_calibrate
If all goes well, you should see “TSLIB calibration utility” and “Touch crosshair to calibrate” appear on the screen. Touch the crosshair for each corner and then the center, and the app will exit (the screen will stay the same, though). Now you’re calibrated! Test it by typing:
/mnt/storage/tslib/bin/ts_test
You should be able to drag the crosshair around on the screen. Hit ctrl-C to exit.
OK, so your touchscreen setup is now complete. You know all of those export commands you just typed before calibrating? You may want to put them into a file somewhere so they can be sourced whenever you need to run them. Whenever you want to use the touchscreen, those environment variables need to be set that way. We are now finally ready to compile Qt.
Qt
Here we go! The exciting part has arrived. We are ready to compile Qt. First, you need to download the latest source code for Qt. Pick the LGPL version unless you have a commercial license. Note that these instructions are based on version 4.7.2. Once you’ve downloaded the tarball, extract it and enter its directory. We now need to set up a Qt spec for the chumby.
Spec
Inside the mkspecs directory is a list of other directories containing specs for many different architectures. There is also a directory called “qws”. We want to use a qws spec, because our Qt will be drawing directly to the frame buffer device without having to have a window server like X11 installed. Instead, Qt will be its own window server. In the qws directory, there is a spec called linux-arm-gnueabi-g++. This one is very similar to the toolchain we are using, so let’s go ahead and duplicate it:
cd mkspecs/qws cp -R linux-arm-gnueabi-g++ linux-arm-chumby-gnueabi-g++
Now, you need to edit the qmake.conf file inside of our new linux-arm-chumby-gnueabi-g++ directory. You will see several references to tools prefixed with “arm-none-linux-gnueabi-“. You should replace this prefix with the prefix for your toolchain. In fact, I would put the full path to each of these tools. For example, change arm-none-linux-gnueabi-gcc to /home/yourname/x-tools/arm-chumby-linux-gnueabi-gcc, and so on. It may seem stupid because you could always add the directory to your path, but when we’re executing the cross compiler from inside of Qt Creator, it will be easier if the full path is already there. Once you’re done with this, save the file and we’re ready to go!
Compiling
Here is the complete command, along with descriptions of what every option does below it:
./configure -opensource -embedded arm -xplatform qws/linux-arm-chumby-gnueabi-g++ -no-feature-cursor -qt-mouse-tslib -nomake examples -nomake demos
-opensource: specify that we’re using the LGPL. If you have a commercial license you should change this.
-embedded arm: specify that we’re compiling Qt/Embedded for ARM.
-xplatform qws/linux-arm-chumby-gnueabi-g++: specify the make spec that will be used for compiling. This tells Qt where our cross compiler is, and info about the cross compiled system.
-no-feature-cursor: hides the cursor, since it doesn’t make much sense to have one with a touchscreen. If you want a cursor, you can remove this option.
-qt-mouse-tslib: tells Qt to use tslib for its mouse driver
-nomake examples: Tells Qt to skip the examples. I already know how to use Qt so I don’t need a bunch of examples compiled. If you want them, keep it there.
-nomake demos: Same as above.
There are other options you can use to turn off specific features. If you don’t envision ever using WebKit, you can turn it off (-no-webkit). Anyway, type the command above, and you’ll have to agree to the open source license. Once that’s done, it’ll take a few minutes to configure the build. When the configure is complete, type:
make -j 2 (or whatever number of cores you have available)
Once that’s done (and it’ll be a while, trust me on this one!) you can install the libraries by typing:
sudo make install
The reason you need sudo is that Qt defaults to installing in /usr/local/Trolltech. You can change this with the -prefix option on the configure script, but I don’t mind Qt residing there. Once that’s done, you should have a functional Qt. Send the Qt libraries over to the chumby:
cd /usr/local/Trolltech/QtEmbedded-4.7.2-arm/ tar -cf - lib | ssh root@MyChumbysName.local tar -xf - -C /mnt/storage
And now Qt should be on the chumby’s SD card, with its libraries stored in /mnt/storage/lib. We can’t test it until we make a quick Qt app, though, so let’s install the Qt SDK.
Qt SDK
The Qt SDK is a massive download, currently in beta, but it works just fine. We’re going to install it and use it to create a test app to make sure Qt works. First, download the SDK from the Qt download site. I downloaded the Qt SDK 1.1 Beta online installer for Linux 64-bit from here (since my development machine is 64-bit). Make it executable and run it:
chmod +x Qt_SDK_Lin64_online_v1_1_beta_en.run ./Qt_SDK_Lin64_online_v1_1_beta_en.run
At this point, you’ll need to wait for it to finish “Retrieving information from remote installation sources…”. Once that’s done, accept the defaults, allow it to install in ~/QtSDK or wherever else you want it, and eventually you’ll have to wait for it to finish downloading and installing the SDK. Once it’s done, go ahead and allow it to Launch Qt Creator, but uncheck Open Qt SDK ReadMe.
After a few moments, Qt Creator will pop up. We now need to add our cross toolchain so that Qt Creator knows about it.
Adding the cross toolchain
Click on Tools, and choose Options. On the left, click Qt4. You should see a list of available Qt versions. We are going to add a new one, so click the blue + on the right side of the window. Where it says <specify a name>, type “Chumby Qt”, and where it says <specify a qmake location>, click Browse… and navigate to your cross compiled Qt’s qmake binary (/usr/local/Trolltech/QtEmbedded-4.7.2-arm/bin/qmake). Below, it should say “Found Qt version 4.7.2, using mkspec qws/linux-arm-chumby-gnueabi-g++ (Desktop)”. It’s inaccurate in claiming that this is a desktop target, but it’s ok — it still works. Click OK to save your new Qt version.
Creating a test app
I didn’t want to turn this into a tutorial on how to use Qt, but I at least want to walk you through creating a basic app, cross compiling it, getting it onto the chumby, and running it. Click on File and choose New File or Project…. Leave Qt Gui Application highlighted and click Choose…. Name your project TestChumbyQt and save it wherever you’d like. When it asks you to set up targets, uncheck all the targets except for the Chumby Qt target we created earlier. Click Next and leave the QMainWindow class name alone (MainWindow is fine for this example). Accept all the default choices.
Now we are ready to create the test app. Expand the Forms folder on the left and open mainwindow.ui. Click on the background of the newly-created window. Next, on the right side, there should be a “geometry” property. Click the + to expand it. Change width to 200 and height to 150. This should shrink the window a bit. On the left, drag a push button onto the main window. You can resize it to make it easier to press if you’d like.
Compiling the test app
Ok, we’re ready to compile the app. Go to Build and choose Build Project “TestChumbyQt”. If it asks you to save, click Save All. You should see a progress bar on the left side and eventually it will turn green, meaning the compile succeeded.
We’re ready to go. I put my test app in ~/Documents, so your command may be slightly different from mine:
scp ~/Documents/TestChumbyQt-build-desktop/TestChumbyQt root@MyChumbysName.local:/mnt/storage
Environment variables
Now, we need to define a few environment variables in order for Qt to work. You will need to do all of the exports we did above when we were testing out tslib, so if you still have that shell open you can skip doing those again. I’m listing them again just in case, though, plus some extras you need to do regardless.
export LD_LIBRARY_PATH=/mnt/storage/tslib/lib:$LD_LIBRARY_PATH export TSLIB_TSDEVICE=/dev/input/by-id/soc-noserial-event-ts export TSLIB_CALIBFILE=/mnt/storage/tslib/etc/pointercal export TSLIB_CONFFILE=/mnt/storage/tslib/etc/ts.conf export TSLIB_PLUGINDIR=/mnt/storage/tslib/lib/ts
And the ones you need to do:
export QWS_DISPLAY="LinuxFb:mmWidth80:mmHeight52" export QT_QWS_FONTDIR=/mnt/storage/lib/fonts export QWS_MOUSE_PROTO=tslib
The QWS_DISPLAY variable tells Qt about the physical dimensions of the chumby’s screen in millimeters. I did a very crude measurement with a ruler. If you don’t specify it, all the text will appear tiny because we’re working on a small screen. QT_QWS_FONTDIR tells Qt where to look for its fonts. Because it thinks it’s stored in /usr/local/Trolltech, we need to specify this so it knows where it should actually look. Finally, QWS_MOUSE_PROTO tells Qt to use tslib rather than the generic Linux input system.
As I said earlier, you will want to make a file you can source that includes all of these environment variable definitions — they are necessary in order to start a Qt app. Notice I didn’t add /mnt/storage/lib (where we put the Qt libraries) to LD_LIBRARY_PATH — the reason I didn’t is because the chumby already has that directory in its pre-supplied LD_LIBRARY_PATH, so I didn’t want to put it in there twice.
Running the test app
We’re ready to go now. In an SSH session to the chumby, type:
/mnt/storage/TestChumbyQt -qws
(-qws tells TestChumbyQt to be a window server. If you run other apps while it’s running, you don’t want to pass -qws to them–only one app should be the window server.) You may see an error about being unable to open /etc/pointercal, but that’s okay–our environment variable tells it to look elsewhere anyway. If all goes well, your window should appear on the chumby’s screen. Try pressing the button and dragging the window around. Everything should work! You can quit the app by pressing control-C in the terminal, or tapping the X in the upper right corner of the window.
Congratulations, you now have Qt working on the chumby. There are ways to get a Qt app to run at bootup now rather than the flash-based GUI, but that’s beyond the scope of this document. All that’s important is now you know how to run Qt apps.
All done
Unfortunately, I just don’t have the bandwidth necessary to host any binaries or source code for this. However, I have (hopefully) provided you with all the info you’ll need to create the libraries yourself. I have not yet figured out how to make a Windows-based toolchain for this — I know how to create the ARM cross compiler for Windows, but I just don’t know how to get Qt to compile inside of Windows. If I ever discover how, I’ll make another post about it. So for now, you will need a Linux environment for your development.
I know some of what I did was a little crude — I should have specified the ultimate destination directory for tslib when I configured it with –prefix, and I also probably could have specified in the configure script some of the paths that I had to redefine with environment variables, but this works for now. There’s nothing special about /mnt/storage, by the way — I just picked it because it was easy to put stuff there. It might be better suited for a special directory in /usr or something along those lines.
If you run into any problems, feel free to leave a comment and I’ll try to figure out what’s wrong. Enjoy!