nRF24 / RF24

OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
https://nrf24.github.io/RF24
GNU General Public License v2.0
2.23k stars 1.02k forks source link

[Question] NRF24L01+ fails when using with MicroSD card. #692

Closed rbp9802 closed 3 years ago

rbp9802 commented 3 years ago

Sorry to ask here. This is not a library issue.

Hi, im trying to log and send aceleration data using NRF24L01+ (3.3v) and MicroSD (5V) modules. They are both working correctly separately but when i try both at the same time, NRF24L01+ fails.

i tried 2k ohm resistor in MISO line (see https://forum.arduino.cc/index.php?topic=507086.0) without success.

Also saw https://forum.mysensors.org/topic/6724/solved-nodemcu-esp8266-and-spi-with-nrf24l01-sd-card-module-sanity-errors/3 but i'm not sure if it applies to my problem. As this implies breaking the moduleI, befor i would like to know if ther are any other solutions. In my cases NRF24L01+ can be configured while not having MISO conected (obiously logging fails for this).

if it helps, her is my code:

#include "Arduino.h"
#include "Wire.h"
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"
#include "SdFat.h"
SdFat SD;
const int CS_SD = 10;
const int CS_RF = 8;
const int CE_RF = 7;
File DataFile;

RF24 radio(CE_RF, CS_RF); // CE, CSN

typedef struct{
  const int8_t ID = 1;
  int16_t AY;       // Y Acceleration
  int16_t AZ;       // Z Acceleration
  unsigned long t1; // time in microseconds
  }
myData;
myData data;

unsigned long t2;

void setup(){
  Wire.begin();
  Serial.begin(115200);
  printf_begin();
  Serial.print("Initializing SD card:   ");
  if(!SD.begin(CS_SD)){
    Serial.println("SD Fail");}
  else{Serial.println("SD OK");}

  RadioSetUp();

  data.t1 = micros();
  }

void loop(){
  t2 = micros();
  if (t2 - data.t1 > 9990) {       //100Hz reading - logging - sending
    Read();

    DataFile = SD.open("data.txt", FILE_WRITE);
    if (DataFile) {
      DataFile.print(data.ID);DataFile.print("\t");
      DataFile.print(data.t1);DataFile.print("\t");
      DataFile.print(data.AY);DataFile.print("\t");
      DataFile.println(data.AZ);
      DataFile.close();
      SerialPrint();}

    else {Serial.println("SD Error");}

    Send();
    }
  }

void RadioSetUp(){
  const uint64_t pipes[2] = { 0xE8E8F0F0E1LL, 0xE0F0E0F0E1LL };
  radio.begin();
  radio.setPALevel(RF24_PA_HIGH);
  radio.setChannel(0x76);
  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1, pipes[0]);
  radio.enableDynamicPayloads();
  radio.setPayloadSize(32);
  radio.setCRCLength(RF24_CRC_8);
  radio.setDataRate(RF24_250KBPS);
  radio.setAutoAck(1);
  radio.setRetries(1, 15);
  radio.powerUp();
  radio.stopListening();
  radio.printDetails();
  }

void Read(){
  data.t1 = micros();
  data.AY = yRead();
  data.AZ = zRead();
  }

void SerialPrint(){
  Serial.print(data.AY);Serial.print("\t");Serial.println(data.AZ);}

void Send(){
  if (radio.write(&data, sizeof(data))){
    SerialPrint();
    }
  }
2bndy5 commented 3 years ago

It is important to manage the Chip Select pins properly when using 1 SPI bus for multiple SPI slave devices (in your case the SD card module and the nRF24 module are SPI slave devices).

My first guess is the Chip Select pins aren't being managed properly. Looks like the fix in the link you posted bypassed the problem by disabling the level shifting chip on the SD card module via connecting the level shifting chip's Enable pin to the SD card module's CS pin. I would need to see the schematic for the level shifting chip and how it is soldiered into the SD card module's PCB to fully endorse this fix.

What MCU are you using?

Why are you using I2C? #include "Wire.h" is for I2C & the code you posted doesn't use it for either SD card or nRF24 modules. I haven't looked at the Wire.h file, but the I2C library might be messing with the SPI bus pins depending on the MCU board you are using (you didn't mention what MCU you're working with).

rbp9802 commented 3 years ago

Hi, thanks for your reply. I'm using Arduino nano, CS pins are correct. NRF24 works correctly with accelerometer LIS2DW12 which also use SPI. NRF24 stop working when i connect MISO pin of Micro SD Adapter to the arduino.

I'm using I2C to call ADS1115 in data.AY = yRead(); and data.AZ = zRead(); which i didn't include in the post.

Nano ADS1115 ADXL335 NRF24 Micro SD LIS2DW12
5V VCC     VCC  
3V3   VCC VCC   VCC
GND GND GND GND GND GND
A4 SCL       SCL*not used here
A5 SDA       SDA*not used here
--- A0 X      
 --- A1 Y      
