RF22 wireless library replaced with "RadioHead" lib. For RFM69,RFM22, Si4432 wireless

Status
Not open for further replies.

stevech

Well-known member
RF22 wireless library replaced with "RadioHead" lib. For RFM69,RFM22, Si4432 wireless

NOTE: Post #7 below is a blog/diary of changes, working with RadioHead.

See
https://groups.google.com/forum/#!topic/rf22-arduino/rNDSHm6Q6eg
Replaces the well regarded RF22 library that is Arduino/Teensyduino compatible. I'll be confirming that Teensy3 that worked with RF22 is compatible with new "RadioHead" lib.
I've been talking with Mike, the author, about maintaining Teensy duino compatibility - just a tiny few lines of code for T3, related to I/O pin setup. It uses the standard SPI lib.

Added HopeRF's RFM69 module.
I've also used Mike's lib with the Dorji.com module that uses the same Si4432 radio chip which HopeRF also uses on the RFM22. (HopeRF OEM-brands it).

The RFM69 is another Si44xx family chip too.

Note that the RFM22 can produce 100mW Tx power at 85mA from the Teensy's Vout pin. The RFM69 needs more like 130mA at max power. These transmissions current demands can be quite brief be at higher data rates - like 1-10mSec. For now, I plan to continue to use the RFM22 because of this.

The RFM69 does add AES encryption in hardware. Others have used the RFM22 with application layer encryption of the user data payload. I prefer the app. layer encryption because it enables source-to-destination encryption (end to end), important in a network with relay nodes (e.g., meshed or routed). With data link layer encryption, a relay node decrypts and reencrypts - a security issue and overhead. But link layer encryption is fine if there are no relays and the first-hop destination is trusted.

If you're an amateur radio licensee (as I am), you can run these radios legally in the ham band and legally at high transmissions/sec. In the ISM 433MHz band, the FCC rules severely limit transmission duty cycle because things like wireless thermometers have no anti-collision mechanism (they have no receiver). Indeed, in my house, operating with frequent transmissions, near 433MHz jams my own wireless thermometer.
 
Last edited:
This looks like it would be great for the project I'm working on now. I'm using the Teensy 3.1 as a Ethernet gateway for a network of sensor nodes. The RF newtwork is using the RFM69 transceiver. The nodes are Moteino's with the RFM69.

The reliable datagrams will be useful and remove the need to code at the application level to verify that messages are received. It is also great that it provides flexibility in the support for various radios & microcontrollers. If we decide to use a different radio, or switch to using the UNO32 or Maple, the driver would remain the same.

The RHMesh feature, multi-hop routed across a network, with automatic route discovery, will be a useful feature when I have a larger number of nodes in the network.
 
Status of my work to get RadioHead (RH) to replace the RF22 library I have been using, with RFM22 radios and Teensy 3.
Problem 1 has a work-around: the RH library calls setDataMode() in the SPI library which hangs on an access to the SPI registers made by code in avr_emulation.h. The code that traps/hangs is the SPI0_CTAR0 within this:
if ((val & (1<<DORD)) && (SPI0_CTAR0 & SPI_CTAR_LSBFE)) ret |= (1<<DORD);

