RFM22B library for Arduino

Hi,

I’ve developed a custom mini-board containing an ATMEGA328P with internal RC oscillator and a HopeRF RFM22B wireless module. I’m currently doing some communication tests with the idea of developing an Arduino library for this module. Using the Arduino IDE, I’ve coded a very basic program that tries to read and write RFM22B’s registers via SPI at different addresses. My board is hooked to an USB/UART converter so I can debug the SPI comms anytime from minicom (Linux).

This is my current code:

/*
 * Testing comms between ATMEGA328P and RFM22B module
 */

#define RFM_OFF  9      // PB1 = RFM_OFF
#define RFM_IRQ  2      // PD2 (INT0) = RFM_IRQ
#define SPI_SS   10     // PB2 = SPI_SS
#define SPI_MOSI 11
#define SPI_MISO 12
#define SPI_SCK  13

#define PORT_SPI_SS  PORTB
#define BIT_SPI_SS   2


// SPI initialization
static void spi_Init()
{
  digitalWrite(SPI_SS, HIGH);
  
  // Configure SPI pins
  pinMode(SPI_SS, OUTPUT);
  pinMode(SPI_MOSI, OUTPUT);
  pinMode(SPI_MISO, INPUT);
  pinMode(SPI_SCK, OUTPUT);

  // SPI speed = clk/4
  SPCR = _BV(SPE) | _BV(MSTR);
}

// RFM22 initialization
void rfm22_Init()
{
  byte value;
  
  uint16_t res;
  
  // Condifure pins
  pinMode(RFM_IRQ, INPUT);
  digitalWrite(RFM_IRQ, HIGH); // enable pull-up
  digitalWrite(RFM_OFF, HIGH);  // switch RFM off
  pinMode(RFM_OFF, OUTPUT);
  delay(1000);
  digitalWrite(RFM_OFF, LOW);  // switch RFM on  
  delay(1000);
  
  // The following command should return 0x06 (Device version) but is returning 0x08 (Device Type, reg 0x00)
  value = spi_readReg(0x01);
  Serial.print("Reg 0x01 = ");
  Serial.println(value, HEX);
}

// SPI - Write value at a given register address
static void spi_writeReg(byte regAddr, byte value)
{ 
  bitClear(PORT_SPI_SS, BIT_SPI_SS);     // SPI_SS = 0 (Select RFM22B module) 
  delay(1);
  regAddr |= 0x80;                       // Set MSB: write mode
  SPDR = regAddr;
  while (!(SPSR & _BV(SPIF)))
    ;
  SPDR = value;
  while (!(SPSR & _BV(SPIF)))
    ;
  delay(1);
  bitSet(PORT_SPI_SS, BIT_SPI_SS);       // SPI_SS = 1 (Unselect RFM22B module)
}

// SPI - Read value from a given register address
static byte spi_readReg(byte regAddr)
{
  byte value;

  regAddr &= 0x7F;                       // Clear MSB: read mode  
  bitClear(PORT_SPI_SS, BIT_SPI_SS);     // SPI_SS = 0 (Select RFM22B module)
  delay(1);
  SPDR = regAddr;
  while (!(SPSR & _BV(SPIF)))
    ;
  SPDR = 0xFF;
  while (!(SPSR & _BV(SPIF)))
    ;
  value = SPDR;
  delay(1);
  bitSet(PORT_SPI_SS, BIT_SPI_SS);       // SPI_SS = 1 (Unselect RFM22B module)

  return value;
}

// Setup function
void setup()
{
  Serial.begin(9600);
  spi_Init();
  rfm22_Init(); 
}

// Main loop
void loop()
{
}

The following command taken from the above code

value = spi_readReg(0x01)

should return the contents of the RFM22 register with address 0x01. However, whatever address I try to read, the RFM module always return the value of the first register (addr = 0x00). I’ve even done some bulk reads starting from different addresses but the result is always the same: a list with the default register values starting from 0x00.

It seems like the RFM22 is not able to understand the address sent by the master. I’d tend to think that this could be due to the inaccuracy of the internal RC oscillator but SPI comms share the same clk line at both ends so this can not be the reason. On the other hand, I’ve tried with lower SPI speeds (clk/16, clk/64) without success.

