JeeLib
An easy interface to the port headers, the RF12 driver library, timers, low-power code, and more.
 All Classes Files Functions Variables Enumerations Macros Pages
RF12.cpp File Reference

RFM12B driver implementation. More...

#include "RF12.h"
#include <avr/io.h>
#include <util/crc16.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <WProgram.h>
#include <Ports.h>

Macros

#define RF_MAX   (RF12_MAXDATA + 5)
 
#define RFM_IRQ   2
 
#define SS_DDR   DDRB
 
#define SS_PORT   PORTB
 
#define SS_BIT   2
 
#define SPI_SS   10
 
#define SPI_MOSI   11
 
#define SPI_MISO   12
 
#define SPI_SCK   13
 
#define RF_RECV_CONTROL   0x94A0
 
#define RF_RECEIVER_ON   0x82DD
 
#define RF_XMITTER_ON   0x823D
 
#define RF_IDLE_MODE   0x820D
 
#define RF_SLEEP_MODE   0x8205
 
#define RF_WAKEUP_MODE   0x8207
 
#define RF_TXREG_WRITE   0xB800
 
#define RF_RX_FIFO_READ   0xB000
 
#define RF_WAKEUP_TIMER   0xE000
 
#define RF_LBD_BIT   0x0400
 
#define RF_RSSI_BIT   0x0100
 
#define NODE_BAND   0xC0
 
#define NODE_ID   0x1F
 
#define RETRIES   8
 
#define RETRY_MS   1000
 
#define rf12_xfer   rf12_xferSlow
 
#define DELTA   0x9E3779B9
 
#define MX
 

Enumerations

enum  {
  TXCRC1, TXCRC2, TXTAIL, TXDONE,
  TXIDLE, TXRECV, TXPRE1, TXPRE2,
  TXPRE3, TXSYN1, TXSYN2
}
 

Functions

void rf12_set_cs (uint8_t pin)
 Option to set RFM12 CS (or SS) pin for use on different hardware setups. More...
 
void rf12_spiInit ()
 Only needed if you want to init the SPI bus before rf12_initialize() does. More...
 
uint16_t rf12_control (uint16_t cmd)
 Low-level control of the RFM12B via direct register access. More...
 
uint8_t rf12_recvDone ()
 Call this frequently, returns true if a packet has been received. More...
 
uint8_t rf12_canSend ()
 Call this to check whether a new transmission can be started. More...
 
void rf12_sendStart (uint8_t hdr)
 Call this only when rf12_recvDone() or rf12_canSend() return true.
 
void rf12_sendStart (uint8_t hdr, const void *ptr, uint8_t len)
 Call this only when rf12_recvDone() or rf12_canSend() return true. More...
 
void rf12_sendNow (uint8_t hdr, const void *ptr, uint8_t len)
 This variant loops on rf12_canSend() and then calls rf12_sendStart() asap. More...
 
void rf12_sendWait (uint8_t mode)
 Wait for send to finish. More...
 
uint8_t rf12_initialize (uint8_t id, uint8_t band, uint8_t g, uint16_t f)
 Call this once with the node ID, frequency band, and optional group. More...
 
void rf12_onOff (uint8_t value)
 This simulates OOK by turning the transmitter on and off via SPI commands. More...
 
uint8_t rf12_configSilent ()
 Initialize the RFM12B module from settings stored in EEPROM by "RF12demo" don't call rf12_initialize() if you init the hardware with rf12_config(). More...
 
void rf12_configDump ()
 Call this to send a description of the EEPROM settings to the serial port. More...
 
uint8_t rf12_config (uint8_t show)
 
void rf12_sleep (char n)
 Power off the RFM12B, ms > 0 sets watchdog to wake up again after N * 32 ms. More...
 
char rf12_lowbat ()
 Return true if the supply voltage is below 3.1V. More...
 
void rf12_easyInit (uint8_t secs)
 Set up the easy tranmission mode, arg is number of seconds between packets. More...
 
char rf12_easyPoll ()
 Call this often to keep the easy transmission mode going. More...
 
char rf12_easySend (const void *data, uint8_t size)
 Send new data using easy transmission mode, buffer gets copied to driver. More...
 
void rf12_encrypt (const uint8_t *key)
 Enable encryption (null arg disables it again). More...
 

Variables

volatile uint16_t rf12_crc
 Running crc value, should be zero at end.
 
volatile uint8_t rf12_buf [RF_MAX]
 Recv/xmit buf including hdr & crc bytes.
 
long rf12_seq
 Seq number of encrypted packet (or -1).
 