Problem 2 is that there is no pin 0 interrupt (pin 0 wired to nIRQ of radio). It works with the RF22 library, same hardware arrangement. attachInterrupt() for this pin 0 interrupt is essentially the same call as RF22. (confusing as there are 3 functions named attachInterrupt() with a 3 argument signature. testing showed that the correct one runs for teensy 3. (Not to be confused with the imprudently named by Arduinoians function attachInterrupt() with one argument that enables SPI port interrupts but doesn't setup a vector to an ISR.)

I spent more time than I wish to admit trying to find out why the interrupt doesn't happen. Resorted to putting LED blinkies in code that can't use Serial.print as it's in constructors, etc. Just can't find it yet. It's likely something simple. 'Scope shows pin 0 go low when the radio transmit command goes out SPI. SPI itself is working for read/write.
 
Last edited:
Here's an update on my work to get the new RadioHead library for the HopeRF RFM22/69 running. Its predecessor library, RF22, works great on Teensy. These libraries include handing un-addresses packets, unicast and broadcast, routed, and mesh routing. For the RFM22/69 radios ($5) for 433/868/900MHz, 100mW.

Today, the author (Mike) announced RadioHead v1.2 which adds support for the Nordic nRF 2.4GHz radios, and the 900MHz to come. With RadioHead, he's structured the software to hide the radio type from the layers above the radio link layer. Very clever library code.

see
https://groups.google.com/forum/#!topic/radiohead-arduino/XCNMO8YPIMs
 
Working to debug new RadioHead (RH) library working with the RFM22 radio. Should be same for RFM69. For Teensy, RH is beta.
Changes so far:
Assuming you are not using software SPI (bit banging):
in RHHardwareSPI.cpp, the work around for the SPI.setDataMode(SPI_MODE0) problem is in this code.
Code:
void RHHardwareSPI::begin() 
{
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_UNO32)
    uint8_t dataMode;
    if (_dataMode == DataMode0)
	dataMode = SPI_MODE0;
    else if (_dataMode == DataMode1)
	dataMode = SPI_MODE1;
    else if (_dataMode == DataMode2)
	dataMode = SPI_MODE2;
    else if (_dataMode == DataMode3)
	dataMode = SPI_MODE3;
    else
	dataMode = SPI_MODE0;
// ADD these. Temporary work-around due to problem where avr_emulation.h does not work properly for the setDataMode() call made within the new RH lib.
SPCR &= ~SPI_MODE_MASK; // mode 0 SPI.setDataMode(dataMode);
Serial.printf("RHHardwareSPI.cpp says SPI.setDataMode(%d) changed to SPCR &= ~SPI_MODE_MASK, mode=0\n", dataMode);

And in RH_RF22.cpp - and I'd assume the same needs to be added to the RH_RF69.cpp, I added the code in the bottom of this segment:
Code:
bool RH_RF22::init()
{

    if (!RHSPIDriver::init())
		return false;

    // Software reset the device
    reset();

    // Get the device type and check it
    // This also tests whether we are really connected to a device
    _deviceType = spiRead(RH_RF22_REG_00_DEVICE_TYPE);
    if (   _deviceType != RH_RF22_DEVICE_TYPE_RX_TRX
        && _deviceType != RH_RF22_DEVICE_TYPE_TX)
    {
	return false;
    }
	
    // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr>
	#if defined (__MK20DX128__) || defined (__MK20DX256__)
	// ARM M4 requires the below.. else pin interrupt doesn't work properly.
	pinMode(_interruptPin, INPUT); 
	#endif
It was in the RF22 lib from the person named above but it didn't get into the RH version.

I'm still testing. Some items include missing code to enable promiscuous mode.

Note too that the modulation mode table indexes changed-two new modes were added near the top of the table. So if you were using, say, index number 16, it is now 18. The default modulation mode is a very slow/conservative bit rate/channel width.

At this point, I have the RFM22 interrupts and transmit going. But my four radio star network with sniffer is not yet working as it did with the RF22 library. Something wrong with the addressing because I see receiver interrupts as frames arrive from the sending node.



=======Added 23Apr14 16:23PT ===============
Add this code to enable promiscuous mode. The RH driver's setPromiscuous() method updates a variable but I have yet to see how it gets sent to hardware (reliable datagram use case).
Below "radioDriver" is the name of an instance of the two classes needed with the RH lib+ReliableDatagram (only one class instance was needed for the RF22 lib). Seems to be so
because not all methods in the low level driver for the radio are inherited by the datagram and reliable datgram as they were in the RF22 lib.

Example:
Code:
RH_RF22 radioDriver;
RHReliableDatagram radioManager(radioDriver, 2);
Add something similar to the below for using promiscous mode:
Code:
	radioDriver.setPromiscuous(config.role == role_sniffer); // does not affect radio hardware, so...
    // write directly to radio for now. The class driver does not seem do so for setPromiscuous or other config items. 
	radioDriver.spiWrite(RH_RF22_REG_43_HEADER_ENABLE3, config.role == role_sniffer ? 0x00 : 0xff);//0's disables matching
I'm determining if other things like frequency, modulation mode, antenna switching bits can be changed at run time rather than only compile time.
My apps need to change freq., modulation mode, TX power, etc. at run time via a user interface (do so only in idle state).

MAX message size/buffer size in driver
Beware of this definition in the radio .h file (RF22, RF69, et al)
Code:
#define RH_RF22_MAX_MESSAGE_LEN 255
//#define RH_RF22_MAX_MESSAGE_LEN 50
I use 255 because the Teensy has plenty of RAM. The default is 50. Odd symptoms if message received is too large vs. this constant.

Confusing: The radio .h file has RH_RFM22_MAX_MESSAGE_LEN. The Reliable Datagram has RH_MAX_MESSAGE_LEN (no RF22 in the name). The buffer in the radio driver uses the former.

Don't use more than 255.
There is ONE receiver buffer in the drivers. It isn't double-buffered so you must quickly copy out the data and post another read if messages can come back-to-back, e.g., a star topology.
In reliable datagram mode, any one node can't send again until ACK is received. But in a star/mesh some other node may send on the tail of a different node's message



========= 24April2014============

This is a fix or a work-around for a larger problem in RH_ReliableDatagram. Applies to all radio types.
Problem:
A Node's address can be given at compile time in which case the class constructor receives the node address.
Also, the intention is that the method setThisAddress(n) can be used to change the node's address at run-time. A number stored in EEPROM would be recalled and used with setThisAddress().
For the Reliable Datagram case, this does not work correctly. The result is that the node maintains its compile-time address, thus the node can't be properly addressed.

A solution or work-around
In RHDatagram.cpp add one line of code:
Code:
Code:
void RHDatagram::setThisAddress(uint8_t thisAddress)
{
    _driver.setThisAddress(thisAddress);
#pragma message "is this a cure for ReliableDatagram skipping Datagram's _thisAddress?"
    _thisAddress = thisAddress; // local copy << ADD THIS LINE OF CODE
    // Use this address in the transmitted FROM header
    setHeaderFrom(thisAddress);
}
It is confusing that _thisAddress exists in the driver, in the RHDatagram, and in the RHReliableDatagram. A C++ wizard might know how this is supposed to work in terms of inheritance/priorities rather than keeping multiple copies of the same item in sync up the stack.

NOTE: I have not checked if the same issue exists for the mesh protocol.


========= 24April2014-1============
Problem:
RF22M and RFM69 radios: setPromiscuous(bool) was not in the .h for the class, nor coded in the .cpp as a public method. Needs to be called at run time, but only if radio is in idle state.


========= 24April2014-2============
All radios: some methods, esp. in drivers, if called while not in idle state, will sometimes crash - because these access the radio via SPI, and receive or transmit interrupt will conflict with use of SPI.
Solution could be driver (force idle state) or in app "don't do that- change to idle mode".
Best would be for driver to return false on these calls if state is not idle, e.g., get temperature, get RSSI, set frequency, set modulation, ...

========= 24April2014-3============
Made a couple of changes to the RH_RF69 .h and .cpp files to reflect same changes that were made to the RH_RF22 files.
each change has a #pragma message "explanation of situation" - which will be on the console output during a compilation. Be sure to watch for these.

========= 25April2014============
Improvement suggestion: Several places in libraries have "spin-loops" that loop until a variable/flag is altered by the interrupt service routine (ISR) for the radio. Such as transmit complete, message received, etc. At each spin loop, there should be a call to yield(). This is in the Arduino and Teensyduino core. By default, yield() does nothing. It can be overridden by added task schedulers or other code, to use the CPU productively.


========= 25April2014============
RH_RF22.cpp change 1 of 2, below, in the class init. It now calls function rather than set = 0. matches change #2, below.
Around line #150:
Code:
#pragma message "changed this to call a function herein so antenna control bits can be set at run time."
	setGpioReversed(_gpioReversed);

    // Enable interrupts

RH_RF22.cpp change 2 of 2, below. Moved code from init section to this function. Reversed the 0x12, 0x15 to match _gpioReversed = 0 for HopeRF modules, 1 for Dorji and some other brands.
Code:
// should call this only in idle state.
void RH_RF22::setGpioReversed(bool reversed)  {
	_gpioReversed = reversed;
    // Ensure the antenna can be switched automatically according to transmit and receive
    // This assumes GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit
    // This assumes GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive
    if (_gpioReversed)
    {
	    // Reversed for HAB-RFM22B-BOA HAB-RFM22B-BO, also Si4432 sold by Dorji.com via Tindie.com.
	    spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x12) ; // RX state
	    spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x15) ; // TX state
    }
    else
    {
	    spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x15) ; // TX state
	    spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x12) ; // RX state
    }
}

