MaxStrange
MaxStrange

Reputation: 137

Arduino softwareserial clashing with SPI?

I have an Arduino project that is using an nRF24l01+ radio module over SPI (using this library: http://tmrh20.github.io/RF24/) and an RFID reader over softwareserial. I am sleeping my Arduino and having them wake it up via interrupt when a message has been received or an RFID tag is ready to be read. The RFID is on pins 4 and 5, while the nRF covers pins 9 - 13 as well as number 2 for its interrupt.

Both of these modules work fine with the sleep and interrupt code separately, but when combined in to a single sketch, the Arduino will wake up due to an RFID tag, read it, then try to send something over the radio and then just hang, waiting for the library call to write() to return.

I have delved a bit into the two libraries, but I mostly can't make heads or tails of the softwareserial library. It seems to maybe be using the same ISR behind the scenes as my nRF module, but I don't immediately see why that should be a big problem, and I don't understand why it should cause the radio to hang.

I know it may be a long shot, but does anyone have any idea what might be going on? Maybe somebody knows these libraries? Any thoughts on a work around? Thanks.

Upvotes: 3

Views: 1725

Answers (1)

freespace
freespace

Reputation: 16711

I was experiencing the same symptoms, and the problem turned out to be a buffer overrun in my code. The overrun itself was due to SoftwareSerial dropping bytes because the RF24 library was interring with its interrupt handling.

The code in question reads from a GPS receiver using SoftwareSerial, parses the NMEA sentences and extracts the latitude and longitude information for sending over radio using RF24. It goes something like this:

if (gpsSerial.available()) {
  int c = gpsSerial.read();
  if (c == '\r') {
    buf[bdx] = '\0';
    // process buf here, which contains a null terminated NMEA sentence
    // and then send via RF24
    bdx = 0;
  else if (c != '\n' && bdx < BUFLEN) buf[bdx++] = c;
}

Where BUFLEN was sized to be larger than any single NMEA sentence.

The experienced reader will pick up on the problematic line:

buf[bdx] = '\0';

Which writes to buf without a range check. In normal operation, with an uninterrupted stream of characters from the GPS module, this code works fine, because we will always encounter a \r before running out of space in buf. However sending information over RF24 causes enough delays that characters are dropped or corrupted by SoftwareSerial, and this assumption no longer holds.

So now what happens is this:

  1. a \r is missed, and the previous NMEA sentence is not discarded
  2. the next NMEA sentence is read into buf
  3. bdx advances until stopped by the range check in the else
  4. \r of the next NMEA sentence is countered
  5. buf[bdx] = '\0'; writes past the end of buf

At this point the Arduino stops responding, and it looks like write is blocking.

The fix is this line added before the if:

if (bdx >= BUFLEN) bdx = 0;

With no other modification than this line, the code has now been running for over 5 hours without issue, whereas previously it would not last more than 30 seconds before write "blocks".

Upvotes: 0

Related Questions