Wednesday, January 30, 2013

Transparent serial emulation for the RFM12B radio module


For the AuroraWatchNet magnetometers that I am developing I currently use the Ciseo XRF radio module. They are based upon the pseudo-standard XBee footprint, operate at 3.3V and provide a UART interface to the microcontroller. They are easy to use but I have found the range is not always sufficient, typically limited to 60 metres of free space plus one exterior wall/window. Another commonly-used radio module is the HopeRF RFM12B transceiver, as used on the JeeNode. The RFM12B also operates at 3.3V but features an SPI interface to the microcontroller. It is available as a 2mm pitch surface-mount module, or as a 12 pin 2mm pitch DIP package.

As my Calunium v2 microcontroller development board can be fitted with a RFM12B radio module I naturally wanted to compare the performance of the RFM12B and XRF. Initial testing of the Hope RFM12B gave a usable range of 150 metres of free space after transmission through one exterior wall/window. Some of the additional range is due to using the 433MHz version of the RFM12B compared to the 868MHz operating frequency that the XRF defaults to. The range measurements were not very scientific and further work is required before meaningful conclusions can be drawn regarding the comparative ranges. However these initial results have encouraged me to investigate replacing the XRF by the RFM12B, particularly as it is both cheaper and uses less power. A comparison of the basic specifications can be found at

As I have already deployed a few magnetometers using the Ciseco XRF module I would like to to use the RFM12B as a serial device. This would enable the existing firmware to be used with minimal changes. It would also allow the possibility of having a single firmware support both XRF and RFM12B modules.

Serial emulation

I have created a serial emulation layer for the RFM12B by deriving a class (RF12_Stream) from the Arduino Stream class. Text or binary data can then be read from or written to the RFM12B in exactly the same way as the standard Serial port. Since it is a Stream object the Streaming class (which overloads the >> and << operators) can also be used with RF12_Stream. The RF12_Stream class automatically breaks up the data to be transmitted into short packets which are sent using the Jeelab RF12 library. ACKs and retransmissions are employed to prevent losing data from dropped packets. A packet number is included to prevent duplicated data, which could otherwise occur for the case when the ACK is not received and a packet is retransmitted. The class keeps track of the number of packets sent and received, and the number of retransmissions, data which may be useful to identify the optimal channel on which to operate.

The begin() function takes different parameters to Serial.begin() and it returns a logical value to indicate whether the RFM12B has been found on the system. The user is required to call the poll() function periodically so that outgoing packets are sent and received packets processed. These are the only differences compared to sending data to the standard Arduino Serial port.

Source code for the RF12_Serial emulation can be found on Github,; it is released under the MIT license. An example sketch is included which echoes data between the Arduino serial port and the RFM12B. When compiled for the ATtiny84 this sketch is less than 8kB so I am hopeful it can be used on the RFM12B to Raspberry Pi expansion board designed by Martin Harizanov. AsyncDelay,, and CircBuffer,, are also required.

Calunium uses INT2 for the interrupt from the RFM12B which is not supported by the JeeLabs version of the RF12 library so I am currently using a modified version. Source for this version is also available on Github, It is intended that RF12_Serial should work with both the original RF12 library and my version.

Single firmware to support RFM12B and XRF

By utilising the return value from the RF_Stream begin() function it becomes trivial to produce a single firmware which can support either the RFM12B or XRF radio modules. The trick is to create a Stream reference which is set to either Serial (for the XRF) or to rfm12b. The RFM12B_autoselect example sketch illustrates this concept.

How does it differ from RF12sio?

The RF12sio library overloads the << and >> operators so that packets can be constructed and processed easily. The user is still responsible splitting the data into packets. RF12sio is not derived from Stream so it is not possible to use the Stream pointer trick shown in the RFM12B_autoselect sketch.


Thanks to JeeLabs and JC Wippler for the RF12 library and to Arduino for the standard Arduino libraries.


  1. When compiling i got to errors RF12_Stream.cpp: In member function ‘bool RF12_Stream::begin(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)’:
    And :
    RF12_Stream.cpp:49:30: error: ‘rf12_set_irq’ was not declared in this scope

    1. You need to use my modified RF12 library, not the Jeelibs version.

  2. Hi Steve,

    i downloaded your library because I need a serial bridge for my existing 10 or more RFM12 modules to use them with my quadcopters for telemetry. I tried compiling the examples by copying all the required files from the libs you mentioned into the example folder.
    The first thing I see is "CircBuffer not declared" which seems a bit weired because CircBuffer.h is included from all files that requires it. I'm new to the arduino IDE so maybe you can help me out?

    My goal is to design a pcb with a USB connector, an FT232RL USB-2-Serial chip, a Mega88 and the RFM12 module to have a stand alone wireless transmitter that just works when you plug it in.

    Best regards,

  3. Ok, I managed to get that right - the folder names after downloading the zip file from github came with a "-master" at the end. This doesn't work. I also put all of the lib folders into the arduino library folder.

    Now that this works, I get this error message:
    SerialStream.ino: In function 'void writeEepromConfig()':
    SerialStream:221: error: 'eeprom_update_byte' was not declared in this scope
    SerialStream:222: error: 'eeprom_update_word' was not declared in this scope

    I found many people on the web with a similar problem but no solution, yet :(

  4. Updated Arduino to 1.6.0 ... compiles now.

    1. Glad you have got it working. I used v1.07.