I’ve looked into some examples found in the net for PIC’s, (even HopeRF’s) but they don’t seem to explain the behaviour of my board. Maybe some of the HopeRF users around herehave a better idea about how to focus this problem.

Thanks in advance for your help,

Daniel.

P.S: Congratulations to Sparkfun for their great website and forums.

You probably want to clear the MSB from the device’s address, not the register’s address.

I haven’t read the datasheet for your device, but usually the process for a read goes like this:

  1. Activate the slave device with the SS line.

  2. Send the device’s read address (slave addresses are 7 bit, the MSB is read or write.)

  3. Send the register you want to read from.

  4. Send a byte containing all 0s to shift the register value into your micro & read the byte.

  5. Release the slave device (the SS line again)

Also why the static on the readByte function?

And while I’m asking questions: Why not use the wire library?

Thanks for your response!

thebecwar:
You probably want to clear the MSB from the device’s address, not the register’s address.

I haven’t read the datasheet for your device, but usually the process for a read goes like this:

  1. Activate the slave device with the SS line.

  2. Send the device’s read address (slave addresses are 7 bit, the MSB is read or write.)

  3. Send the register you want to read from.

  4. Send a byte containing all 0s to shift the register value into your micro & read the byte.

  5. Release the slave device (the SS line again)

Yes, I’ve followed the above steps. I’ll try to post a screenshot showing the SPI traffic

thebecwar:
Also why the static on the readByte function?

Well, those are still prototype functions but I tend to declare non-global functions as static, even if arduino does not care about this. I’m new to C-arduino in fact

thebecwar:
And while I’m asking questions: Why not use the wire library?

Isn’t the wire library for I2C only? Anyway, I’ve also tried the Arduino SPI library with the same result. I just wanted to have better control over the problem.

Thanks again for your ideas.

Daniel.

See that’s what I get for typing at 2am… Yes, the wire library is I2C. You were correct.

I’ve simplified the above code to the minimum, removing the serial prints and using the Arduino SPI library:

#include <SPI.h>

#define RFM_OFF  9
#define SPI_SS     10
#define SPI_MOSI 11
#define SPI_MISO 12
#define SPI_SCK  13

#define PORT_SPI_SS  PORTB
#define BIT_SPI_SS   2

// SPI initialization
static void spi_Init()
{
  digitalWrite(SPI_SS, HIGH);
  digitalWrite(SPI_SS_MIRROR, HIGH);
  
  // Configure SPI pins
  pinMode(SPI_SS, OUTPUT);
  pinMode(SPI_MOSI, OUTPUT);
  pinMode(SPI_MISO, INPUT);
  pinMode(SPI_SCK, OUTPUT);
  
  // SPI speed = clk/4
  SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0);
}

// RFM22 initialization
void rfm22_Init()
{
  byte value;
   
  // Condifure pins
  digitalWrite(RFM_OFF, HIGH);  // switch RFM off
  pinMode(RFM_OFF, OUTPUT);
  delay(1000);
  digitalWrite(RFM_OFF, LOW);  // switch RFM on  
  delay(1000);
  
  spi_DisableRFM();            // Disable SPI slave
  delay(100);
  
  // The following command should return 0x06 (Device version) but is returning 0x08 (Device Type, reg 0x00)
  value = spi_ReadReg(0x01);

//  Serial.print("Reg 0x07 = ");
//  Serial.println(value, HEX);
}

// SPI - Read value from a given register address
byte spi_ReadReg(byte regAddr)
{
  byte value;

  regAddr &= 0x7F;                       // Clear MSB: read mode  
  spi_EnableRFM();                       // Enable SPI slave
  delay(1);
  SPI.transfer(regAddr);                 // Send slave's register address
  value = SPI.transfer(0x00);            // Read response from slave
  spi_DisableRFM();                      // Disable SPI slave

  return value;
}

// SPI - Enable SPI slave
void spi_EnableRFM(void)
{
  bitClear(PORT_SPI_SS, BIT_SPI_SS);     // SPI_SS = 0 (Select RFM22B module)
}

