SPI Conflict with I2C


This is the core project, a basic data logger.
The SD is 3.3v and hooked up accordingly.
The OLED and RTC are 5v and also hooked up to the correctly. [omitted for clarity]
The OLED and RTC w/flash interfaces with I2C.
The SD card reader interfaces with SPI. Using pin 10 for CS.

The individual modules have been tested individually. They are work as intended.
The problem begins when the SPI begin is invoked. At that time the I2C bus appears to no longer have reliable communications. I would say none, but a follow-on test while writing this shows some attempt at activity.

[ A side note - for whatever reason the I2C devices must be connected as shown. I originally had everything off J1-A4/A5. The OLED will not initialize. Ran through a selection of pull-up resistors to no effect. Also attempted to chain through the RTC, same results. Per the schematic The pins J1-A4/A5 and J2-17/18 are the same. So that will remain a mystery unless anyone knows what is up with that. I may chase it around later, this config works. ]

My theory is a conflict between the "Wire.h" and either "SPI.h" and/or "SD.h". I have not delved into the libraries yet.

I cannot be the only one with this problem, I just haven't found the answer yet.
Any advice would be appreciated.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
#include <SPI.h>
#include <SD.h>

#define SCREEN_WIDTH 128 // OLED display width,  in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
RTC_DS3231 rtc;

String  time;
String  hour;
String  min;
String  sec;

File    logHandle;
String  logName;

void setup() {
  Serial.begin(9600);

// start-setup OLED
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
  }
  delay(2000);                  // wait for initializing
  oled.clearDisplay();          // clear display
  oled.setTextSize(2);          // text size
  oled.setTextColor(WHITE);     // text color
  oled.setCursor(0, 10);        // position to display

// start-setup RTC
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (true); }
  
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  time.reserve(10);
  hour.reserve(2);
  min.reserve(2);
  sec.reserve(2);

// start SD
  Serial.print("Initializing SD card..."); 
  if (!SD.begin(10)) {                                   // <-- problem begins 
    Serial.println("initialization failed!"); }
   else {
    Serial.println("initialization done."); }

// format-create log file name
    DateTime now = rtc.now();
    logName = "";
    logName += now.year();
    logName =  logName.substring(logName.length()-2,logName.length());
    logName += now.month();
    logName += now.day();
    logName = "L" + logName;

} // end setup

void loop() {

  DateTime now = rtc.now();
  
// makes a clock - extra code for making the time look pretty
  time = "";
  hour = "";
  min = "";
  sec = "";
  hour  = now.hour();
  time += hour;
  min   = now.minute();
  if ( min.length() == 1 ) { time += ":0"; } else { time += ":"; }
  time += min;
  sec   = now.second();
  if ( sec.length() == 1 ) { time += ":0"; } else { time += ":"; }
  time += sec;

  oledDisplayCenter(time);
  Serial.println(time); // debug echo to serial monitor
}

void oledDisplayCenter(String text) {
  int16_t x1;
  int16_t y1;
  uint16_t width;
  uint16_t height;

  oled.getTextBounds(text, 0, 0, &x1, &y1, &width, &height);

  // display on horizontal and vertical center
  oled.clearDisplay(); // clear display
  oled.setCursor((SCREEN_WIDTH - width) / 2, (SCREEN_HEIGHT - height) / 2);
  oled.println(text); // text to display
  oled.display();
}

What board is this? What SD breakout is this?

'Accordingly' is it? My crystal ball is in the shop; what, precisely, does that mean?

I do hope that SD breakout has level shifters included if that's a 5V board. Because you may be using 3.3V power, but if it's a 5V board, the signals will still be 5V levels.

First post here, the original write-up stated the board was UNO R3 SMD.
We seemed to have lost that one.
You couldn't know that due to that issue with your ball.

I had not considered signal levels with the SD reader.
The SD is powered from 3.3v, upon inspection it does not have level shifters.

I can fix that.
Thanks for the observation.
Not sure if that fixes the existing problem but you never know.

Having used SPI and I2C on an R3 numerous times, I am very skeptical that your problems are caused by a conflict with Wire, SPI or SD.

I'm also very skeptical that hooking one I2C device up to one set of pins and another up to the other set is doing anything.

