Monday, December 29, 2014

Wifi on the BeagleBone Black with Systemd

EDIT 2015-01-01: What I thought was the last word in getting wifi going on the Beaglebone Black was missing one key piece of information.  Read this addendum, or you're screwed.

Unlike most of my posts on this blog, the title of this post is neither clever nor funny.  I'm feeling serious today.  Why?  I have a Rev B BeagleBone Black (BBB) and I have a love / hate relationship with it.  And I suspect I'm not the only one.
It's Not Me - It's You
I love that it is a hell of a lot more powerful than the Raspberry Pi in all aspects except probably for its graphics: it has a faster processor, more RAM, onboard flash, more I/O, and the incredibly powerful PRUs.  The new Raspberry Pi B+ certainly helps level the field with more I/O than the original B and four sweet USB ports, but for raw power the award still goes to the 'Bone.

On the other hand, I hate that the 'Bone can be such a bastard to get working to its full potential.  One problem is the Imagination Technologies SGX530 graphics core.  This company is open-source hostile, and their closed-source graphics binary blob works only with certain kernels.  All attempts to reverse-engineer an SGX driver have unfortunately failed.  The Raspberry Pi has both more powerful graphics hardware and Broadcom is (amazingly) supporting the development of an open source driver.  Incidentally, the upcoming Beagleboard-X15 will also have an SGX graphics core in it, and they will not be getting my money for this reason alone. Huge win for the Pi here.

The other problem I've had is with getting wifi going.  One dongle was crashing the kernel.  I wasn't the only one having problems, either.  I spent a ridiculous amount of time on this in the fall with no luck.  I thought the best way to fix the problem was to just drive over the 'Bone with my car and get a Pi.

But I'm stubborn.  I've got some time over the Christmas break and I thought I'd give it one more shot.  Amazingly, I seem to have something working using the Jessie snapshot I got from here named BBB-eMMC-flasher-debian-jessie-lxqt-armhf-2014-12-19-2gb.img.xz and running kernel 3.14.26-ti-r42.  I also learned a bit about systemd along the way.  Systemd is fairly new to Debian, and that fact makes a lot of the wireless tutorials out there obsolete.  So how did I get wifi working on the Beaglebone Black with systemd?  Read on, McDuff...

First of all, get the latest Jessie snapshot from the link above and flash it to the eMMC (flash) memory on the 'Bone. I'm not going to go into the details of doing that, because lots of other people have. That is what Google is for.

OK, so you've flashed the 'Bone with the Jessie image and want to get wireless networking going. I'm going under the assumption that you have console access to the 'Bone, either via a USB to Serial connection or SSH over a hard-wired connection. No fancy Graphical User Interface stuff for me.

Next thing to know is that a lot of wifi dongles need some kind of firmware loaded into them.  My TP-Link TL-WN722N already has its firmware on the image linked above (from the ath9k_htc package) so no need to download anything in this case just yet.  Other dongles like those based on the ZD1211 or RaLink chipsets will need you to install a firmware package with the old "apt-get update; apt-get install... " dance.  Here, the dmesg command and good ole' Google are your friend, helping you to figure out what you need.

Now you've got a 'Bone with a dongle plugged in and its firmware loading up.  After that, the thing that trips people up is that recent kernels "soft block" (disable) things like wifi and bluetooth by default. A program called rfkill is necessary to enable them. rfkill is part of the image above (and because I politely asked Robert C. Nelson to put it in) so you should be good to go without having to download something over a hardwired ethernet connection first.

Let's kick things off by checking to see if the wifi is blocked, and enable it if it is.
root@beaglebone:~# rfkill list wifi
0: phy0: Wireless LAN
Soft blocked: yes
Hard blocked: no
root@beaglebone:~# rfkill unblock wifi
root@beaglebone:~# rfkill list wifi
0: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
Good start. "Soft blocked" changed from "yes" to "no" after we unblocked it.

