I got my hands on some Tradfri devices finally and made good progress talking to them. That's an unmodified Tradfri remote initiating a touchlink pairing with a kw41z board running RIOT-OS and a fork of the old open-source ZBOSS Zigbee stack that I've started updating to support modern off-the-shelf Zigbee devices like Tradfri.
I think this is the first time that open-source-only code has been used to do a touchlink pairing with an off-the-shelf Zigbee device, so I think it's pretty exciting. There's no Xbee module, no closed vendor-provided libraries, no third-party commercial Zigbee stack, no binary blobs at all. All of the code running on the microcontroller is accessible and modifiable, including 100% of the Zigbee functionality. This makes me super happy.
So I did say I was starting to add support. The reason I didn't show off toggling the light with the on/off button is that nothing after the touchlink pairing is implemented yet. But it's pretty cool progress and I wanted to share. Anyway that's all for now.
This is a different twist on an old Tradfri hack. Instead of talking to a Tradfri gateway via its community-documented CoAP API, what I wanted to do was implement a Tradfri gateway's CoAP API. (Actually, as you'll see, the idea in some sense is to delete the gateway altogether.) As we know, the Tradfri gateway talks CoAP over DTLS. Riot talks CoAP and DTLS. What could go wrong? Well, a lot could go wrong. That's what makes it interesting. That plus this bit about wanting to evaluate how close we're getting to fundamentally deleting or at least redefining one of the roles that an IoT gateway traditionally has served. More on that below.
The setup consists of a Raspberry Pi connected by uart to a kw41z-mini running Riot's gnrc-border-router example. The uart connection hosts a SLIP link between the Riot node and the pi so that the Riot node gets a world-routable IPv6 address and is routed to my home network through the pi's ethernet connection. The Riot node is then the router for a network of 802.15.4 wireless Riot nodes, each with its own world-routable IPv6 address. The function of the pi is really only to give the Riot node an ethernet connection. An obvious substitution would be an esp32, but as long as I'm doing this for academic purposes, a pi running linux is more convenient. More convenient still would of course be a Riot board which itself has ethernet or wifi, but there are approximately two problems with making such a board in proper open fashion which I'll rant about some other time.
Anyway, here the trouble has already begun, because the Tradfri app doesn't seem to support IPv6. Further, the way it finds a Tradfri gateway is via link-local mDNS. Link-local meaning the multicast won't get past the pi to the network of Riot nodes without adding more unwanted intelligence to the pi. Finally, the app is only meant to connect to one gateway at a time, which here means only one Riot node at a time.
It's that last part that's really unfortunate, or you could say it would become the limitation I set out to find. See, the idea is that we want all of our IoT devices to be IP-reachable by the devices from which we control them and with as little as possible happening in between. That doesn't mean directly that it's bad for a phone app to connect to only one gateway at a time. What it means is that we don't want any "special sauce" running on the gateway. The border router (gateway) should only do network stack things for the Riot nodes in the same way that a wifi router only does network stack things for laptops. It seems that it will take no less than this to get to a point where we have one type of border router that works with all 802.15.4 devices just as we now have one type of wifi router that works with all 802.11 devices. This ultimately is one of the major goals of moving IoT networks off of commercial protocols like Zigbee and Z-Wave, and onto IETF standard protocols like IPv6, UDP, 6lowpan, CoAP, RPL, and so on.
To that end, the link-local mDNS queries that Tradfri uses aren't the worst thing to encounter. We can rebroadcast that to the 6lowpan network and that's still fairly standard networking stuff. We can also deal with translating between IPv4 and IPv6 obviously. And in fact I did succeed in getting the Tradfri app to talk directly to a Riot node. But only one Riot node at a time, because the app expects to talk to one gateway, not directly to each light and dimmer switch. So the only way to have it work with multiple Riot node lights and switches would be to set up the border router Riot node (the one connected to the pi) to do the same thing the Tradfri gateway does which is manage the network of end devices using special sauce.
Actually, what I was hoping to find was that the Tradfri app was already talking to devices directly and that the Tradfri gateway indeed did only network stack things. After all, the Tradfri gateway can easily have enough resources to do heavier protocols than CoAP, so I thought why would they use CoAP unless they're talking directly to the end devices. But it isn't so. Their api requires that the gateway implement a CoAP endpoint that returns a complete list of its connected devices, and although you can probably multicast that out to the 6lowpan network, there seems an unavoidable need to have ingelligence in the border router to cache and assemble the responses from the 6lowpan network because the Tradfri app expects to receive a single reply containing a list of all of the devices in (as far as I can tell, although I haven't tried otherwise) a single CoAP response.
So then if that's the case, is it even useful to have the server side of Tradfri's gateway implementation in a Riot node? I dunno. Some things just beg to be done, and sometimes you have to do a thing just to see if you can. There's also the fact that I wasn't sure what limitations I would find until I found them, and finding them was actually the point anyway. Certainly it is always useful to test different network stacks against each other in any case. And sure enough, I found bugs to fix. Incidentally, I also found bugs in the Tradfri Android app (including one which makes it trivially easy for a bad player to crash any Tradfri app present on the local wifi) which I can't fix, because the Tradfri app is closed-source. I know, big surprise there, right?
I got some requests for instructions on how to flash the firmware on a kw41z-mini and this is long overdue. It flashes just like any other modern jtag/swd microcontroller that has good OpenOCD support, which is to say that the open tools we want to use are generally working well with the latest cortexm-whatever stuff we want to work with and life is pretty good right now. But if you haven't played in the 32-bit world yet then you may not have encountered OpenOCD yet, and it can take a lot of googling to figure out where to start with that. Here's a practical introduction, and of course, the oneliners you're looking for.
In this case I'm compiling RIOT-OS firmware and flashing it onto a board with a kw41z microcontroller, but this is how you could go about flashing any firmware onto any microcontroller supported by OpenOCD, which seems to be about everything in the cortexm world where all of the cool new IoT chips are.
I've been using raspberry pis in place of jtag adapters for a while and I've been incredibly happy with this setup. It's super convenient and I'm surprised more people aren't talking about how great it is. OpenOCD added support for using raspberry pi gpio pins as a jtag interface a while ago, so as long as OpenOCD supports the chip you're using then really all you need for a jtag interface is a raspberry pi. And it's like having a jtag interface on steroids that runs linux with builtin wifi and gdb and you can even compile your firmware on it if you want to. Seriously, how cool is that?
It turns out that Linux's 802.15.4 implementation is just about ready to facilitate promiscuous packet sniffing natively. With minimal patching, we can now use a Raspberry Pi and 802.15.4 radio to sniff raw 802.15.4 packets using Wireshark or
tcpdump or whatever.
Here I'll demonstrate the method I've come to prefer: Sniffing packets on the RPi with
tcpdump and sending them over wifi using
netcat to Wireshark running on another (less graphically laggy) computer.
Here I document a case of compiling on Lubuntu 14.04 and flashing with lpc21isp through the openOBC's micro-USB port. This is what I'd recommend as probably the most supported setup.
The same steps can work on a variety of systems, but I'm doing this with a fresh install of Lubuntu 14.04 in Virtualbox. A normal install will have just about everything we need out of the box, but we'll probably have to install an
arm-none-eabi toolchain and
lpc21isp, and we may have to install
Yeah, I finally got around to installing an oil pressure sensor in my E36 and trying it out with my openOBC. It was one of those really bizarre experiences where everything just worked without a fight.
The pressure sensor I used is a VDO 360-007. It's an 80 psi sensor with M12x1.5 threads and a warning light output at 8.5 psi. It fit perfectly in place of the factory pressure switch I replaced with it and the gauge output on it connects to the analog.in1 or in2 pins of the openOBC board, which are both set up by default to work with grounded resistive sensors like this.