I would be more likely to believe that the up to 1K of RAM that the Adafruit_SSD1306 library allocates at run time and whatever buffers the SD library also allocates then are consuming more RAM than the measly 2K the the Uno R3 has.

And even if that's not true, your use of String variables is going to rapidly fragment whatever little RAM is left after the SSD1306 takes up to half of it and SD grabs another big chunk.

I completely agree with @van_der_decken especially for the "String" class variables.

So the first thing to do (and a good habit for UNO and other low-memory boards) is to get rid of the String variables, replacing them with standard "C strings" (and modifying accordingly all the instructions where they are managed, replacing them with C string functions like strcpy(), strcat(), and use of snprintf() for formatted strings).

Did you try to test SD module separately, for example with an examples from SD.h library ?

The issue between J1-A4/A5 and J2-A4/A5 was discovered while only working with I2C portions of the project. I may work this a bit further while I wait for the level shifting SD readers. Ordered a few right after your first response.

I guess it's time to educate me about the IDE -

When compiling and loading the code I posted, the IDE reports:

Sketch uses 25086 bytes (77%) of program storage space. Maximum is 32256 bytes.
Global variables use 1303 bytes (63%) of dynamic memory, leaving 745 bytes for local variables. Maximum is 2048 bytes.

With the SD portion commented out I get:

Sketch uses 23540 bytes (72%) of program storage space. Maximum is 32256 bytes.
Global variables use 1233 bytes (60%) of dynamic memory, leaving 815 bytes for local variables. Maximum is 2048 bytes.

Is this accurate?
Is there some dynamic allocation happening that I am not aware of?

I did exactly that. All by itself it the reader works.
A bit sketchy which I attributed to using SD from my lost and found pile.
I have some SD readers with level shifters on the way.

The Adafruit library will allocate 512 bytes of ram at run-time for a 128x32 display.

Can you verify that your RTC is a DS3231 and not a DS1307? The DS1307 will not work at the 400KHz I2C speed the display defaults to.

I can do that.

In post # you were made aware of the runtime allocation that is occurring.

The RTC is a DS3231 with AT24C32.

The run time allocation of the display and SD card buffers will consume over 1500 of the remaining 815 bytes.

One possible solution is to use a "text only" display library, which does not require the 1024 byte buffer. Another is to use an Arduino with more dynamic memory.

1 Like

After reading here and a quick look at the libraries I concede the conflict theory.
It appears the only conflict is over memory resources.
I have an UNO R4 on the way...on my desk is a Leonardo which has a touch more SRAM.

For my project the SD, RTC and Display are required.
I won't be doing any pixel level graphics so a text only method would help.
Cleaning up my string usage would help.
I don't believe that will free up the 800K or so of RAM required.
That SD instance really grabs up a lot of the few remaining bytes.
This project may not be feasible with 2K of SRAM.

Follow-on from the Leonard idea...
Won't work with this version of my code.
While I now have 1292-bytes of RAM after load, still not enough.
There appears to be some overhead with the Leonardo, available program memory is less than the R3...28672 vs. 32256.

So I'm going to chase the I2C thing while I wait for parts.

Adafruit have a pair of libraries to run the ssd1306 in plain ASCII, which is probably all you need for datalogging (click here) and is a lot more compact. All those Strings are a recipe for disaster - eventually, if not immediately.

Further, I submit that the Uno is not a good choice for a typical datalogging project. Even when you get rid of the dross you've got, you are likely to run out of memory next week. Consider a painless transfer to a Mega.

I assume you have already concluded that, if you use the standard code, libraries, and procedures for the I2C and SPI busses, you are not likely to be the first person in the universe to suffer a conflict.

K?? Review your math and your assumptions.

Simply replacing the TFT library with a text only version may free up enough RAM to allow the program to run. Then you can get to work removing the String objects, etc.

Yep! I cleaned up the code a bit. Almost doubled available SRAM.
I need to try those other SSD1306 libraries and see how it goes.
I believe at the last compile I had 1392-bytes available...getting that over 1.5K should (almost) give the SD library enough leg room. That's going to be challenging on the UNO, that has become obvious.

For the scope of the project I can log data to the AT24C32 on the RTC.
I just wanted the ease of removable media.

I haven't done much at the embedded level in a while. Last serious project I did with a Rabbit2000. Not quiet like ridding a bike.

I sure appreciate everyone's input. Very helpful.
Thanks.