The Quest for the Particle Border Router

I wasn’t quite sure where to put this. I am sharing a Library, and this is also sort of a Project – but I’ve hit a few bottlenecks with the Firmware itself, and it seems to me that the Firmware is truly at the bottom of what’s stopping Particle from being a killer border-router solution for the IoT meshes out there.

The Quest for the Particle Border Router

I have been working towards implementing an 802.15.4 mesh border router using Particle hardware. I think that the Photon and the Electron are both excellent choices for this purpose. Much portable. Very drop-in. Wow! So MCU speed!

Of course, neither device comes out-of-the box with the hardware to interface with an 802.15.4 mesh. So we need to add a mesh radio.

AT86RF233 Radio

ATMEL makes an 802.15.4 radio called the AT86RF233, available as a $30 extension board.

http://www.atmel.com/Images/ATREB233-XPRO.jpg

9 jumper cables later, and you’re cooking!

The wiring setup is illustrated in a nicely formatted table in the readme.

Radio Driver

Of course, we could connect this radio to a wall socket for all the good it will do us. What we really need is a sequence of symbols that’ll make this chip sing.

To this end, I present my AT86RF233 802.15.4 radio driver for Photon/Electron (and Arduino). You can just search in the online IDE for “AT86RF2XX”. This is a much-simplified port of the at86rf2xx driver from the amazing RIOT project.

Here’s an example application that sniffs all nearby 802.15.4 (broadcast) traffic, prints that traffic, and counts up how many frames it’s received so far:

#include "at86rf2xx/at86rf2xx.h"

SYSTEM_THREAD(ENABLED);

int received = 0;

int interrupt_pin = D2;
int reset_pin = D6;
int sleep_pin = D5;
int cs_pin = D0;

void setup() {
  Serial.begin(115200);
  at86rf2xx.init(2000000, cs_pin, interrupt_pin, sleep_pin, reset_pin);
}

void loop() {
  if(at86rf2xx.events)
    at86rf2xx_eventHandler();
  return;
}

void at86rf2xx_eventHandler() {
  /* One less event to handle! */
  at86rf2xx.events--;

  /* If transceiver is sleeping register access is impossible and frames are
   * lost anyway, so return immediately.
   */
  byte state = at86rf2xx.get_status();
  if(state == AT86RF2XX_STATE_SLEEP)
    return;

  /* read (consume) device status */
  byte irq_mask = at86rf2xx.reg_read(AT86RF2XX_REG__IRQ_STATUS);

  /*  Incoming radio frame! */
  if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__RX_START)
    Serial.println("[at86rf2xx] EVT - RX_START");

  /*  Done receiving radio frame; call our receive_data function.
   */
  if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__TRX_END)
  {
    if(state == AT86RF2XX_STATE_RX_AACK_ON || state == AT86RF2XX_STATE_BUSY_RX_AACK) {
      Serial.println("[at86rf2xx] EVT - RX_END");
      at86rf2xx_receive_data();
    }
  }
}

void at86rf2xx_receive_data() {
  /*  print the length of the frame
   *  (including the header)
   */
  size_t pkt_len = at86rf2xx.rx_len();
  Serial.print("Frame length: ");
  Serial.print(pkt_len);
  Serial.println(" bytes");

  /*  Print the frame, byte for byte  */
  Serial.println("Frame dump (ASCII):");
  uint8_t data[pkt_len];
  at86rf2xx.rx_read(data, pkt_len, 0);
  for (int d=0; d<pkt_len; d++)
    Serial.print((char)data[d]);
  Serial.println();

  /* How many frames is this so far?  */
  Serial.print("[[Total frames received: ");
  Serial.print(++received);
  Serial.println("]]\n");
}

If you’re trying this out, please note that the default value for the library is to use the minimum available radio channel 11. If you’re using different channels check out the configuration notes in the readme on how to set them.

Example output:

[at86rf2xx] EVT - RX_START
[at86rf2xx] EVT - RX_END
Frame length: 39 bytes
Frame dump (ASCII):
A��#���Msg 5271: It is now 2931108343
[[Total frames received: 6]]

[at86rf2xx] EVT - RX_START
[at86rf2xx] EVT - RX_END
Frame length: 39 bytes
Frame dump (ASCII):
A��#���Msg 5272: It is now 2934108343
[[Total frames received: 7]]

