Cross-compiling for OpenWrt with various programming languages

I have been messing about with the Nim programming language quite a lot recently. Because it compiles to C code, this means that it's very good for programming lots of devices - since most everything has a C compiler. I have to say that I am drawn to Nim's main goal of being efficient, because I dislike bloat. However, Nim has some disadvantages, and for me the biggest is that it has not reached a v1 release yet. So the language is not as stable as it could be, and I get nervous each time a new version of the compiler is released, wondering if my code will still work.

In fairness, Nim is quite stable, I don't want to be misleading. But until it reaches a version 1 release I will continue to be cautious. I would not use Nim for important work (yet) - although I am using it for hobbyist type stuff. And I've been enjoying it too.

But, in recent weeks I thought that it would be only fair for me to try out some other languages. So I've been doing some experiments on my linux box, mostly inside docker containers. Cross-compiling for embedded linux is high on my personal list of requirements, so I have been trying to target the latest stable release of OpenWrt (15.05.1) running on the Ralink RT5350F chipset. This can be done very easily with Nim. So, I tried that with a few other languages to see how I got on. Thank goodness for docker, it has made this process so much easier and when I'm finished it's very easy to tidy up my mess!

1. Go

At first, I thought that it was going to be easy with Go. It looked as simple as this command:

~# GOOS=linux GOARCH=mipsle /usr/local/go/bin/go build -v hello.go

Which looked all-good at first glance:

~# file hello
hello: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), 
statically linked, not stripped

But when I ran it on my device, I just got this 'Illegal instruction' message:

~# /tmp/hello 
Illegal instruction
~# 

I then found several issues on the golang GitHub repo about MIPS32 support. So it looks like the easy way is not so easy after all. Anyway, I did eventually get a 'hello world' program cross compiled for OpenWrt with Go. And this time I was able to run the executable on my target device. In the end I followed the first set of these instructions. However, the resulting binary was 1.8 Mb just for the hello world program. At this point I decided to give up, and by that time I had read posts from other people saying that cross-compiling Go code for embedded devices resulted in binaries that were big and slow. Maybe in the future things will get better. I hope so.

2. Rust