// SPI - Disable SPI slave
void spi_DisableRFM(void)
{
  bitSet(PORT_SPI_SS, BIT_SPI_SS);     // SPI_SS = 0 (Select RFM22B module)
}

// Setup function
void setup()
{
//  Serial.begin(9600);
  spi_Init();
  rfm22_Init(); 
}

// Main loop
void loop()
{
}

Further tests showed that MOSI is not actually driven by the atmega most of the times. I’ve posted a screenshot of the SPI transaction here:

http://8451358633823226897-a-usapiens-c … edirects=0

The first 8 bits in SCK mark the master request. This request should 0x01 and not 0x00 as the screenshot shows. Then the slave replies with the contents of the register 0x00.

Even when the uC sends the correct address (over MOSI), bits are too short so the slave ends by reading address=0 anyway.

Well, I’ve first thought that MOSI was tied to ground but this was not the case. Moreover, I’m able to bitbang MOSI (arduino pin 11) without problems and I’m even working on a SW SPI driver for my tests. It’s clear to me that I’m skipping an important step.

Thanks again for your ideas,

Daniel.

I’ve solved the problem!

There was a thin shortcut between MOSI and nSEL. So many hours for a so simple issue… :frowning:

Thanks for your help.

estratos,

if there is a page with your project write-up, please provide the URL.

Not yet sorry.

I still have to play with other RF IC’s, like the CC1101, but I hope to come back soon with a decent web page.

Daniel.

Hello estratos

Have you made progress on your project and library. I am looking to use the RFM22B.

Thanks,

Alan

estratos,

the page http://www.ulrichradig.de/home/index.ph … d-atmega88 includes a download link for a library http://www.ulrichradig.de/home/uploads/ … VR/LSS.zip to communicate to the RMB22B.

It might help you.

Working with the RFM22B from my Arduino platform resulted to be quite simple. It’s a matter of following some of the examples available on the net. However, since I’ve decided to test other RF equipment and IC’s, I’ve not created a library for Arduino yet. I’ll work on the library only for the retained IC I guess.

BTW, RFM22B is based on Silabs Si4432 IC and it provides really good features, including an integrated power amplifier and some good smarties on both Rx and Tx sides. FSK, GFSK, OOK modulations, FIFO’s, etc. However, I’ve found that I’ll have better luck with the CC1101 in terms of interoperability with other RF equipment, mainly because the CC1101 is wider adopted than the Silabs IC. It’s a pity that each manufacturer uses its own frame format so if you want to enable the Rx filtering and the Tx frame autocompletion feature, you need to use the format imposed by the manufacturer. This does not happen with 802.15.4 equipments though but they are more expensive and there are still few 802.15.4 IC alternatives for sub-1GHz bands.

Thanks estratos

I have been using the xBee’s and am not happy with the limited distance. I am interested in exploring other options that would improve the distance capabilities. I know that there are many factors that are out of my control when it comes to maxing out the distance. I want to have a very small antenna and small overall package too. I am will to experiment with other radios to find one that would get me close to what I want. Reading about everyone’s experiences on the web and through these forums have been very helpful.

Alan

AlanNYC:
Thanks estratos

I have been using the xBee’s and am not happy with the limited distance. I am interested in exploring other options that would improve the distance capabilities. I know that there are many factors that are out of my control when it comes to maxing out the distance. I want to have a very small antenna and small overall package too. I am will to experiment with other radios to find one that would get me close to what I want. Reading about everyone’s experiences on the web and through these forums have been very helpful.

Alan

RFM22B radios are then a good option for you I guess. Sub-1GHz bands provide better transmission distances. Moreover, Tx can reach +20 dBm max.

estratos:

AlanNYC:
Thanks estratos

I have been using the xBee’s and am not happy with the limited distance. I am interested in exploring other options that would improve the distance capabilities. I know that there are many factors that are out of my control when it comes to maxing out the distance. I want to have a very small antenna and small overall package too. I am will to experiment with other radios to find one that would get me close to what I want. Reading about everyone’s experiences on the web and through these forums have been very helpful.

Alan

RFM22B radios are then a good option for you I guess. Sub-1GHz bands provide better transmission distances. Moreover, Tx can reach +20 dBm max.