void(* crypter )(uint8_t)
 

Detailed Description

RFM12B driver implementation.

Macro Definition Documentation

#define MX
Value:
(((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + \
(cryptKey[(uint8_t)((p&3)^e)] ^ z)))

Function Documentation

uint8_t rf12_canSend ( void  )

Call this to check whether a new transmission can be started.

Call this when you have some data to send. If it returns true, then you can use rf12_sendStart() to start the transmission. Else you need to wait and retry this call at a later moment.

Don't call this function if you have nothing to send, because rf12_canSend() will stop reception when it returns true. IOW, calling the function indicates your intention to send something, and once it returns true, you should follow through and call rf12_sendStart() to actually initiate a send. See this weblog post.

Note that even if you only want to send out packets, you still have to call rf12_recvDone() periodically, because it keeps the RFM12B logic going. If you don't, rf12_canSend() will never return true.

uint8_t rf12_config ( uint8_t  show)
void rf12_configDump ( )

Call this to send a description of the EEPROM settings to the serial port.

This replaces rf12_config(0), to be called after rf12_configSilent(). Can be used to avoid pulling in the Serial port code in cases where it's not used.

uint8_t rf12_configSilent ( )

Initialize the RFM12B module from settings stored in EEPROM by "RF12demo" don't call rf12_initialize() if you init the hardware with rf12_config().

This calls rf12_initialize() with settings obtained from EEPROM address 0x20 .. 0x3F. These settings can be filled in by the RF12demo sketch in the RFM12B library. If the checksum included in those bytes is not valid, rf12_initialize() will not be called.

As side effect, rf12_config() also writes the current configuration to the serial port, ending with a newline. Use rf12_configSilent() to avoid this.

Returns
the node ID obtained from EEPROM, or 0 if there was none.
uint16_t rf12_control ( uint16_t  cmd)

Low-level control of the RFM12B via direct register access.

This call provides direct access to the RFM12B registers. If you're careful to avoid configuring the wireless module in a way which stops the driver from functioning, this can be used to adjust frequencies, power levels, RSSI threshold, etc. See the RFM12B wireless module documentation.

This call will briefly disable interrupts to avoid clashes on the SPI bus.

Returns the 16-bit value returned by SPI. Probably only useful with a "0x0000" status poll command.

Parameters
cmdRF12 command, topmost bits determines which register is affected.
void rf12_easyInit ( uint8_t  secs)

Set up the easy tranmission mode, arg is number of seconds between packets.

Set up the easy transmission mechanism. The argument is the minimal number of seconds between new data packets (from 1 to 255). With 0 as argument, packets will be sent as fast as possible:

  • On the 433 and 915 MHz frequency bands, this is fixed at 100 msec (10 packets/second).
  • On the 866 MHz band, the frequency depends on the number of bytes sent: for 1-byte packets, it'll be up to 7 packets/second, for 66-byte bytes of data it will be around 1 packet/second.

This function should be called after the RF12 driver has been initialized, using either rf12_initialize() or rf12_config().

Parameters
secsThe minimal number of seconds between new data packets (from 1 to 255). With a 0 argument, packets will be sent as fast as possible: on the 433 and 915 MHz frequency bands, this is fixed at 100 msec (10 packets/second). On 866 MHz, the frequency depends on the number of bytes sent: for 1-byte packets, it will be up to 7 packets/second, for 66-byte bytes of data it drops to approx. 1 packet/second.
Note
To be used in combination with rf12_easyPoll() and rf12_easySend().
char rf12_easyPoll ( void  )

Call this often to keep the easy transmission mode going.

Needs to be called often to keep the easy transmission mechanism going, i.e. once per millisecond or more in normal use. Failure to poll frequently enough is relatively harmless but may lead to lost acknowledgements.

Returns
1 = an ack has been received with actual data in it, use rf12len and rf12data to access it. 0 = there is nothing to do, the last send has been ack'ed or more than 8 re-transmits have failed. -1 = still sending or waiting for an ack to come in
Note
To be used in combination with rf12_easyInit() and rf12_easySend().
char rf12_easySend ( const void *  data,
uint8_t  size 
)

Send new data using easy transmission mode, buffer gets copied to driver.

Submit some data bytes to send using the easy transmission mechanism. The data bytes will be copied to an internal buffer since the actual send may take place later than specified, and may need to be re-transmitted in case packets are lost of damaged in transit.

Packets will be sent no faster than the rate specified in the rf12_easyInit() call, even if called more often.

Only packets which differ from the previous packet will actually be sent. To force re-transmission even if the data hasn't changed, call "rf12_easySend(0,0)". This can be used to give a "sign of life" every once in a while, and to recover when the receiving node has been rebooted and no longer has the previous data.

The return value indicates whether a new packet transmission will be started (1), or the data is the same as before and no send is needed (0).

Note that you also have to call rf12_easyPoll periodically, because it keeps the RFM12B logic going. If you don't, rf12_easySend() will never send out any packets.

Note
To be used in combination with rf12_easyInit() and rf12_easyPoll().
void rf12_encrypt ( const uint8_t *  key)

Enable encryption (null arg disables it again).

This enables or disables encryption using the public domain XXTEA algorithm by David Wheeler. The payload will be extended with 1 .. 4 bytes, containing a 6..30-bit sequence number which is incremented in the sender for each new packet.

The number of bits sent across depends on the number of padding bytes needed to make the resulting payload an exact mulitple of 4 bytes. A longer sequence number field can provide more protection against replay attacks (note that verification of this sequence number must be implemented in the receiver code).

Encrypted packets (and acknowledgements) must be 4..62 bytes long. Packets less than 4 bytes will not be encrypted. On reception, the payload length is adjusted back to the original length passed to rf12_sendStart().

There is a "long rf12seq" global which is set to the received sequence number (only valid right after rf12recvDone() returns true). When encryption is not enabled, this global is set to -1.

Parameters
keyPointer to a 16-byte (128-bit) encryption key to use for all packet data. A null pointer disables encryption again. Note: this is an EEPROM address, not RAM! - RF12_EEPROM_EKEY is a great value to use, as defined in the include file, but another address can be specified if needed.
See Also
http://jeelabs.org/2010/02/23/secure-transmissions/
uint8_t rf12_initialize ( uint8_t  id,
uint8_t  band,
uint8_t  g,
uint16_t  f 
)

Call this once with the node ID, frequency band, and optional group.

Call this once with the node ID (0-31), frequency band (0-3), and optional group (0-255 for RFM12B, only 212 allowed for RFM12).

Parameters
idThe ID of this wireless node. ID's should be unique within the netGroup in which this node is operating. The ID range is 0 to 31, but only 1..30 are available for normal use. You can pass a single capital letter as node ID, with 'A' .. 'Z' corresponding to the node ID's 1..26, but this convention is now discouraged. ID 0 is reserved for OOK use, node ID 31 is special because it will pick up packets for any node (in the same netGroup).
bandThis determines in which frequency range the wireless module will operate. The following pre-defined constants are available: RF12_433MHZ, RF12_868MHZ, RF12_915MHZ. You should use the one matching the module you have, to get a useful TX/RX range.
gNet groups are used to separate nodes: only nodes in the same net group can communicate with each other. Valid values are 1 to 212. This parameter is optional, it defaults to 212 (0xD4) when omitted. This is the only allowed value for RFM12 modules, only RFM12B modules support other group values.
fFrequency correction to apply. Defaults to 1600, per RF12 docs. This parameter is optional, and was added in February 2014.
Returns
the nodeId, to be compatible with rf12_config().

Programming Tips

Note that rf12_initialize() does not use the EEprom netId and netGroup settings, nor does it change the EEPROM settings. To use the netId and netGroup settings saved in EEPROM use rf12_config() instead of rf12_initialize. The choice whether to use rf12_initialize() or rf12_config() at the top of every sketch is one of personal preference. To set EEPROM settings for use with rf12_config() use the RF12demo sketch.

char rf12_lowbat ( void  )

Return true if the supply voltage is below 3.1V.

This checks the status of the RF12 low-battery detector. It will be 1 when the supply voltage drops below 3.1V, and 0 otherwise. This can be used to detect an impending power failure, but there are no guarantees that the power still remaining will be sufficient to send or receive further packets.

void rf12_onOff ( uint8_t  value)

This simulates OOK by turning the transmitter on and off via SPI commands.

This can be used to send out slow bit-by-bit On Off Keying signals to other devices such as remotely controlled power switches operating in the 433, 868, or 915 MHz bands.

To use this, you need to first call rf12initialize() with a zero node ID and the proper frequency band. Then call rf12onOff() in the exact timing you need for sending out the signal. Once done, either call rf12onOff(0) to turn the transmitter off, or reinitialize the wireless module completely with a call to rf12initialize().

Parameters
valueTurn the transmitter on (if true) or off (if false).
Note
The timing of this function is relatively coarse, because SPI transfers are used to enable / disable the transmitter. This will add some jitter to the signal, probably in the order of 10 ┬Ásec.
uint8_t rf12_recvDone ( void  )

Call this frequently, returns true if a packet has been received.

The timing of this function is relatively coarse, because SPI transfers are used to enable / disable the transmitter. This will add some jitter to the signal, probably in the order of 10 ┬Ásec.

If the result is true, then a packet has been received and is available for processing. The following global variables will be set:

  • volatile byte rf12_hdr - Contains the header byte of the received packet - with flag bits and node ID of either the sender or the receiver.
  • volatile byte rf12_len - The number of data bytes in the packet. A value in the range 0 .. 66.
  • volatile byte rf12_data - A pointer to the received data.
  • volatile byte rf12_crc - CRC of the received packet, zero indicates correct reception. If != 0 then rf12_hdr, rf12_len, and rf12_data should not be relied upon.

To send an acknowledgement, call rf12_sendStart() - but only right after rf12_recvDone() returns true. This is commonly done using these macros:

if(RF12_WANTS_ACK){
   rf12_sendStart(RF12_ACK_REPLY,0,0);
 }
See Also
http://jeelabs.org/2010/12/11/rf12-acknowledgements/
void rf12_sendNow ( uint8_t  hdr,
const void *  ptr,
uint8_t  len 
)

This variant loops on rf12_canSend() and then calls rf12_sendStart() asap.

Wait until transmission is possible, then start it as soon as possible.

Note
This uses a (brief) busy loop and will discard any incoming packets.
Parameters
hdrThe header contains information about the destination of the packet to send, and flags such as whether this should be acknowledged - or if it actually is an acknowledgement.
ptrPointer to the data to send as packet.
lenNumber of data bytes to send. Must be in the range 0 .. 65.
void rf12_sendStart ( uint8_t  hdr,
const void *  ptr,
uint8_t  len 
)

Call this only when rf12_recvDone() or rf12_canSend() return true.

Switch to transmission mode and send a packet. This can be either a request or a reply.

Notes

The rf12_sendStart() function may only be called in two specific situations:

  • right after rf12_recvDone() returns true - used for sending replies / acknowledgements
  • right after rf12_canSend() returns true - used to send requests out

Because transmissions may only be started when there is no other reception or transmission taking place.

The short form, i.e. "rf12_sendStart(hdr)" is for a special buffer-less transmit mode, as described in this weblog post.

The call with 4 arguments, i.e. "rf12_sendStart(hdr, data, length, sync)" is deprecated, as described in that same weblog post. The recommended idiom is now to call it with 3 arguments, followed by a call to rf12_sendWait().

Parameters
hdrThe header contains information about the destination of the packet to send, and flags such as whether this should be acknowledged - or if it actually is an acknowledgement.
ptrPointer to the data to send as packet.
lenNumber of data bytes to send. Must be in the range 0 .. 65.
void rf12_sendWait ( uint8_t  mode)

Wait for send to finish.

Wait for completion of the preceding rf12_sendStart() call, using the specified low-power mode.

Note
rf12_sendWait() should only be called right after rf12_sendStart().
Parameters
modePower-down mode during wait: 0 = NORMAL, 1 = IDLE, 2 = STANDBY, 3 = PWR_DOWN. Values 2 and 3 can cause the millisecond time to lose a few interrupts. Value 3 can only be used if the ATmega fuses have been set for fast startup, i.e. 258 CK - the default Arduino fuse settings are not suitable for full power down.
void rf12_set_cs ( uint8_t  pin)

Option to set RFM12 CS (or SS) pin for use on different hardware setups.

Set to Dig10 by default for JeeNode. Can be Dig10, Dig9 or Dig8

void rf12_sleep ( char  n)

Power off the RFM12B, ms > 0 sets watchdog to wake up again after N * 32 ms.

This function can put the radio module to sleep and wake it up again. In sleep mode, the radio will draw only one or two microamps of current.

This function can also be used as low-power watchdog, by putting the radio to sleep and having it raise an interrupt between about 30 milliseconds and 4 seconds later.

Parameters
nIf RF12SLEEP (0), put the radio to sleep - no scheduled wakeup. If RF12WAKEUP (-1), wake the radio up so that the next call to rf12_recvDone() can restore normal reception. If value is in the range 1 .. 127, then the radio will go to sleep and generate an interrupt approximately 32*value miliiseconds later.
Todo:
Figure out how to get the "watchdog" mode working reliably.
void rf12_spiInit ( void  )

Only needed if you want to init the SPI bus before rf12_initialize() does.

Initialise the SPI port for use by the RF12 driver.