---   A2 Z    
GND A3        
GND ADDR        
D13     SCK SCK SPC (SCL)
D12     MISO MISO SDO
D11     MOSI MOSI SDI (SDA)
D10     CS  
D9         CS
D8     CSN    
D7     CE    
2bndy5 commented 3 years ago

Thanks for specifying the MCU.

NRF24 stop working when i connect MISO pin of Micro SD Adapter to the arduino.

  1. This probably has something to do with the level shifting chip on the SD card module (assuming you're using one mentioned in the blog post you linked to).
  2. SD cards use the SPI bus a little differently than any other SPI slave device. The differences depend on the type of SD card that you're using (I'm not too personally familiar with these differences).

Conclusion

Look into the specs for your SD card and the datasheet for the level shifting chip on the SD card reader module. Prepare for a new level of confusion because the specs/datasheet for these devices will likely not share the same terminology.

I'm starting to think that tying the level shifting chip's CE pin to the SD card module's CS pin might be the only fix. Your concern before making that hardware modification is a good habit. Since there doesn't seem to be a problem on the RF24 side of the application, I probably won't be of much more help to you. Feel free to keep posting progress/questions on this thread.

rbp9802 commented 3 years ago

Hi Brendan, I solved it by setting a dedicated SPI bus for de SD module using https://github.com/greiman/SdFat library. I followed the "SoftwareSPI.ino" example https://github.com/greiman/SdFat/tree/master/examples/SoftwareSpi.

You have to change SdFatConfig.h file of the library. In line 112 change #define SPI_DRIVER_SELECT 0 to #define SPI_DRIVER_SELECT 2

Here is my new code:

#include "SdFat.h"
//new SPI pins
const uint8_t SD_CS_PIN     = 2;
const uint8_t SOFT_MOSI_PIN = 3;
const uint8_t SOFT_MISO_PIN = 4;
const uint8_t SOFT_SCK_PIN  = 5;

SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
SdFat sd;
File file;

In the setup section include this

if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt();
  }

In the loop section nothing changes

    file = sd.open("file.txt", FILE_WRITE);
    if (file) {
      file.println("print done.");
      file.close();

Thank you fo your support.

2bndy5 commented 3 years ago

Well done!! If you are able to, you should copy this successful solution to that mysensor forum you linked to in the OP.

Its probably a good tactic (in this particular scenario) to use the bitbanged SPI (AKA SoftSPI or SoftwareSPI) for the SD card since those things aren't exactly used for their "fast" I/O transfer speed. As for the nRF24, SPI timing is rather detrimental.

Thank you for posting back (not many people do). Again, good job!

rbp9802 commented 3 years ago

Thanks Brendan! I tested velocities over Hardware SPI and SoftSPI. Hardware SPI can write data up to 168Hz while Software SPI up to 54Hz, arround 3 time slower. I wonder if there is a way to use SoftSPI on NRF24L01 modules that can read/write data over 100Hz.

2bndy5 commented 3 years ago

Well there is a bit in the RF24 docs about using SoftSPI, but it would be slower...

I wouldn't recommend it because wireless throughput it often the "tighter" bottleneck in most projects. But you are free to experiment. As always, feel free to post your findings here!

2bndy5 commented 3 years ago

Now that I think about it, this RF24 library has introduced (since this issue was closed) a new feature to allow using secondary (or non-primary) hardware-driven SPI buses. But that still depends on if your MCU board has more than 1 SPI bus exposed.

rbp9802 commented 3 years ago

I've actually read that specifying SPI bus was an issue when using Raspberry Pi Pico. These does not include SoftSPI on Arduino Nano?

2bndy5 commented 3 years ago

specifying SPI bus was an issue when using Raspberry Pi Pico.

I need more details to confirm this. Where did you find that info?

The SoftSPI implementation likely won't work with the RP2040 because it wasn't written for that architecture. SoftSPI should work on AVR architecture (like Arduino nano) as long as the pins specified aren't the same pins used for hardware-driven SPI.

rbp9802 commented 3 years ago

Where did you find that info?

Read that from you, sorry. The RP2040 exposes multiple hardware SPI buses, so we may have to chew on that a bit.

2bndy5 commented 3 years ago

oh. 😆 That shouldn't be a problem anymore; consider it chewed and digested. I'm now a bit more familiar with the PicoSDK now than when I originally made that comment. Actually, I was able to apply the overloaded-begin() feature (which provides an OOP approach to specifying a different SPI bus) towards the RP2040. I've outlined as much in the rp2xxx branch's how-to docs about using the PicoSDK.

jvi326 commented 4 months ago

A way to fix it is making some changes to the board. You need to get rid of a resistor, cut some connection and wire from MISO pin to pin 7 DO of the SD adapter directly. It wasn't my idea but I followed the following tutorials which I give all the credit to:

https://www.youtube.com/watch?v=_Q2pBEgoMN0&t=548s https://www.youtube.com/watch?v=iqqFn2jn3p8 https://easyeda.com/modules/SD-card-module-schematic_88def62da2c043ee96033c31134c0a25