[at86rf2xx] EVT - RX_START
[at86rf2xx] EVT - RX_END
Frame length: 39 bytes
Frame dump (ASCII):
A��#���Msg 5273: It is now 2937108343
[[Total frames received: 8]]

Those funky symbols in the frame dump are the IPV6 headers! The payload follows. I am emitting messages from a SAMR21-xpro running a RIOT application that sends a 6LoWPAN message every 3 seconds following the format

Msg X: It is now [millis at time of message sent]

I have also tested this using ContikiMAC on TI 2650 modules. That works perfectly as well – but of course we get lots of repeats due to the packet strobing that ContikiMAC implements.

Ok great! In short, this library is a perfectly valid hardware driver, in that we can make this radio do everything we want physically. It lets us read 802.15.4 traffic from other devices. We can also send raw data using functions like at86rf2xx.send(uint8_t *data, size_t len).

But I need help to implement this into the network stack!

We can send/receive raw data over this radio to other 2.4Ghz radios, but that doesn’t necessarily make it 802.15.4 traffic. Consider the following problems:

  • What about packet construction? (i.e. what machinery do we use to generate IPV6, 6LoWPAN, etc. headers and then manage a buffer for said packets? This is a purely software problem.)
  • Why is this so ugly? In other words, the user should not have to be making direct register read/writes in order to use a radio. Like the existing WiFi framework, these mechanics should be abstracted away, so the user really only deals with syntax at the transport layer – UDP, TCP clients, etc. But, this depends on the point above – packet construction & parsing. In addition, there is a need for some sort of internal messaging framework by which information can be passed from hardware interrupts into an actual driver thread that handles this! I have tried to get this to work but I found using new Thread() tends to interfere with my interrupts, and too many threads seems to make serial debugging well–buggy. I am thinking this is a mutex thing? Either way, it’s clearly a multithreading thing, and I dearly wish it were more obvious how to approach this in Particle.
  • What about RDC and more sophisticated MAC approaches such as ContikiMAC? That is another level of abstraction between the transport layer and the link layer, but very very important to many IoT mesh architectures where power is constrained for the nodes!

These questions clearly transcend the scope of just this particular radio driver. There is a need for the firmware architecture to be more extensible in terms of network interfaces. For example, even if I got a multithreaded driver setup – how would I route my IPV6 frames between my radio straight to the WiFi? I’m not interested in opening a new UDP or TCP connection, I’m literally just routing packets that are ready to go. It is not clear how that level of hardware access is accomplished in the firmware.

I hope that Particle’s engineers and the community here can help me answer/implement/contribute to the development of these solutions which are so necessary to using the Particle products as viable border routers.

Alternatives

I have also given serious thought to just throwing out the Particle firmware, and porting the Photon to RIOT (like the Core). This is a very attractive solution (especially for the portability & isomorphism between other micro controllers that RIOT offers, and the robust modularity of the network stack), but I really like Particle’s OTA and developer tools on the web side. I’d rather improve the Particle firmware to accomplish a border router, because that (more or less) buys us a GSM border router too. (Or at least makes a very large down payment.)

I have also seen some discussion surrounding porting the Photon to Contiki. Well, that would buy us ContikiMAC, certainly, but I’d really prefer to fold that functionality into Photon rather than vice versa, for the same reasons I just cited for RIOT.

I’ve also considered just staying on the train I’m already on, and pulling in the netdev implementation of this radio from RIOT as well, along with the dependencies on pkt.h machinery. But again, that doesn’t provide an answer to ContikiMAC integration, and I really feel that this kind of functionality is something that truly belongs to the core of the Photon (heh, no pun intended) and not a crude hack, port or add-on from another project.

Summary

If you’re someone interested in mesh networking, and especially using the Photon/Electron as a border router, please feel free to play around with my AT86RF233 library. I would appreciate any feedback on it (this is my first C++ class ever :wink: ) and would especially appreciate feedback and guidance to how we make the Particle firmware a real contender for multi-interface applications such as border routers by extending its multithreading, internal messaging, and network stack capabilities. I think a lot of people would find it to fill a vacuum that is conspicuously missing from the IoT world.

4 Likes