RFM69 tranceiver with Wiznet 820io Ethernet and Teensy 3.1

Status
Not open for further replies.

thinkpeace

Active member
I've been working on using the HopeRF RFM69 low power RF tranceiver with the Teensy 3.1 and a WizNet820io Ethernet module. The conflict occurs because the RFM69 and the Ethernet both use the SPI port to communicate with the Teensy. The arduino library does not provide support for sharing the SPI port with multiple devices. I attempted to work around this limitation with two attempts.

The closest I've got this to working is by using the Low Power Lab RFM69 library. I would disable interrupts and put the radio to sleep before using the Ethernet module to send data to a server every 10 seconds. Most of the time is spent polling the RFM69 for available packets. This works, but the Teensy would hang up every ten minutes or so. The watchdog timer will reset the Teensy when this happens, but messages are lost in the process.

My next try was to use the RadioHead library which also provides a software SPI mode. I figured that if no interrupts were used, then everything would happen in the loop() function, and the conflicts would be avoided.

The software SPI mode works fine on the Teeensy without using the Ethernet library. But, when I add Ethernet.begin(mac); to my sketch, the RadioHead library hangs up on the first call to radiohead.available(); If I call Ethernet.begin(mac) before calling radiohead.init(), init fails.

The RFM module is connected as follows:

Teensy..RFM
0.......IRQ
8.......SS
11......MOSI
12......MISO
13......SCK


The Ethernet is connected as follows

Teensy..Ethernet
9.......RESET
10......SS
11......MOSI
12......MISO
13......SCK


This is the setup() and loop() code for the radio. This code works, until I add Ethernet.begin() to the setup() function.

Code:
#define USE_RADIOHEAD

RFServer rfServer;    // Singleton class

#define VERBOSE_TRACE
#define ENCRYPTKEY    "kd8w3a0mz8q01gbh" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW

// Singleton instance of the radio driver
#ifdef USE_RADIOHEAD
    RHSoftwareSPI spi;
    RH_RF69 radiodriver(8,0,spi);
    uint8_t encryptionKey[] = ENCRYPTKEY;
    // Class to manage message delivery and receipt, using the driver declared above
    RHReliableDatagram radiohead(radiodriver, 1);

#else
    RFM69 radio(8,0,true);
#endif

extern bool needToSendMessagesToServer;


// Call this function in setup() to initialize the RFM12B interface
void RFServer::setup( int cfg_node, int cfg_group, int cfg_freq, int cfg_spi_ss )
{
    pinMode( 0, INPUT );
    node = cfg_node;
    group = cfg_group;
    Serial.print( "radio.init(" ); Serial.print( cfg_freq ); Serial.print( ", " );
    Serial.print( node ); Serial.print( ", " ); Serial.print( group ); Serial.println( " );" );
    delay(100);
    
#ifdef USE_RADIOHEAD
    if (!radiohead.init())
        Serial.println("Radio init() failed");
    else
        Serial.println("Radiohead init successful" );
  // Defaults after init are 434.0MHz, modulation FSK_Rb2Fd5, +13dbM
  // No encryption
    radiodriver.setEncryptionKey(encryptionKey);
  if (!radiodriver.setFrequency(433.0))
    Serial.println("setFrequency failed");
  if (!radiodriver.setModemConfig(RH_RF69::GFSK_Rb250Fd250))
    Serial.println("setModemConfig failed");
#else
    radio.initialize(cfg_freq, node, group );

#ifdef IS_RFM69HW
    radio.setHighPower(); //uncomment only for RFM69HW!
#endif

    radio.encrypt(ENCRYPTKEY);
    radio.setPowerLevel(10);
#endif
}

