Share SPI bus BME280+SDcard

Hello,
For my new project I will have 3 Adafruit boards (9DOF IMU+BMP280+microSD) connected to a T-Minus board. Probably you don’t know this hardware but the board has an ATmega2560 uC similar to Arduino Mega and its coded using Arduino IDE.
Well, my 9DOF IMU is connected via I2C so no problem with this guy. Then and I need to share SPI bus to get data from BMP280 and save data to microSD. First I connected only the BMP280 using for chip select (CS) digital pin 5. The pins defined for SCK , MISO and MOSI are correct regarding the T-Minus board pinout. This works fine (see code bellow).

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>


#define BME_SCK 26 //pin 52 on Arduino Mega
#define BME_MISO 24 //pin 50 on Arduino Mega
#define BME_MOSI 25 ///pin 51 on Arduino Mega
#define BME_CS 5


#define SEALEVELPRESSURE_HPA (1013.25)

//Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO,  BME_SCK);

File myFile;

void setup() {
  Serial.begin(9600);
  Serial.println(F("BME280 test"));

  if (!bme.begin()) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

void loop() {
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();
    delay(2000);
}

Then I tried to connect the microSD breakout. I used the same SCK, MISO and MOSI but for chip select (CS) I am using digital pin 6. For testing I am saving the temperature value each second on the SD card, despite sending all data (temperature, pressure, altitude,…) to serial monitor. The saving operation is done correctly but the BMP280 starts giving wrong values. This is the code modified for SD card saving:

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <SD.h>;

#define BME_SCK 26 //pin 52 on Arduino Mega
#define BME_MISO 24 //pin 50 on Arduino Mega
#define BME_MOSI 25 ///pin 51 on Arduino Mega
#define BME_CS 5
#define SD_CS 6

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO,  BME_SCK);

File myFile;

void setup() {
  Serial.begin(9600);
  Serial.println(F("BME280 test"));

  if (!bme.begin()) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

void loop() {
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();
    delay(2000);
    SD.begin(SD_CS);
    myFile = SD.open("templog.txt", FILE_WRITE);
    myFile.print(bme.readTemperature());
    myFile.close();
}

Can you help me with this? What am I doing wrong?
Thank You!

I wonder if your problem is due to the BME280 and the SD Card libraries using different SPI settings. If so you need to implement the appropriate settings before trying to communicate with a device. Unfortunately library authors do not seem to think about this and provide a simple function to do it.

I don’t have a BME280 but I know that the SD Card library uses different settings from the nRF24L01+ transceivers.

Each library probably set up the SPI system to suit itself in the initialization code so it should not be too hard to find.

…R

The Adafruit BME280 library uses the SPI.beginTransaction and endTransaction calls, so it should be ok with the SD card.

Not that it matters. The BME280 and SD card have the same SPI settings.

I always recommend disabling all SPI devices before initializing any of them.

void setup() {
  digitalWrite(BME_CS, HIGH);
  digitalWrite(SD_CS,HIGH);

// rest of your setup

Which SD card module are you using? Is it a Catalex?

SurferTim:
The Adafruit BME280 library uses the SPI.beginTransaction and endTransaction calls, so it should be ok with the SD card.
GitHub - adafruit/Adafruit_BME280_Library: Arduino Library for BME280 sensors
Not that it matters. The BME280 and SD card have the same SPI settings.

I always recommend disabling all SPI devices before initializing any of them.

void setup() {

digitalWrite(BME_CS, HIGH);
 digitalWrite(SD_CS,HIGH);

// rest of your setup




Which SD card module are you using? Is it a Catalex?

Hello and thank you very much. I 'm using an Adafruit microSD breakout. I never had problems with this board but I never tried to connect it with other SPI slaves as well. I will try and report you the result. :slight_smile:

SurferTim:
The Adafruit BME280 library uses the SPI.beginTransaction

The beginTransaction() function can take parameters, and there are also other functions to adjust the SPI settings - speed, mode etc. The two devices may need different parameters.

The OP needs to be sure that the correct settings are in place everytime s/he wants to communicate with one of the devices.

...R

It does take parameters. This is what it uses. Both use MSBFIRST and SPI_MODE0. The speed is a bit slow for the SD card, but it should work.

 SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));

SurferTim:
It does take parameters.

Indeed. But are they the same parameters that are used by the other SPI device - the SD Card?

...R

From /utility/Sd2Card.cpp. case 4 is exactly the same as the BME280.

  switch (sckRateID) {
    case 0:  settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); break;
    case 1:  settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break;
    case 2:  settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break;
    case 3:  settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); break;
    case 4:  settings = SPISettings(500000, MSBFIRST, SPI_MODE0); break;
    case 5:  settings = SPISettings(250000, MSBFIRST, SPI_MODE0); break;
    default: settings = SPISettings(125000, MSBFIRST, SPI_MODE0);
  }

