Paul and others re the SPI Transactions in RC2...
RF69 radio issue: SPI Clocks (and/or other related) sent while radio has interrupt request true for a long time due to other use of SPI port...
Using two T3.1's
I got the same code Paul was using running and did reproduce the symptoms.
Next, I decided to simplify the test and observe effect.
I reorganized the looping of the code and put in timers that detected things that might hang, such as transmit complete, etc.
I gave the client and server a "from" address and a packet sequence number. Server=1, Client=2. These both show in my off-air sniffer which I use to see transmitted packets by sequence number and source address, e.g, did it get sent?
(this sniffer is just an RFM69 module with my sniffer code and display on serial).
I changed the modulation mode on the radios to about 9Kbps using a profile that I have found to be among the best of the 20 available. We know there are serious problems with these 20 and we're working on it.
This modulation mode, using this test, and ideal signal strengths, still showed about 3% packet error rate. I put printouts on each end to tally these errors and delay 5 seconds before resuming.
With this, we can know to ignore these errors for the testing goal we have here.
I can run this modified code and see no problems other than the packet error rate for quite some time, with the code to call SPI.beginTransaction() and competing SPI transfers disabled.
The 'scope shows the interrupts from the radio are as expected.
Then I recompile and run with the SPI.beginTransaction(), pin 10 chip select, BUT NO spi clocking transfers. Just the chip select on/off.
After running normally about 15 seconds, the messages stop being transmitted. The 'scope shows that the radio's interrupt request came on and stays on forever.
This doesn't happen unless the transaction manager API call is made.
Looking at my 'scope, I see that the interrupt from the radio is goes true. But the ISR isn't running because there are no radio chip selects. This suggests that somehow the external interrupt enable got left in the false state.
So then I commented out the digitalWrite(10,LOW) and its mate, and ran program. A delayMicroseconds() in lieu of SPI.transfer() calls.
I haven't gotten it to fail. This is without sending an SPI clocks in the test.
Speculation: The new SPI transactions code is is not handling an esoteric of the SPI chip properly. On the AVR, there's the gotcha that if pin 10 (SS) is ever made to be be an input while the SPI chip is enabled, and there is a LOW on that pin, then the SPI chip automatically changes to the SPI slave mode. This may be n/a to the problem, or n/a to the K20.
I have included some code grabs and screen grab. I can make a screen video, maybe narrated, and post that somewhere too.
Also, would it be moot or useful to try this same code with a pair of mega328P boards and the same RFM69 radios and SPI transaction manager code?
Steve
Image is 1800 pixels wide...
Code:
#include "server.h"
#include <SPI.h>
#include <RH_RF69.h>
// THIS IS THE CLIENT SIDE TEST CODE
///////////////////////////////////////
// SPI traffic while radio's chip select is false. comment out to stop such traffic.
#define DO_OTHER_SPI_ACTIVITY 1 // boolean
#define RADIO_SS_PIN 15
#define RADIO_IRQ_PIN 16
// Below two items must be same in server code
#define FREQUENCY 433.0
//#define MODULATION_MODE RH_RF69::FSK_Rb19_2Fd19_2 // this is index 4 in table
#define MODULATION_MODE RH_RF69::FSK_Rb9_6Fd9_6 // this is index 3 in table
RH_RF69 rf69(RADIO_SS_PIN, RADIO_IRQ_PIN);
void setup()
{
pinMode(10, OUTPUT);
digitalWrite(10, HIGH);
pinMode(15, INPUT_PULLUP);
delay(100);
Serial.begin(9600);
//while (!Serial) ;
delay(3000);
Serial.println("PRJC CLIENT RF69 & Ethernet Test Begin");
if (!rf69.init()) {
Serial.println("RF69 init failed");
while (1);
}
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
// No encryption
if (!rf69.setFrequency(FREQUENCY)) {
Serial.println("RF69 setFrequency failed");
while (1);
}
rf69.setModemConfig(MODULATION_MODE); // this one works OK
Serial.println("RF69 init ok");
// If you are using a high power RF69, you *must* set a Tx power in the
// range 14 to 20 like this:
//rf69.setTxPower(14);
// The encryption key has to be the same as the one in the server
//uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
//stevech rf69.setEncryptionKey(key);
rf69.setThisAddress(2);
rf69.setHeaderFrom(2);
delay(100);
}
void loop()
{
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
static elapsedMillis msec;
static elapsedMillis testtimeout;
int errCount = 0;
int receiveTimeout;
static uint8_t packetNumber;
uint8_t data[] = "Client says Hello World!";
while (1) { // stay in this function for sake of local vars
Serial.print(millis()/1000);Serial.print(" errCount:");Serial.print(errCount);
Serial.print(" Sending to rf69_server, ID#");Serial.println(packetNumber);
rf69.setHeaderId(packetNumber++);
rf69.send(data, sizeof(data));
rf69.waitPacketSent();
msec = 0; // timer
receiveTimeout = 200; // msec
//rf69.available(); // TURN ON RECIVER NOW
while (msec < receiveTimeout) {
#if DO_OTHER_SPI_ACTIVITY
//SPI.beginTransaction(SPISettings(12000000ul, MSBFIRST, SPI_MODE0));
SPI.beginTransaction(SPISettings(4000000ul, MSBFIRST, SPI_MODE0));
//digitalWrite(10, LOW);
delayMicroseconds(10);
////for (int i=0; i > 0; --i) // some transfers in a single chip select
//// SPI.transfer(0x00);
//digitalWrite(10, HIGH);
SPI.endTransaction();
#endif
if (rf69.available()) {
uint8_t len = sizeof(buf);
Serial.println("calling recv()");
rf69.recv(buf, &len);
if (len == 0)
{++errCount;Serial.println("ERROR: len==0");delay(5000);break;}
Serial.print("Client got reply in ");Serial.print(msec);
Serial.print(" ms, len=");Serial.println(len);
msec = 0;
break; // escape inner while loop
} // available()
} // while (msec...
if (msec >= receiveTimeout)
{ ++errCount;Serial.println("ERROR: response timeout");delay(5000);}
delay(250); // message rate delay
} // while (1)
} // loop()
Code:
// THIS IS THE SERVER SIDE CODE
/////////////////////////////////////////////
// rf69_server.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple messageing server
// with the RH_RF69 class. RH_RF69 class does not provide for addressing or
// reliability, so you should only use RH_RF69 if you do not need the higher
// level messaging abilities.
// It is designed to work with the other example rf69_client
// Demonstrates the use of AES encryption, setting the frequency and modem
// configuration.
// Tested on Moteino with RFM69 http://lowpowerlab.com/moteino/
// Tested on miniWireless with RFM69 www.anarduino.com/miniwireless
#include <SPI.h>
#include <RH_RF69.h>
#define RADIO_SS_PIN 15
#define RADIO_IRQ_PIN 16
// Below two items must be same in server code
#define FREQUENCY 433.0
//#define MODULATION_MODE RH_RF69::FSK_Rb19_2Fd19_2 // this is index 4 in table
#define MODULATION_MODE RH_RF69::FSK_Rb9_6Fd9_6 // this is index 3 in table
RH_RF69 rf69(RADIO_SS_PIN, RADIO_IRQ_PIN);
// Singleton instance of the radio driver
void setup()
{
Serial.begin(9600);
delay(3000);
Serial.println("Server calling init");
rf69.init();
Serial.println("here here");
if (!rf69.init()) {
Serial.println("init failed");
while(1)
;
}
Serial.println("init returned OK");
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
if (!rf69.setFrequency(FREQUENCY)) {
Serial.println("RF69 setFrequency failed");
while (1)
;
}
else
Serial.println("setFrequency returned OK");
rf69.setModemConfig(MODULATION_MODE); // this one works OK in 433MHz band. Leave other band to use default
// If you are using a high power RF69, you *must* set a Tx power in the
// range 14 to 20 like this:
// rf69.setTxPower(14);
// The encryption key has to be the same as the one in the client
//uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
//rf69.setEncryptionKey(key);
#if 0
// For compat with RFM69 Struct_send
rf69.setModemConfig(MODULATION_MODE);
rf69.setPreambleLength(3);
uint8_t syncwords[] = { 0x2d, 0x64 };
rf69.setSyncWords(syncwords, sizeof(syncwords));
rf69.setEncryptionKey((uint8_t*)"thisIsEncryptKey");
#endif
rf69.setThisAddress(1);
rf69.setHeaderFrom(1);
Serial.println("exiting setup");
}
void loop()
{
static elapsedMillis msec = 0;
static elapsedMillis testtimeout;
static uint8_t packetNumber = 0;
if (rf69.available())
{
msec = 0; // reset msg receipt timer
// Should be a message for us now
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf69.recv(buf, &len))
{
Serial.print("received: ");Serial.println((char*)buf);
uint8_t data[] = "Hello from server";
rf69.setHeaderId(packetNumber);
rf69.send(data, sizeof(data)); // Send a reply
testtimeout = 0;
//rf69.waitPacketSent();
while (rf69.mode() == RH_RF69::RHModeTx) // changes at TX complete interrupt
if (testtimeout >= 500)
{Serial.println("tx interrupt timeout");while(1) ; }
rf69.available(); // TURN RECEIVER ON quickly
Serial.print("Server Sent reply ID#");Serial.println(packetNumber);
packetNumber++;
}
else
Serial.println("recv() returned 0");
}
else
if (msec >= 1000) {
msec = 0;
Serial.print(millis()/1000);Serial.println(" waiting for client msg");
}
}