Thank you. I think I need to learn more about how dBm works. Is there a good resource to learn dBm? I have read a bit about it on wikipedia, but it sounded complicated. I need another resource to teach me how it works. Suggestions?

dBm is simply the logarithmic ratio the the RF power to 1mW.

waltr:
dBm is simply the logarithmic ratio the the RF power to 1mW.

How does that correlate to distance? For this discussion let’s assume no obstacles in between the receiver and transmitter.

dBm does not directly correlate to distance. It is really only valid at the output terminal of a transmitter (and some other cases). Remember that dBm is referenced to 1mW of RF power and does depend on load impedance (antenna or dummy).

In general the signal strength of a transmitted RF field strength decreases by to inverse square of the distance (plus a path loss that is dependent on the frequency of the transmission that I will not consider in this discussion). The received signal strength is mostly dependent on the receiver’s antenna and will not have the same power across its terminals as the transmitter’s antenna.

So dBm can be used to correlate to distance after a measurement is made at a know distance. Example:

A transmitter is setup with a fixed antenna and output power.

A receiving antenna is setup at a distance of 1 meter that is terminated with 50 Ohm.

The voltage across the antenna terminals is measured to be 100mV RMS.

That calculates to P = (E^2)/R = 200uW. Which is 10*log(200uW/1mW) = -7 dBm.

Now move the receiving antenna to be 8 meters from the transmitting antenna.

The received power should be 1/(8^2) = 0.0156 the power at 1 meter or -18 dB (relative) to the power at 1 meter.

The power on the receiving antenna terminals would be -7 dBm -18 dB = -25 dBm or 3.1uW. And the voltage across the receiving antenna should be 12.4mV.

This is a very simplified example to show the theory and basic math (there are many other factors to consider in a real system).

Any help?

After having played with the rf22B modules for a some weeks, I’ve finally switched to the CC1101 as the RF front-end. For anyone interested in this project, I’m maintaining a blog: http://panstamp.blogspot.com

right - so 0dBm = 1mW, 10dBm = 10mW, 20dBm = 100mW, etc., in log form. If the transmitter spec (or you measure) the power of x dBm, you add the antenna gain to get the effective radiated power. The gain is often expressed as dBi, decibels of gain relative to an ideal isotropic (spherical) radiator (antenna). This is abbreviated as EIRP. The FCC et al regulatory limits are based on EIRP.

I have some new Arduino code for the RFM12 transceiver, from the same SiLabs chipset family, and as a HopeRF module - SPI interface.

I wrote it to have no dependencies on specialized port I/O libraries. For testing, it uses the Serial library.

My code also does variable length data packets with a header then payload data. There’s a CRC for error detection for both the header and the payload - which can be up to 255 bytes.

The MAC and PHY in the HopeRF/SiLabs and TI chips is proprietary to each vendor, unlike, say, 802.15.4. However, '15.4 at 868/900 or 2.4GHz (type-certified modules (not chips or demos) are available from many vendors, including Digi (XBee), Jennic, Atmel/Meshnetics, OKI, Panasonic, et al). One might argue that these modules, at $25-35 each, though already FCC (and its peers) type certified, are an overkill for some applications. I have used XBees and Jennic extensively.

Recently though, I have a project that is quite cost conscious and needs low speed and longer range for bi-directional data. That leads one to a simple sub-GHz module, at a 1ea cost point of $6 or so, for a 2mW transmitter. The MicRel modules, sub-GHz, are appealing if you want 10mW rather than 2mW. With FSK and narrow channel bandwidth, the range with just dangling wire antennas is hundreds of ft. line of sight, sufficient for a star topology, or a simple self-forming mesh. However, not using a standards based module does require that you ensure that the type-certified module vendor (not the chip vendor) is a robust vendor, and the module won’t go EOL before you want.

In home automation, there’s Z-wave - proprietary. Used in mesh form by some large companies (licensed).

In a perfect world, the new 802.15.5 (not 15.4) standard for meshing could be adapted for use on other than 15.4 modules. The 15.5 standard is at layer 2.5 as they say, making it a peer of ZigBee and ISA100.11, but 15.5 is an open IEEE standard.