Computing stuff tied to the physical world

RF transport independence

In Software on Feb 20, 2010 at 00:01

With the basic serial interface access and dispatch in place in JeeMon, it’s time to move on to JeeNode / JeeLink packet processing.

What I want is to be able to forget all about how readings got to JeeMon. It shouldn’t make a difference whether I’m using a directly connected Room Node and grabbing the readings off its serial USB connection, or whether they came in through the air via the RF12 wireless driver – or by any other means. Take a snapshot with your cell phone, send the picture to EverNote, have it OCR’d, and then grab the readings off the web … whatever!

The way I’ve tied this into JeeMon, is to let the interface to RF12DEMO act as de-multiplexer. This is purely a decision at the RF12DEMO listener level. Each incoming packet is examined to determine which node it came from. Then we need a way to map from nodes to listener class – i.e. find out what sketch is running on the remote node. This is hard-coded for now:

Screen shot 2010-02-18 at 22.57.41.png

What this does is actually a bit more elaborate: a RF12DEMO listener will set up and manage a bunch of extra “RF12_PacketStream” listeners, one for each node. When packets come in, they will simply be dispatched to the corresponding stream. Each packet stream can process its packets in a different way.

The fun part is that these packet streams can use the same listener classes as with direct serial interfaces. All they need is an extra “decode_RF12” method:

Screen shot 2010-02-18 at 23.02.04.png

The task of decode_RF12 is to re-cast the incoming packet as messages of the same structure as what would come in over a serial connection.

Here’s the “rooms” listener as example:

Screen shot 2010-02-18 at 23.04.34.png

This one class encapsulates all the protocol details of room nodes, both serial and via RF12. When a 4-byte data packet comes in via RF12 (as $a..$d), the bits are decoded and an “onMessage” call is generated with a “ROOM” message id and the 5 decoded readings.

Here is a log of this code in action, one message per line:

Screen shot 2010-02-18 at 22.37.15.png

The way to read these messages is as key-value pairs, i.e. id = OK, type = RF12, name = usb-A900ad5m, etc.

The first two lines show an incoming OK message from node 21 (53=21+32), which is then turned into a ROOM message, tagged as coming from the “rf12-868.5.23” packet listener.

The next 3 lines are more involved: first an EM10 message came in over USB, then an OK message came in which got dispatched again, as the same EM10 message. That’s because I’m running JeeMon with a direct connection to the ookRelay board, even though it transmits all its information over RF12. So everything from the ookRelay comes in twice (great for debugging).

The point is that the two EM10 messages have the same info. It no longer matters how the message got here (but it is traceable, using the remaining details). And all the code to accomplish this is in a single source file, right next to the sketch running on the ookRelay board.

This design makes it possible to develop an application using only the serial USB connection, and then later add logic to send the information via RF12 (or not). Infrared, XBee, Twitter, anything: transport independence!

Note that nothing is done with these decoded messages at this stage. This messaging framework is independent of higher-level application decisions, such as where to store / send / show msgs, or even when to process them.

  1. This is shaping up, nice :)

    Some thoughts on the transport independence wrt. radio:

    The contents of the RF12 packages are still dependent on the type of the JeeNode talking over the radio. These can be a room, meter, ook or whatever else exists out there. Now, in my case, I want to monitor electricity, so I use or adapt the meter sketch and use its payload structure for this purpose. While I already have a JeeNode at the meter, I’ve added a temp sensor there (I had some tmp101’s lying around so I used one of those). Now, my node doesn’t fit any of the standard ones.

    Two ways from here: 1) Make my own node type -> I need to adapt the code on the server side, which I’d like to avoid in this case. 2) Make my node act as if it was two separate nodes -> this probably requires some hacking on the radio side of stuff to manage ACKs correctly.

    Have you thought about this scenario?

  2. You could make the node send packets out under two different node id’s, but it seems like a very awkward hack to me (and acks will be tricky). If each sensor has to act as a separate node, you may soon run into the 30-node-per-group limit, and you’d be sending a lot more packets around than needed.

    My suggestion would be: append the extra temp reading to your payload, i.e. send a bigger packet. Then create a new “meter_MK” interface, as a subclass of “meter”. Add a TEMP method, for your new reading. And override “decode_RF12” to 1) generate a TEMP call, 2) call the superclass’s “decode_RF12” (with the extra payload data omitted) to handle all the other bytes, i.e. meter readings.

    IMO, that would map quite accurately to what you’re doing while not having to copy any existing code to re-use it.

    So yes, you’d have to adjust the server code, but only as overrides – extending what exists, not altering it.

    (Update – edited some errors out)

Comments are closed.