Next thing we need to do is bring the wifi interface up using the ip command.  Note a couple things.  First, Blogger hates me.  The square brackets below are supposed to be angle brackets, but the Blogger editor gets confused when you switch back and forth between "Compose" mode and "HTML" mode (and yes, I tried coding in the HTML entities and no, that didn't work).

Before getting into that, how do we know the name of our wireless interface?  Assuming your dongle is plugged in, do...
root@beaglebone:~# ip link
1: lo: [LOOPBACK,UP,LOWER_UP] mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: [NO-CARRIER,BROADCAST,MULTICAST,UP] mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: can0: [NOARP,ECHO] mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
link/can
4: can1: [NOARP,ECHO] mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
link/can
5: wlan1: [BROADCAST,MULTICAST] mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
There we go.  My dongle is wlan1 and I'll be using that in the examples that follow.  I'll also strip out other interfaces in the output going forward.  Numbers I'd rather you didn't see have been replaced by "xx".

Now let's bring the wlan1 interface up and check its status.
root@beaglebone:~# ip link set wlan1 up
root@beaglebone:~# ip link
5: wlan1: [ NO-CARRIER,BROADCAST,MULTICAST,UP ] mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
See how the status changes from [ BROADCAST,MULTICAST ] to [ NO-CARRIER,BROADCAST, MULTICAST,UP ] once the link is up?

The next thing we'll do is associate the dongle to a wireless router.  This example is for the connection  to my router that is wide open.  If yours is not open or not WEP encrypted, you'll need to do something different like bringing wpa_supplicant to the party (or just skip ahead to the automatic stuff below).  My router connection is named Abby_Slow, so you'll have to substitute the name of your access point below.  The really important thing to note in the following is that older command line tools like iwconfig have been deprecated and will probably not work anymore.  The new tool in town is called iw.  We will first use iw query the status of the link, and then make a connection.
root@beaglebone:~# iw wlan1 link
Not connected.
root@beaglebone:~# iw wlan1 connect Abby_Slow
root@beaglebone:~# iw wlan1 link
Connected to xx:xx:xx:xx:xx:xx (on wlan1)
SSID: Abby_Slow
freq: 2437
RX: 10694 bytes (75 packets)
TX: 2029 bytes (13 packets)
signal: -72 dBm
tx bitrate: 15.0 MBit/s MCS 0 40MHz short GI

bss flags:      short-preamble short-slot-time
dtim period:    0
beacon int:     100
So now we have a link, but that isn't going to get us as far as we need to go...
root@beaglebone:~# ping www.google.com
ping: unknown host www.google.com
We need to use something called dhclient to get a DHCP address, like so...
root@beaglebone:~# dhclient wlan1
root@beaglebone:~# ping www.google.com
PING www.google.com (216.58.216.228) 56(84) bytes of data.
64 bytes from ord31s22-in-f4.1e100.net (216.58.216.228): icmp_seq=1 ttl=50 time=75.2 ms
^C
--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 75.251/75.251/75.251/0.000 ms
root@beaglebone:~#
The sweet, sweet ping of success.  There is just one more thing before we go automatic on this problem.  Some dongles have issues going into power saving mode.  Let's not take our chances and disable this.  Inspired by this post, create a file called /etc/udev/rules.d/wifi_power_save.rules and add this one long line to it (no line breaks no matter what it looks like below, please).
ACTION=="add", SUBSYSTEM=="net", KERNEL=="wlan*" RUN+="/usr/sbin/iw dev %k set power_save off"
Here is how to check it the next time you boot the board:
root@beaglebone:~# iwconfig wlan1
wlan1 IEEE 802.11bg ESSID:"Abby_Slow"
Mode:Managed Frequency:2.437 GHz Access Point: xx:xx:xx:xx:xx:xx
Bit Rate=5.5 Mb/s Tx-Power=20 dBm
Retry short limit:7 RTS thr:off Fragment thr:off
Encryption key:off
Power Management:off
Link Quality=20/100 Signal level=20/100
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:59 Invalid misc:48 Missed beacon:0
Now obviously you are not going to want to go through this whole process every time you boot the board.  This stuff needs to be automated.  But how?  One thing you could do is trawl the interweb and try to understand how systemd-networkd works.  Then, working from the sparse set of examples on the web, you could manually create some text files against all odds that whatever you come up with is actually going to work.  You could do that, because that is what I tried to do to start with.  I created a 20-dhcp.network file in /etc/systemd/network that should have automatically worked but didn't.  Be smart.  Don't do that.  Do this:
root@beaglebone:~# apt-get update
root@beaglebone:~# apt-get install network-manager
root@beaglebone:~# nmtui
nmtui is the NetworkManager Text User Interface bundled into the NetworkManager package and it is very easy to use.  Ugly, but easy to use.

Ugly, But Works Beautifully
You'll hear a lot more about nmcli, the NetworkManager Command Line Interface.  Don't even try using that unless you are a NetworkManager God.  Stick with nmtui and you'll be good to go in no time.  You can use it to setup connections to open and encrypted access points that will automatically connect up when the board boots.  No fuss, no muss.  It worked perfectly for me.

Until this started to happen...
root@beaglebone:~# [ 2282.574398] INFO: task wpa_supplicant:485 blocked for more than 120 seconds.
[ 2282.581864] Tainted: G O 3.14.26-ti-r42 #1
[ 2282.592586] "echo 0 } /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Then the 'Bone locked up solid.

FML.

I tried once or twice more and the dongle kept locking up hard after a short while, taking the 'Bone along with it.  This is where I started to think that maybe I've got a bad TP-Link dongle.  Fortunately for me, I have another dongle, this one based on a Zydas ZD1211 chipset that is also well supported in Linux.  It needs some firmware that currently isn't in the default image, so I rebooted the 'Bone and did the following before the board decided to crash again.
root@beaglebone:~# apt-get install zd1211-firmware
I plugged in the Zydas dongle and then rebooted the board so the kernel could detect it and load its firmware (might have worked if I hot-plugged it, might not have).  Then I ran nmtui again, set up the dongle, and the network has been working well since then.  But you better believe I'm keeping my fingers crossed.

Incidentally, some people have reported that some dongles work poorly when plugged directly into the 'Bone.  A popular fix seems to be plugging it in via a USB extension cable or powered hub to get a little separation between the two of them.  If you've followed this guide and still can't get things working, try giving that a shot.

This would be a good place to wrap things up with my usual "now go out and do something" line.  But heck, while I'm at this, I might as well throw in some other useful things I picked up along the way to get it all into one place.  If I don't put this stuff here, I'll just be looking for it myself later anyway.
How do you change the name of the dongle's device interface???  See here on the Archlinux wiki.

How do you check out NetworkManager's status?
root@beaglebone:~# systemctl status NetworkManager
How can you see what systemd is putting into its journal for NetworkManager?  (Tip: use the left and right arrow keys to move horizontally a half screen at a time to see long lines of text!)
root@beaglebone:~# journalctl -u NetworkManager
How do I reboot the 'Bone?
root@beaglebone:~# systemctl reboot
How do I power it off?
root@beaglebone:~# systemctl poweroff
Anyhoo, there you have it: a reasonably comprehensive and up-to-date tutorial on how to get wireless networking going on one of the more powerful little ARM boards out there.  Included at no extra charge was a bunch of related systemd stuff that was previously scattered all over the net.  Now get out there and make that 'Bone do something.

Friday, December 26, 2014

No Pressure - I Got This

Earlier this year, My Lovely Wife and I enjoyed a visit from My Evil Brother-In-Law, who also just so happens to work for Environment Canada.  Naturally he was interested in my Davis Weather Station and some of the hacking I've done on it.  But he was more interested in its barometric pressure reading of somewhere around mid-800 kPa.  That is a really low number that could mean only one of two things:
  1. My barometer was not working.
  2. We were in the eye of a hurricane.
The weather seemed pretty nice out, but I know from what little TV I watch that the weather can be deceptively nice in the eye of a hurricane, only to worsen when the eye moves on and you're subjected to the hurricane's nastier bits.  So we waited a while and the weather stubbornly continued to be nice.  Broken barometer it was.  Later my suspicions were confirmed when the barometric pressure would just show as a series of dashes.  No need to take cover under the stairs, for sure.
Broken Barometer Badness
Fast forward a little bit and My Evil Brother-In-Law decides to take up the invitation from My Lovely Wife and myself to visit us for Christmas.  My barometer is still broken, but we've offered our invitation a couple months in advance.  I've got time.  I would fix my barometer before the weatherman's arrival.

The first part of the process was figuring out how the hell I was going to do this.  Everything else on the station was working well, so I figured it was just a dead sensor.  But exactly what sensor are we talking about?  I figured this much out a while ago and documented it in this post.
The part is a Measurement Specialties 5534CM.  Applications include "Weather control systems" according to its product page (man, I'd love to be able to control the weather). Here is its product page, here is where you can buy it, and here is some Arduino code to drive it!
The cool thing about this part is that it has a digital interface rather than an analog one I had originally guessed at.  It also is factory calibrated.  Davis was smart and kept things simple: the amount of A/D they have to do in the console is exactly zero.  But there seems to be a price to pay for this simplicity: $27.27 for a single piece, to be exact.  Good thing they are cheaper by the 1,000.
But there was a problem.  This picture was taken in my old console that has since failed me.  I got a new one and U84 above is not populated.  U85 is.
What is U85?
Good question.  What is U85???  I figured after a bit of research that it must be a minified version of the 5534CM: the 5540CM.  Here is where you can buy it.
You can, but...
Note I said you "can buy it" there, but I didn't say you "should buy it" there.  The price of this little bastard is $32 plus shipping that was going to put me out over $40 total.  Looking good in front of My Evil Brother-In-Law was one thing, but $40 is another.  What to do?  Well, Digikey is a well known name in the electronics components biz, but so is Shenzhen Kesun Electronics Co., Ltd.  Actually, no they aren't.  But their price and their shipping was about a third that of Digikey's.  Here is where you should buy it.  Take my money!  All $12 of it.
The Price is Definitely Right
The only price to be paid for not paying much of a price is time: I ordered this on November 12th and got it December 19th.  It was obviously on the slowest of the slow boats from China, but I do feel Canada Post was at least somewhat to blame.  The good folks at Shenzhen Kesun Electronics Co., Ltd. shipped within two days of my order and gave me a tracking number to boot.  However, that tracking number was never updated again by Canada Post.  Even after I received it, the order showed that it was still inbound from China.  I think my little sensor somehow got lost in the Christmas parcel rush.

So I had the What part of the equation covered.  Equally important is the How.  How do you get the old part off and the new one on?  Any time I had ever tried to take a multi-leaded part off a board like this with just a soldering iron and desoldering wick (a fine copper braid that soaks up solder like a sponge), it always ended in heartache.  You never get all of the solder, and you can't get enough heat around all the part at once to keep the solder molten to have the whole thing come off.  In the end, I would end up just wrecking what I was trying to remove.

That's why I got me a hot air rework station.  Which one?  This one.
Dave Jones Has Many Fine Traits.  Brevity is not One of Them 

Actually, mine is a Yihua 858D.  Same difference.  There are a lot of combo units that combine a soldering station with hot air rework, but I already have a decent iron.  This unit is small, it is quiet, the temperature is well regulated, it comes up to temperature very fast, and it has a neat cooldown function.  Best of all it is cheap.  Better of all, it works great.  I'm not sure how I lived without it.  I got it from here for $79.99 with Free Shipping and a spare heater element.  Nice.  This is my new favorite toy: you can take my hot air gun from my cold dead hands.

I got the What.  I got the How.  The Who is me.  The Where is here.  The When is now.  I got a two week Christmas holiday.  Time to get busy.  Open the case.  Set up on something that could take an accidental blast of heat (in this case, my stove), and put some aluminum foil around the surrounding parts to keep the heat off them to prevent their inadvertant removal.
Preparing for Battle
I set my trusty hot air gun to 340°F and held the nozzle just over the part.  The solder melted after maybe ten seconds and the part was off.  No fuss, no muss.  I freaking love this thing.

The next part of the operation did not go smoothly.  At all.  The plan was to use my soldering iron to solder the new part into place.  I took the new sensor out of its package, applied a bit of flux to the circuit board with a flux pen (always use flux when soldering), and placed the part on the board.  The first problem I had was the part did not want to line up well on the board: the part had some solder on each of its eight pads on its underside.  The circuit board also had a bit of solder on the eight copper pads receiving the part.  Placing the part on the board invariably caused the part to slip out of place.  My first attempts to solder the part in place were met with total failure: the part would be misaligned, I'd get a solder bridge, I'd get a missing connection... and all of my attempts had the part seemingly sitting on top of the board rather than being fastened to it.

A good night's sleep made the answer clear.  As so many instruction manuals say, installation is the reverse of removal.  I was using my soldering iron to attach the new part when I should really have been using the hot air station.  I had been leery to try this because I thought the heat might damage the new part.  But thinking about it some more, these boards are usually built using some kind of wave soldering machine that basically heats the whole board with all parts attached in an oven brought to temperatures above that of the solder's melting point.  That is basically what the hot air station does.

So, I laid down the aluminum foil in the area again and hand placed the part as best I could after having put a little liquid flux in the area.  I set the fan speed of the air gun about halfway and again set the temperature to 340°F.  I slowly lowered the nozzle of the gun directly over the part, trying hard not to blow the part off to one side.

What happened next was beautiful.  The solder around the part melted and the resulting surface tension moved the part perfectly into position.  The part also visibly pulled itself toward the circuit board and was no longer just kind of sitting on top of it.  Huzzah!

I let the area cool down a bit and then used a Q-tip and some isopropyl alchohol to clean the residual flux away.  I put it all together and...
Victory Is Mine!!!
The console display is 1020 hPa, or 102.0 kPa, a value that checked out nicely with the value reported from the nearest airport.  Better yet, the repair passes muster with My Evil Brother-In-Law.  Now all I've got to do is keep him on a steady diet of good food and good booze for the rest of his visit.  It's all good.

And by the way, Merry Christmas and a Happy New Year from all of us here at Mad Scientist Labs.