Tested the changes with both HopeRF RFM22 and Dorji modules which have opposite GPIO-TXRX switch hard-wired via PCB traces.

========= 3May2014============
RadioHead for RFM69 radios.
Symptom: Software hangs waiting for first interrupt
Applies to: RadioHead 1.5 and older. <<<<<<<<<<<< CHANGED. Was 1.7 and older.

The below is already in RadioHead 1.7 and later!
Solution:
In library file RH-RF69.cpp located in the Arduino libraries folders such as (PC/Windows):
C:\Arduino\libraries\RadioHead1_5\RH_RF69.cpp

Move the pinMode() which was added for the Teensy MK20 CPU. Move the pinMode() and enclosing #if and #endif from where it was as published, to just above the attachInterrupt() calls.
Below is what it looks like after the move:
Code:
...
  // Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
    #if defined (__MK20DX128__) || defined (__MK20DX256__)
        // ARM M4 requires the below. else pin interrupt doesn't work properly.
        pinMode(_interruptPin, INPUT);
    #endif

    _deviceForInterrupt[_interruptCount] = this;
    if (_interruptCount == 0)
	attachInterrupt(_interruptPin, isr0, RISING);
...




========= 3May2014-1============
Tested OK with
RadioHead 1.7 (must have for Teensy)
RFM69 radio on Teensy 3.1 and on Anarduino AVR mega32 board.
These two MCUs swap messages using the test program 2nd version uploaded as an attachment in these threads.