It appears that Rust would be easier for cross-compiling to OpenWrt if I was using the trunk version of OpenWrt, because support for the target mipsel-unknown-linux-musl used by OpenWrt trunk is much better than mipsel-unknown-linux-uclibc which I need for the stable release (which I'm running). So I expect that this will become easier in time. In any case, this seems to work:

~# rustup target add mipsel-unknown-linux-musl
info: downloading component 'rust-std' for 'mipsel-unknown-linux-musl'
 15.3 MiB /  15.3 MiB (100 %)   1.5 MiB/s ETA:   0 s                
info: installing component 'rust-std' for 'mipsel-unknown-linux-musl'

Whereas this version (with uClibc) doesn't:

~# rustup target add mipsel-unknown-linux-uclibc
error: toolchain 'stable-x86_64-unknown-linux-gnu' does not contain 
component 'rust-std' for target 'mipsel-unknown-linux-uclibc'

So, maybe I should just try that again when I've moved onto a later version of OpenWrt (or one of the LEDE releases perhaps).

3. Vala

I found the Vala language by accident, but it is apparently used a lot by the Elementary OS project - which gives it some kudos in my book. Additionally, the syntax is very much like C#, which means it should be somewhat familiar to me. Like Nim, it also compiles to C so I thought that support for embedded linux should be good. However, it depends on some libraries (like GLib2) which I don't have available on my device. I could probably make this work if I wanted, but I had little interest in doing that. So I didn't proceed any further.

4. Crystal

I had a very brief look at Crystal which does have some support for cross-compiling. It looked interesting, although I found some of the syntax to be a bit strange in places. However I don't think it supports the target device I am looking for. But I did get this far, purely out of interest:

~# crystal build hello.cr --cross-compile --target "x86_64-unknown-linux-gnu"
cc hello.o -o hello  -rdynamic  -lpcre -lgc -lpthread 
/opt/crystal/src/ext/libcrystal.a -levent -lrt -ldl -L/usr/lib -L/usr/local/lib ~#

I didn't go any further than that, but it was interesting to have a little play.

In summary...

It actually looks like Nim is a pretty sensible choice for what I'm doing. In comparison to other languages it's amazingly simple to set up for cross-compilation. So maybe I just need to put up with any minor instabilities in syntax that are likely to appear. But it is a good reminder that I need to try Nim on OpenWrt trunk (or one of the LEDE releases), because they've moved from uClibc to musl. I don't know if that will introduce any issues. I need to come back to that...

At least in the short term I am happy to keep on messing about with Nim. And I'm happy that I have at least tried some alternatives. I will try to keep my eye on Go and Rust, both of which showed signs of promise. When I have a device running OpenWrt trunk, or LEDE, then perhaps I will give Rust another try.

Building a tiny docker image

For a while, and purely for fun and experimentation, I've wanted to create a Docker image which does something useful and isn't huge. I knew that it's possible to use gcc to compile C code with the -static flag and then run it standalone inside a Docker container, but I'd never actually tried it. It's just one of those things I wanted to do. It means you can build your Docker image from scratch and not need any other OS files from another distribution. I was aiming for something that could be measured in kilobytes rather than megabytes.

So ... I decided to try and build my mini-webserver, dweb with the -static flag, then pack it down with upx before setting it up to run in Docker. I thought that I could get it to run the webserver in the container, but serve up files from a location on the host machine. I have no real need to do that, but I thought that it would be interesting. I'm not suggesting that this is a really good idea, but I do like the idea of avoiding bloat in docker images.

The result is on Docker Hub here and GitHub here. The compressed size of the image comes in at just 364 KB. That makes a nice change.

Well, that was fun. Perhaps one day I'll use this technique to do something useful, but at least I know how.

Another HooToo HT-TM02

A while back, my wife noticed that the HooToo HT-TM02 was on a special deal at Amazon. So she bought me another one. This is the second one I have. The one I got before is in daily use as an internet radio and is doing a fantastic job. But the problem is that I don't want to mess about with it, because I might break my radio! So this *second* one gives me something to actually play about with...

As before, I immediately installed OpenWrt on it. The installation procedure involves adding some extra storage which gives some additional space during the install process. So you need to plug in a correctly formatted USB thumbdrive. The instructions advise you to check whatever USB storage you have added is working before proceeding. But when I did it this time I forgot to check, so there was a slightly tense moment! Anyway it must have been fine, because the install went without a hitch. Phew.

Then I upgraded to the latest released version of OpenWrt (which was Chaos Calmer 15.05.1) and reset it to the default configuration. I like to work from a clean starting point. So this gives me a good hackable little linux toy to play with. I expect that most of my effort will be continue to be programmed in Nim and cross-compiled.

So here are my notes on configuring the basics when setting up a HooToo HT-TM02 with OpenWrt. This should make it easy to repeat if I need to do a factory reset. It assumes that there is a home router with an address of 192.168.1.1, so works around that. Obviously, it assumes that OpenWrt has already been installed on the HooToo. The aim is to have the HooToo connecting to an existing wifi network, so you can get to it wirelessly, and it can download packages and updates. But also, a network cable still works if required. Here goes:

  1. Start with OpenWrt defaults. Connect with a network cable and get an IP address from DHCP
  2. Don't connect to any other networks, because it will clash with the router
  3. Now point a browser to 192.168.1.1
  4. Log-in without a password and then set a new password straight away
  5. Edit the LAN address of the HooToo to 192.168.0.1 but leave DHCP on
  6. Save and then reboot the HooToo, it now should not clash with your router
  7. After the reboot, unplug and re-plug the network cable, just to be sure
  8. Point your browser to the new address, 192.168.0.1 and log in again
  9. Now, scan for your own wifi and click "Join Network"
  10. Enter your wifi passphrase
  11. Assign the wifi connection to the firewall zone "LAN", but otherwise, use the defaults and save
  12. Check the HooToo has an internet connection (eg go to: System->Software and click "Update Lists")

Connecting via a cable should now give a DHCP address in the 192.168.0.x range, so using a cable is also possible in case wifi is not an option. For me, this is a pretty good starting point; now the fun stuff begins.

Retro terminal on macOS

In the February 2017 issue of Linux Magazine (in the Linux Voice section), there was mention of Cool-Retro-Term. It gives you a terminal emulator that tries to duplicate the experience of using an old cathode ray tube monitor. But I also noticed that it comes with a dmg for installing on macOS.

And since I like a bit of retro technology, I couldn't resist trying it out. So I went a put a copy on my MacBook Pro. It really is quite cool looking, and visually reproduces an ancient monitor quite well. It even has the bend at the edge of the screen. Personally, I've turned off the glow line feature because I found it distracting. I have also made the font smaller, so that I can get more text on the screen - that's probably cheating - but it made the terminal more usable. Currently, I'm using the Apple II font from 1977.

Here I am using it to mess about with streaming radio from Madplay on one of my routers running OpenWrt:

It was really simple to install on macOS, and it really does faithfully reproduce the monitors from the old days. It's good nostalgic fun.

Upgrading to the latest release of Nim

I have had some fun in the last few of days. I decided to upgrade to the latest version of Nim ... because I had gotten a couple of releases behind I think. It is not a hard thing to do, but was just something that I had not gotten round to.

Anyway, for some reason, I decided to rebuild everything, including the Aporia IDE whilst I was at it. That's where things went a bit wrong, and sadly I had already binned the previous version of Aporia. So I was at risk of not having my IDE for Nim.

Whenever I tried to use Nimble (Nim's package manager) to rebuild Aporia, I was getting errors about dependency conflicts with GTK. Eventually, I grabbed the Aporia repository and was able to hack the aporia.nimble file. If you're really interested, I found that removing #head from where it says it requires gtk2 did the trick. Finally, I was able to build the latest Aporia. Phew, panic over.

After having figured all that out, I was looking at the Nimble repository on GitHub and noticed this build failure:

So ... it looked like bad timing. Not being able to install Aporia was being worked on. This must be due to the Nim incarnation of dependency hell I guess. But I can now report that this problem has been fixed anyway. It seems that I had just chosen the wrong moment to do my rebuild. It happens.

After a little more digging and some discussion on GitHub, it appears that I had been using a bleeding-edge copy of the Nimble package manager. So it's understandable that things might break. When I retraced my steps, I realised that I had installed Nimble from a script included with the release of Nim I had been using. That script installs the bleeding edge version, which is probably the wrong thing to do. So I logged that as an issue on the Nim repository. But it seems like the next release of Nim is just round the corner anyway. I'll try to upgrade to that version more quickly this time!

So, for the time being all is well. I have the latest release of Nim installed and Aporia is back. I've also replaced Nimble on my machine with the latest tagged version (manually), so in the meantime I should have a more stable setup. And I kind of enjoyed all this stuff anyway, so no worries.

Wifi problems with Elementary OS Freya on the ideapad 100

This week I had a problem where wifi suddenly stopped working on my Lenovo ideapad 100 15iby running Elementary OS Freya. I don't know what happened, but suddenly the machine refused to work with any access points. It would say I was connected, and I would have an IP address from DHCP... but there was no actual connection working. Chrome would just complain there was no internet, for example.

I ended up having to rebuild the Realtek rtl8723be wifi driver from the source repository here: https://github.com/lwfinger/rtlwifi_new/ but actually that was not too bad, because the instructions are quite simple. I just had to follow this post which seemed to work fine for me. But I did need to disable the sleep feature of the driver, which is mentioned as an extra step in the instructions. Otherwise the connection would keep dropping and then prompting me for my wifi password all the time.

So that was a slightly annoying problem! But I am pleased to have it sorted out. As I'm writing this I'm downloading a 750Mb file to make sure it's all OK. I'm currently at 500Mb and all is fine, so hopefully that's a job well done.

I have also noticed that Elementary OS Loki is now released, but there is no upgrade route from Freya, so I need to find some free time to do a backup and clean install. Hopefully that will be a smooth process... maybe I'll try running it 'live' first, without installing, to make sure that things work beforehand. That's a job for another day.

Vanishing mouse on my Lenovo ideapad

One problem I have found with my new Lenovo ideapad 100 15iby running Elementary OS Freya is an issue with the mouse pointer vanishing, as reported here. It seems that if you lock the machine, close the lid, or let it go to sleep, then after signing back in there is no mouse pointer. It's annoying because neither the trackpad or an external mouse seems to work.

Anyway, the solution which seems to work for me is just to replace Light-Locker with Gnome Screensaver. It means that the lock screen is not as pretty looking, but I'd rather have a mouse that works!

It's an easy fix and worth it, although it looks like future updates won't suffer from the same problem. So I look forward to trying that out when I upgrade to Elementary OS Loki. But I'm waiting for it to be released rather than using the beta.

New laptop for Linux experiments

So my trusty old Toshiba Satellite T130 has started to fail. The keyboard has started to go wrong, it seems to miss out random keystrokes, which is really annoying. It's a shame, because apart from that the machine is still working fine. But I decided that it was time for it to be replaced. I didn't want to spend very much money on a new laptop, because the old Toshiba was running Elementary OS very nicely. So I didn't need a super fast processor or anything. I ended up going for the cheapest laptop with 4GB RAM that I could find. So I am typing this on a Lenovo ideapad 100 15iby. It was less than £190, which is pretty reasonable I think. So far I am pretty happy with it.

This machine does feel cheap. It seems very plasticy and the touchpad buttons rattle a little bit as I type. I don't think that it is as robust as the Toshiba, but for the price it seems like good value. It only has a Celeron processor, but it still seems to fly along when running Elementary OS. When I first got the machine, it was running Windows and that was a slightly more painful experience. Although I only kept Windows for a few minutes - just long enough to apply the firmware updates from Lenovo so that I had the most recent BIOS. After that, I blew everything away and installed Elementary OS Freya from a DVD (the ideapad does have an internal DVD drive). I don't have anything against Windows, but I don't think that I'd be happy running Windows on this laptop - I think I would find it too slow on this hardware.

I had no problems installing Linux, everything worked right out of the box - even the special volume and brightness buttons on the keyboard.

After installing the OS, the next things I installed were:

  • Docker
  • Kate / Konsole (for coding in C)
  • Spotify
  • Chrome (and the Chromecast plugin)
  • Nim / Nimble / Aporia
  • FileZilla
  • TLP in the hope of getting more out of the battery

The ideapad version I've got does not appear to have bluetooth (I think that it's an optional extra on this machine) but I managed to find a tiny Bluetooth dongle on Amazon which seems to do the trick. So anyway, let's see how it goes!

A little more about Nim

I am still tinkering with the Nim programming language, and quite enjoying it. I find the language to be well thought out, and it's very easy to build little libraries which have unit tests included in them. I like that idea.

But I have noticed that the binaries can be quite a lot bigger than my equivalent C programs, which I suppose is to be expected. So I have also been experimenting with the UPX packer which helps to keep the file size down ... this could be useful if you want to run your Nim programs on something tiny, like a router.

In the end, I did buy a copy of the Nim in Action book, available through Manning's early access program. I'd recommend this book to people who are learning Nim, I found it easy to follow and enjoyable to read.

Nim in Action book

Because Nim has not reached v1 yet, for the moment I'm only using it for experimentation and hobbyist type stuff. But when it actually hits v1, I think that I'd consider using it for real work too.

Updated!

I have just migrated this blog to a newer version, so hopefully everything will work and all the old content will have been transferred over...  And I should be able to continue blogging again soon ... as long as I can tear myself away from learning Nim.