SurferTim:
From /utility/Sd2Card.cpp. case 4 is exactly the same as the BME280.

How do you get to that code when you are using the SD Card library?

It looks like it may be possible to work with the same settings as the BME device. But that does not mean that the default SD Card values are the same.

And I don't know to if the different SD Card settings will work with every SD Card.

I am not trying to argue with you. I am trying to find an explanation of the steps the OP needs to take so that he can use the two devices in the same program.

...R

I am using the SD card library. You can find that file here.
/libraries/SD/src/utility/Sd2Card.cpp

SurferTim:
I am using the SD card library. You can find that file here.
/libraries/SD/src/utility/Sd2Card.cpp

Sorry. That was not my question - I, also, have that file.

What i meant by "How do you get to that code when you are using the SD Card library?" is what lines do you need to put in a program to ensure that mode is used by the library.

...R

This works.

#include <SD.h>
#include <utility/Sd2Card.h>
Sd2Card sdcard;

void setup() {
  Serial.begin(115200);
  SD.begin();

  sdcard.setSckRate(4);
}

void loop() {
}

SurferTim:
This works.

That's very helpful. But I wonder if we have lost the OP?

I presume you could/should call sdcard.setSckRate(4); after a read of BME and before a read of the SD Card. Or would you also need to call SD.begin() ?

...R

Last I checked, the settings variable is set to the default in the begin call, which I believe is the same as "case 1". The BME280 calls its own beginTransaction with its settings, which I posted above. The SPI bus will alternate between 500KHz and 4MHz, depending on the device using it, with no intervention needed by your code.

If the OP has a question, he/she is free to jump in any time.

SurferTim:
The SPI bus will alternate between 500KHz and 4MHz, depending on the device using it, with no intervention needed by your code.

There have been other Threads recently in which people were not able to get an nRF24L01+ working alongside an SD Card. It seemed to me the problem might be the same as in this case.

Do you have a complete example program that works with the BME and the SD Card?

...R

There have been other Threads recently in which people were not able to get an nRF24L01+ working alongside an SD Card.

I think occurs where MISO back from the SD card is not buffered and the card interferes on the line when it is not selected. Buffering MISO with 1 gate of a 5V powered 74HC125 (or equivalent) with it's OE controlled by the SD card's CS pin provides the needed isolation of the MISO signal.

CrossRoads:
I think occurs where MISO back from the SD card is not buffered

Interesting.

Is this the result of poor design of the SD Card holder or is it a more general issue?

...R

Thank you very much. I solved the problem by other way. I am keeping SD card alone over SPI bus and sharing I2C bus between BMP280 and one 9DOF IMU. It´s working fine!

Best.

Is this the result of poor design of the SD Card holder or is it a more general issue?

I don't know if one blames it on poor SD cards for not getting off the bus when not selected, or for poor SD card socket/adapters for knowing it occurs and not doing anything about it.
I always isolate MISO so I don't have to worry about 3.3V circuitry in the SD card interfering with 5V signals or being damaged by 5V signals from elsewhere, like a Programmer or the uC during programming.

The ethernet and wifi shields do not buffer the MISO line for the SD card.

I have not tried the BME280 on SPI or I2C.

edit: There was a problem with the SD card library at one time. SD cards do not release the MISO line when the CS line goes HIGH. It requires another SPI clock cycle to free up the MISO line after the SD access. If you try using the SPI bus at some time in the future, maybe try a transfer after the SD card access and before attempting to access the BME280.
  SPI.transfer(0);