Next is to test RadioHead with RFM22 radios - had worked OK with older RadioHead version.


========= 3May2014-2============
Latest version of two-node test program:'
Purpose of program: First-use troubleshooting aid for two radios. Same code on both. MCUs controller these radios may differ, e.g., AVR plusTeensy 3 ARM.
Change source code #define for frequency. Now set to 433MHz and default modulation mode.
Compiled for RF69 radios. Change class names in code to use RFM22 (or other?)

Usage:
  • Uses default SPI port. On Teensy 3 ARM, the SPI clock is moved to pin 14.
  • Pin 13 is remapped back to the LED. This is done after the SPI port initializes as it undoes this.
  • edit source as above. compile and download to each of two radios. Change LED pin # if need be.
  • Open terminal window promptly.
  • LED on board should go on, then off if hardware init scuceeded (e.g., SPI port connections)
  • Terminal program should show that first transmission succeed and a transmit complete interrupt occurred OK.
  • The two nodes (radio+MCU) now exchange messages every second or so.
  • Each node chooses which node address to use to ensure each node's address differs (client/server IDs in the #defines).
  • If a node receives no message in 5 seconds, it broadcasts a message and tries to receive again. Forever.
  • For each message received, terminal displays from,id,rssi,message text
  • While in this loop, LED is on when message is received and until ACK is sent


========= 9May2014============
RH_RF69 lib v1.8 - new

Note for RF69 users: default Transmitter power is +13dBm which is the max that the RF69W does. For the RF69HW your app may change to +18 if you need that.
The RF69W, if set above +13, seems to disable the transmitter entirely.

But in v1.8 - no interrupts. It's because v1.8 changed the default pin number from 0 to 2. Affects any MCU that had been wired up for pin 0.
So in your application, code this, or any other pin #
add these in your application
// for RFM69 module variants
#define RF69_1_SLAVE_SELECT_PIN SS // 10
#define RF69_1_INTERRUPT_PIN 2

Then in your app, change the instance of the radio driver class as:
// Singleton instance of the radio driver.
// Override the default pin numbers...
RH_RF69 driver (RF69_1_SLAVE_SELECT_PIN, RF69_1_INTERRUPT_PIN);

I'm testing the new modulation mode constants in v1.8. V1.7 had some values that did not work properly.