// Call this function in the main loop() to reive any pending messages and process them
void RFServer::loop()
{
    int sender;
    Serial.println( "loop" );
    delay(100);
#ifdef USE_RADIOHEAD
    while(radiohead.available())
    {
        Serial.print( "." );
        delay(100);
       radio.DATALEN = MAX_DATA_LEN;
       if (radiohead.recvfromAck(radio.DATA, &radio.DATALEN, &radio.SENDERID))

#else
    while(radio.receiveDone())
    {
              Serial.print( "-" );
        delay(100);

#endif
        {
          ProcessMessage();
        }
    }
    Serial.println("$");
    delay(100);
    updateConnectedStatus();
}
 
I don't know about LowPower labs' driver, but the RadioHead driver for the RFM69 radios uses an interrupt. In that interrupt service routine, the SPI port is used.
So this can conflict with the Wiznet software should it too be using the SPI port at the same time.
I am working a lot with the RFM69 and RadioHead for both Teensy and Anarduino's MiniWireless.

Two simple brute force solutions that may work for you...
1. In the Wiznet Code, non-ISR, disable the HopeRF radio interrupt while using the SPI port at the non-ISR level of the Wiznet code. You can do this by setting to false the external interrupt enable bit for pin 0 as shown above. Or less desirably, disable interrupts globally for that time.
2. Before doing any Wiznet I/O on the SPI, place the HopeRFM radio in the idle state (not receiving) until use of the SPI port is finished. This will prevent the HopeRF radio from interrupting.

The wiring you show says you don't use an Interrupt from the Wiznet module - so you are likely polling it frequently, and that is likely via SPI commands. This poll is likely what's conflicting with interrupts from the RFM69 hardware. Suggestion #1 deals with this.

A better way to use the Wiznet is to wire up its interrupt signal to an I/O pin. If you don't have or want an interrupt handler for the Wiznet, the do the polling as follows, instead of via recurring SPI commands. To poll, simply read the I/O bit and see if it is true (low). If so, it's time to use the SPI port but coordinated as in #1 above.

This is the general problem of interrupt handlers using the SPI port. It's a problem in simple systems with no operating system to coordinate. The topic of SPI port management has been discussed at length here and on Arduino's forums, as more people are using multiple SPI based devices on small micros, and any one driver for these assume it "owns" the SPI port at all times, i.e., was written in a myopic manner.
 
Last edited:
Judging by some of Paul's most recent posts, shared SPI access is one of the most important items on his ToDo list. The Ethernet library is also likey one of the furst ones to be fixed. So hang on....
 
Yup, this and the pin 33 issue are at the top of my priority list.

I already patched the Ethernet library as part of an earlier work on this a couple months ago, and RFM69 is one of the 7 SPI peripherals I bought for testing, so odds are very good I'll have this combination working well within the next few weeks.

Look for 1.20-rc2 or -rc3.... hopefully soon!
 
When using the Low Power Labs RFM69 library, the Ethernet and RFM69 work together reasonably well. It's been running for two days communicating with 3 Moeinto's and one web-server, and only hit the watchdog reset 2 times. It is possible that the watchdog reset occurred doing to Ethernet.begin() taking too long. That will happen when I loose my internet and my gateway tries to reconnect. Ethernet.begin() blocks until the DHCP server is reached. So, it might actually be running perfectly.

To get it to work well with the Low Power Labs RFM library, I put the radio to sleep and disable interrupts before sending & receiving using Ethernet.

When I use the Radiohead library, it works OK as long as I don't initialize or use the Ethernet. I tried both interrupt mode, and software SPI mode. I also tried initializing the Ethernet First, and initializing the RFM69 first. I'll stick with the Low Power Labs library until compatibility with Radiohead-Ethernet is resolved.

Thanks for working on this Paul!
 
I am starting work on SPI transactions again. This time around, I'm going to fully complete it on Teensy, then try to work with the Arduino devs. Last time around, it got repeatedly delayed by back-and-forth arguing on their developers mailing list. I'm not going down that path again!

I have a few RFM69s, a WIZ820io, Adafruit's CC3000 and 2.8 inch touch shield and some other misc hardware here for testing.

What I don't have is real test cases that reproduce the problem. I can and will build very artificial tests, which are the best for developing the code. But after that, more "realistic" testing is going to take me a lot of time and effort, to build up some applications.

If you can put together some test cases (the Teensy code, code for the "other" RFM69, info for the other side of the ethernet) and steps to reliably reproduce the problem, that would be a great help. Good test cases will make sure I've really fixed this for your application, and they'll help speed the whole project up.
 
I can offer
For RadioHead software's driver class constructors for the protocol drivers for HopeRF boards, Nordic boards, other radios.

Anarduino miniwireless (mega32p, but same code ideally could run on Teensy 2, 3) boards. I've done so but with lots of #ifdefs on board and MCU type.
Uses two SPI devices:
SPI Bulk flash chip; slave select for is pin D5, true low. No interrupt.
SPI radios, HopeRF RFM22, RFM69, use pin D10, the default SS. Interrupt uses pin D2.

PJRC's RFM69 radio daughtercard. SPI CS is "pin 15" for a teensy 3. Interrupt is pin 10. (I wish we all used port/bit# notation, but board pin # are easier for noobies)

Example below is radio-only. Ext. SPI flash card or chip if present is a separate code module. (which is the basic problem in all of this).
Code:
#if (USE_TEENSY3_OSH) && (RADIO_TYPE_RF69)
  #define SLAVE_SELECT_PIN 15  // Teensy 3 with OSH Park RF69 board
  #define INTERRUPT_PIN    16
#elif (USE_ANARDUINO_MEGA32)
  #define SLAVE_SELECT_PIN 10  // Anarduino mini-wireless, AVR mega32. Same code here and in library
  #define INTERRUPT_PIN    2
#else
	#error "Which target MCU?"
#endif

#if (RADIO_TYPE_RF69)
	#include <RH_RF69.h>
	#define CLASS_NAME			RH_RF69
	#define CLASS_NAME_STRING	"RH_RF69"
	#define MAX_MESSAGE_LEN		RH_RF69_MAX_MESSAGE_LEN
	#define MODULATION_MODE		FSK_Rb9_6Fd9_6 // choices are in library docs and code
#elif (RADIO_TYPE_RF22)
	#include <RH_RF22.h>
	#define CLASS_NAME			RH_RF22
	#define CLASS_NAME_STRING	"RH_RF22"
	#define MAX_MESSAGE_LEN		RH_RF22_MAX_MESSAGE_LEN
	#define MODULATION_MODE		GFSK_Rb9_6Fd45 // choices are in library docs and code
#else	
	#error "Which radio type?"
#endif
 
Can you post this test case as complete, ready-to-run programs for each board, and specific steps to follow once programmed to reproduce the problem?

I'm crazy busy working on the code, so if you can save me some time trying to collect up all the right code and fill in the missing details, it will really help me to fix this faster for you and everyone else!
 
yes - will do. I uploaded the RFM22 diagnostic to RadioHead's website. I need to tweak a couple of things so it can run on the RFM69 modules. Will do this weekend.
I have several RadioHead related progams. The ping-pong diagnostic is probably a good start. Of course, you need two like-kind boards.
The RFM69W boards are 10mW Tx power; the RFM69HW are 100mW. There is a tiny bit of software difference in the configs.

The RFM22 boards... are, IMO, older but better. Different radio chip vendor (HopeRF makes PC modules using various vendors' chipsets).

RadioHead supports quite a few different ones - with a swap-out of the driver program within the stack that goes from Datagram to routed network to mesh network.
I'm way into a Python version of RadioHead (subset), and using it on computer to talk to a radio gateway with my code - and on to all the nodes in the network.
Lots of my code for the gateway and non-gateway network nodes. The nodes can exchange messages with any other node, direct or via routing or meshing.
But key to this is the ability to remotely reprogram these nodes - which I cannot do with the Teensy 2,3.
Anardino's boards have the mega328P. On that, I have different bootloader onboard that can reprogram the boards using a code image binary (derived from the .hex), gotten via the network, and saved in a large flash chip external to the MCU.
But, remote (unattended) reprogramming is a niche that perhaps PJRC doesn't rate high in user apps. I suppose, with a micro SD or just a Teensy 2 daughtercard w/a cheap flash chip for program code and data logging), would work. But the Mega32u/Mega32P with their tiny RAM are frustrating, for some apps.
 
Last edited:
Here's a pile of untested code, if you'd like to give my first attempt a try....

First, you'll need the new SPI library with transaction support.

https://github.com/PaulStoffregen/SPI

If using Teensy 2.0, updating the core library is also needed. This should work ok with Teensy 3.x. It currently doesn't support individual interrupt masking (next on my list...), so it will fall back to disabling all interrupts. That should be ok for simple testing, but it could play havoc with interrupt sensitive libraries like Servo.

Here are copies of Ethernet and RadioHead with my first attempt to support SPI transactions:

https://github.com/PaulStoffregen/Ethernet

https://github.com/PaulStoffregen/RadioHead

If you give this a try, please let me know how it works for you?
 
Status
Not open for further replies.
Back
Top