For this, I am about finished with a newer/better test / demo program which I'll upload.

steve


EDIT
to correct typo above: { should have been ( in two places

The pin # change was in the release notes but I missed it.
 

Attachments

  • RH_RF69_first_use_test.ino
    5.3 KB · Views: 354
Last edited:
SEE POST #7. Updated 4/25.

I'm about ready to have someone try the libraries with the RFM69 radios as I have none.
First, I think I'll make a video showing details and use of test program I wrote for reliable datagrams sent among 4 nodes and with one node as a passive sniffer w/display.
 
Last edited:
Paul -- OK. There is still that avr_emulation.h problem. For now, I coded an alternative workaround.
Discussed in post #5 here.

Wish list: an adapter akin to the one for the RFM69, but for the RFM22 or the lower cost Dorji Si4432 PCB. Dorji is same size as the HopeRF modules. Connections only on one edge.
 
Last edited:
Announcement from airspayce re Teensy3 version of RadioHead.
https://groups.google.com/forum/#!topic/radiohead-arduino/miqC-UGnBrk
http://www.airspayce.com/mikem/arduino/RadioHead/
https://groups.google.com/forum/#!forum/radiohead-arduino
Mike's been great to rapidly add corrections I've reported.

I'm still testing more variations. The man use of this is with the library's Reliable Datagram support. This is peer to peer communications, with data packets addressed, or sent to address 255 as a broadcast. ACKs and retransmissions for error correction are used (except for broadcast of course). Mike no doubt has, but I haven't tested the router (relay) capability, nor the mesh network routing.

I have an elaborate star topology test program, interactive, which I'll post soon. I'm adding clear channel assessment (CCA), also known as CSMA/CA (carrier sense multiple access with collision avoidance). CCA is needed if there is lots of packets flowing among nodes, or 2+ nodes sending whenever they wish, to a server node, etc. Some collisions are corrected by the ACK timeout and retransmissions. But CCA with a random backoff-retransmit should greatly reduce uncorrectable errors due to collisions. You may know that 802.11 and 802.15.4 use CSMA.

My tests are with an RFM22 and a Dorji board. Both use an Si4432 radio. I'll test too with the RFM69s I have on order.
 
Last edited:
Will you be moving on to the NRF24 after the RFM69?
There already is a driver in RadioHead for the Nordic 2.4GHz radio. The RFM22/RFM69 and Dorji4432 all use SiLabs' radio chips, low cost but very sophisticated. The Nordic chips are 2.4GHz and low TX power (range disadvantaged) and are unsophisticated. If you wish, you could give the Nordic's a try with RadioHead and the existing driver.

I'll reconsider trying Nordic in a week or so; the RFM69's are due in this week.
 
From RadioHead's author, today
Version 1.5 was released today:

Added support for Nordic Semiconductor nRF905 transceiver with RH_NRF905
driver. Also added examples for nRF905 and tested on Teensy 3.1

Drivers are the bottom of the protocol stack for RadioHead.
The stack goes up as: datagram, reliable datagram, router, mesh; you can use any of these methods.
complete list of drivers for wireless data radios is listed here.
http://www.airspayce.com/mikem/arduino/RadioHead/
 
Last edited:
EDIT: test program source moved to post #7, above.

Below are obsolete (how can remove them from this post?)
 

Attachments

  • RH_RF69_first_use_test.ino
    4 KB · Views: 173
  • RH_RF69_first_use_test.ino
    4.5 KB · Views: 222
Last edited:
2nd revision of test program at post #21.
Added automatic address changing for two radio nodes so same code can run on both.
 
Great! I will try this with the teensy 3.1 and RFM69HW. I assume it can set high power mode? The rfm69hw version of the moteino would not work more than a few inches without turning on high power mode
 
Power mode: yes, there's a call to increase from the default +13dBm (for the RFM69W) to +18 for the HW. In high power, the module needs something like 130mA which is more than the Teensy 3 is spec'd to source.

With the RFM22 at 100mW, I got very good range, as have others. Many 100's of ft. Range depends too on what bit rate/modulation index you choose to use.


See also post #7 above, 9May2014 update.
 
Last edited:
Status
Not open for further replies.
Back
Top