Can I run SPI and I2C simultaneously on Uno?

Hi all,

I have an Arduino project for a greenhouse that publishes data to a webpage. The ethernet shield communicates via SPI, and I have a barometric sensor that works via I2C on pins A4 and A5.

Here is the issue that I am having. Any time I add code for the Arduino to read the barometric sensor, the webpage stops working completely. I have tried 6 different BMP180 libraries, and they all stop at the point where I tell the code to read the sensor in the loop section.

This got me thinking...can SPI and I2C run at the same time? My consensus is yes, but after this experience, I am thinking that maybe I am wrong. If you would like to see code as well, let me know and I can get it for you.

I appreciate your time,
Joe M

The I2C is not used by the Ethernet Shield or Ethernet library, they are completely seperated from each other.

It could be wrong code in the loop(). If the Ethernet is not handled or the interrupts turn off, then it will no longer work.

It could be a mistake in the hardware. Do you have a level shifter between the Arduino Uno and the BMP180 ? The newer sensor from Bosch really don't like a 5V I2C bus, the BMP180 might still be okay, but that depends on the pullup resistors.

I think you have a memory problem.
The Arduino Uno has just enough memory to do simple things with the Ethernet shield. If the SD library is added or something else, then you get memory problems.
You can upgrade to an Arduino Mega 2560, or move on to something newer, for example a MKR board or a ESP32.

Perhaps we can help to reduce the memory if you show the sketch.

When you show the sketch, the compiler output for the memory usage, give links to the modules that you bought and tell how everything is connected, that would make it easier for us.

Thank you for your response. The interrupt is something that I did not think of. Here is the code that I have currently:

//libraries
#include <SPI.h>
#include <Ethernet.h>
#include <dht.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>


//ethernet
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };// mac address
byte ip[] = { 10, 0, 0, 60 }; // IP address in LAN
byte gateway[] = { 10, 0, 0, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(80);

//strings
String readString;

//integers
int ledPin = 2;
const int temperaturePin = A0;

//pin definitions
#define DHT11_PIN A1
dht DHT;

//conversions
float hum;

Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);

void setup() {

  pinMode(ledPin, OUTPUT); //pin selected to control
  digitalWrite(ledPin, HIGH);
  delay(500);
  digitalWrite(ledPin, LOW);
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
}

void loop() {
  int reading = analogRead(temperaturePin);
  
  float voltage = reading * 5.0;
  voltage /= 1024.0;

  float temperatureC = (voltage - 0.5) * 100;
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  
  int chk = DHT.read11(DHT11_PIN);
  hum = DHT.humidity;

  sensors_event_t event;
  bmp.getEvent(&event);
  
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
        }

        //if HTTP request has ended- 0x0D is Carriage Return \n ASCII
        if (c == 0x0D) {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE> Greenhouse Monitor</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<hr>");
          client.println("
");
          client.println("<H1 style=\"color:Green;\">Greenhouse Monitor</H1>");
          client.println("<hr>");
          client.println("
");

          client.println("<H1 style=\"color:Green;\">Grow Lights:</H1>");

          client.println("<H2><a href=\"/?GROWON\"\">Turn On Growlights</a>
</H2>");
          client.println("<H2><a href=\"/?GROWOFF\"\">Turn Off Growlights</a>
</H2>");

          client.println("<H1 style=\"color:Green;\">Air Temperature:</H1>");
          client.println(temperatureC);
          client.println("C");
          client.println("
");
          client.println(temperatureF);
          client.println("F");
          client.println("
");
          
          client.println("<H1 style=\"color:Green;\">Relative Humidity:</H1>");
          client.println(hum);
          client.println("%");
          client.println("
");

          client.println("<H1 style=\"color:Green;\">Barometric Pressure:</H1>");
          client.println(event.pressure);
          client.println(" hPa");
          client.println("
");

          client.println("</BODY>");
          client.println("</HTML>");

          delay(10);
          //stopping client
          client.stop();

          // control grow lights
          if (readString.indexOf("?GROWON") > -1)
          {
            digitalWrite(ledPin, HIGH);
          }
          else {
            if (readString.indexOf("?GROWOFF") > -1)
            {
              digitalWrite(ledPin, LOW);
            }
          }
          //clearing string for next read
          readString = "";

        }
      }
    }
  }
}

In the meantime, I will compile a detailed list of sensors that I am using and put together a Fritzing diagram so that you have a reference of what I am working with.

EDIT: I have attached a PNG of the my Fritzing diagram below.

I really appreciate your help. Thank you!

The sketch is not very big and you have not included the SD library, maybe it is possible to make this work with an Arduino Uno.
I don't know how much memory the DHT and the BMP085 libraries use.
Do you still want to add a library for the flow sensors and for the DS18B20 ? Even more perhaps ?
Can you show the compiler output about the memory usage ? They are shown at the end when you compile the sketch.

The "bmp.getEvent(&event);" is called hundreds times per second. Is that okay ? You can use a millis() timer to get the data once per two seconds.

The more someone uses the Arduino Uno, the less someone uses the String class. It can eat memory.

You complete HTML page is both in Flash and in RAM !
You can put it in Flash only with the "F()" macro. Try to add the "F()" macro to every .println() and .print().

// client.println("HTTP/1.1 200 OK");  // without F() macro
client.println(F("HTTP/1.1 200 OK"));  // with F() macro

There are some issues with the schematic:

Do you really use a Adafruit module for the BMP180 ? There are already I2C level shifters on that module. That is very good.
Connect VIN of the module to 5V (only if you have the Adafruit module, other modules will get damaged).

Is the "Temp" sensor a TMP sensor.
They are not very reliable, the temperature that is measured depends on how accurate the 5V is.
If the Ethernet uses power and the 5V drops a little, then you measure a higher temperature.

Is the "Water Temp" a DS18B20 sensor ?
The DS18B20 and DHT sensors should have a pullup resistor of 4k7 or 10k.
The "Water Temp" DS18B20 should get a pullup resistor of 4k7 or 10k, not 220Ω as it is now.

The DHT sensor should have a pullup resistor of 4k7 or 10k to 5V, not 220Ω to 3.3V as it is now.

Every led needs a resistor !
You have to add a resistor between pin 5 and the led. For example 220Ω.
Even if it is just a led, turning on a led like that could be main cause of your problem, despite all the other problems.

You might get into trouble with the flow sensors. I assume they both will be using a interrupt. The DHT and DS18B20 sensors uses a specific timing protocol, and the DHT and the DS18B20 libraries turn off the interrupts to do that timing protocol. Also ledstrips with WS2812 (NeoPixel) turn off interrupts for some time.
This is a common problem. You can exchange the sensors for I2C sensors if those libraries cause a problem.

Note: since you don't gave links to the modules and sensors that you bought, I don't know for example which BMP180 module you have. So I have to write that the Adafruit modules needs 5V at VIN (or else the level shifter does not work) but other modules gets damaged if you put 5V to VIN. I don't know the compiler output that shows the memory usage, so I can't calculate if putting the HTML page in Flash will be enough.

When you change something, please show the new sketch and diagram.

No, the one that I have is not the Adafrruit one, I just could not find the one I had in the fritzing parts. Here is the barometric sensor that I bought: Amazon.com

That bmp.getEvent(&event); is the exact line of code that stops the webpage from even loading.

The temperature sensor that looks like a transistor on the breadboard, yes, is a TMP36. It closely parallels the temperature reading of the temperature probe, but maybe I will look into getting a DS18B20 as a better replacement.

The water temperature probe that I have already has a header on it to change from pullup or pulldown resistor. It communicates over the OneWire and DallasTemperature library.

I will change the resistor and wiring for the DHT sensor. I will use 4.7K to 5V.

I haven't had a problem with the flow sensors yet...but if I do I will try to find I2C versions.

Thank you for catching my mistake on the LED. DOH! I have a 470R with my led on my actual breadboard.

I am sorry I just realized that I posted my old version of the code before. My mistake. Here is the code that is the most updated:

//libraries
#include <SPI.h>
#include <Ethernet.h>
#include <dht.h>
#include <Wire.h>
#include <BMP180.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//ethernet
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };// mac address
byte ip[] = { 10, 0, 0, 60 }; // IP address in LAN
byte gateway[] = { 10, 0, 0, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); 

//strings
String readString;

//integers
int ledPin = 5;
int flowPin0 = 2;
int flowPin1 = 3;
volatile int count0;
volatile int count1;
const int temperaturePin = A0;

//definitions
#define DHT11_PIN A1
#define ONE_WIRE_BUS 4
dht DHT;

//conversions
float hum;
float Celcius = 0;
float Fahrenheit = 0;
double flowRate0;
double flowRate1;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);


void setup() {

  pinMode(ledPin, OUTPUT); //pin selected to control
  pinMode(flowPin0, INPUT);
  pinMode(flowPin1, INPUT);
  
  digitalWrite(ledPin, HIGH);
  delay(500);
  digitalWrite(ledPin, LOW); 
  
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
  sensors.begin();
  attachInterrupt(0, Flow0, RISING);
  attachInterrupt(1, Flow1, RISING);
}

void Flow0(){
  count0++;
}

void Flow1(){
  count1++;
}

void loop() {
//Air Temperature Sensor
  int reading = analogRead(temperaturePin);
  float voltage = reading * 5.0;
  voltage /= 1024.0;
  float temperatureC = (voltage - 0.5) * 100;
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;


//Humidity Sensor  
  int chk = DHT.read11(DHT11_PIN);
  hum = DHT.humidity;
  
//Water Temperature Sensor
  sensors.requestTemperatures();
  Celcius = sensors.getTempCByIndex(0);
  Fahrenheit = sensors.toFahrenheit(Celcius);

//FlowRate Valves
  count0 = 0;
  count1 = 0;
  interrupts();
  delay(1000);
  noInterrupts();

  flowRate0 = (count0 * 3.05);
  flowRate0 = flowRate0 * 60;
  flowRate0 = flowRate0 / 1000;

  flowRate1 = (count1 * 3.05);
  flowRate1 = flowRate1 * 60;
  flowRate1 = flowRate1 / 1000;

//Barometric Pressure Sensor

  
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
        }

        //if HTTP request has ended– 0x0D is Carriage Return \n ASCII
        if (c == 0x0D) {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE> Greenhouse Monitor</TITLE>");
          client.println("<meta http-equiv=refresh content=5>");
          client.println("</HEAD>");
          client.println("<BODY bgcolor=#000000>");
          client.println("<H1 style=\"color:Green;\">Shenango Greenhouse Monitor</H1>");
          client.println("<hr>");
          client.println("
");

          client.println("<H1 style=\"color:Green;\">Grow Lights:</H1>");

          client.println("<H3><a href=\"/?GROWON\"\">Turn On Growlights</a>
</H2>");
          client.println("<H3><a href=\"/?GROWOFF\"\">Turn Off Growlights</a>
</H2>");

          client.println("<H1 style=\"color:Green;\">Air Temperature:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println(temperatureC);
          client.println("C");
          client.println("
");
          client.println(temperatureF);
          client.println("F");
          client.println("</H3>");
          
          client.println("<H1 style=\"color:Green;\">Relative Humidity:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println(hum);
          client.println("%");
          client.println("</H3>");

          client.println("<H1 style=\"color:Green;\">Barometric Pressure:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println();
          client.println(" InHg");
          client.println("</H3>");

          client.println("<H1 style=\"color:Green;\">Water Temperature:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println(Celcius);
          client.println("C");
          client.println("
");
          client.println(Fahrenheit);
          client.println("F");
          client.println("</H3>");

          client.println("<H1 style=\"color:Green;\">Water Acidity:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println();
          client.println(" pH");
          client.println("</H3>");
          
          client.println("<H1 style=\"color:Green;\">Flow Valve 1:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println(flowRate0);
          client.println(" L/Min");
          client.println("</H3>");

          client.println("<H1 style=\"color:Green;\">Flow Valve 2:</H1>");
          client.println("<H3 style=\"color:White;\">");
          client.println(flowRate1);
          client.println(" L/Min");
          client.println("
");
          client.println("</H3>");  

          client.println("</BODY>");
          client.println("</HTML>");

          
          delay(10);
          client.stop();

          // control grow lights
          if (readString.indexOf("?GROWON") > -1){
            digitalWrite(ledPin, HIGH);
          }
          else {
            if (readString.indexOf("?GROWOFF") > -1){
              digitalWrite(ledPin, LOW);
            }
          }
          //clearing string for next read
          readString = "";

          }   
        }
      }
    }
  }

It does not have any of the barometric stuff in it yet.

I have attached an image of the verbose output after I uploaded the code to the Uno. It is named Verbose Output.png. I have also added an updated image of my wiring diagram.

When compiling is ready (before uploading) the compiler shows the Flash and RAM usage. If you press the "Verify" button, then it is at the end. I called it "compiling", because that is what the "Verify" button does, just compiling without uploading.

Don't try to find I2C sensor for the flow sensors. Those are okay, but they use interrupts.
The trouble is the other libraries that turn off interrupts (OneWire, DHT, NeoPixel).
The DS18B20 sensor that I mentioned before is a 1-Wire sensor and uses the OneWire library.

When you get into trouble with the flow sensors, then you need to get rid of those libraries. A simple solution is to use I2C sensors. I mean I2C sensors for temperature and so. But then you have to be sure that you don't have a voltage mismatch on the I2C bus.

Your BMP180 module has a voltage regulator but not level shifters for SDA and SCL. The 4k7 pullup resistors are connected to 3.3V. It will work better if you connect VIN to the Arduino 5V pin. But if you want it to be reliable, then you need a I2C level shifter.

When you connect the VIN of that module to 3.3V, then the voltage regulator on that module will make perhaps 3.2V out of it, and the SDA and SCL are pulled up via 4k7 to 3.2V. The Arduino on the other side needs 3.5V for a valid high in I2C mode.

This part is not okay:

interrupts();
delay(1000);
noInterrupts();

I don't know if you copied that from somewhere else, but it is wrong.

If the BMP180 sensor and/or library is causing the problem, then try to make that work. Use a Arduino board with only the BMP180 sensor connected and nothing else. Use a example sketch that comes with the library.

I have doubts about the Water Temp sensor. Can you give a link to that as well ? When it is a DS18B20 in parasite power mode, then it has only two wires. But that is less reliable than the normal three wires with a pullup resistor.

Perhaps this project is too complex, perhaps it is too much for an Arduino Uno, perhaps there are too many problems to solve via this forum. Do you need the BMP180 or can you do without ?

I have tried with just the arduino board and the example code for the sensor and it prints the barometric pressure in the serial monitor just fine. The problem I believe is in the code. After I add that line of code to start communication on the I2C lines (bmp.getEvent(&event);, it stops the webpage completely, whether the sensor is connected or not.

Also, the specs on the barometric pressure sensor says that it can take 1.8-3.6v. Are you sure that I can connect it to 5V?

Here is the link for the two components for the DS18B20 temperature sensor that I have:

Is there a better way to handle the interrupts than that? I looked up an interrupt example and that is what they used to do it. It does give me the flow rate. Now whether it is accurate or not, I still have yet to test.

I would really like to keep the barometric sensor as I am building a monitor for my garden, and I am also using this as a school project so it would be nice to keep it.

Here is the message after I verify the code:
Sketch uses 19326 bytes (59%) of program storage space. Maximum is 32256 bytes.
Global variables use 1272 bytes (62%) of dynamic memory, leaving 776 bytes for local variables. Maximum is 2048 bytes.

The SMD component with "662" is the voltage regulator.
If you connect VIN to 5V, that might change it from not working to working with others. It is just a voltage change of 0.1V, but because you have a voltage mismatch for SDA and SCL, it is not reliable anyway.

You need a I2C level shifter for SDA and SCL to make it more reliable.

I have never used the Adafruit's universal sensor library.
When I compare it with sensorapi.pde, then I notice that you don't have a bmp.begin() in the setup().

If you want to stay out of trouble, then buy something from a company who gives the schematic, gives a tutorial, and make the module compatible with 3.3V and 5V Arduino boards.

If you want trouble, then just buy any cheap thing and it might work once, or only at room temperature or outside the specifications. More than 90% of the cheap modules with I2C works outside the specification when connected to a 5V Arduino board.

62% of dynamic memory (RAM) is still okay I think. Did you add the "F()" macro ?

The flow meters use interrupts. That is okay. The problem is not the flow meters and not that they use interrupts.
The problem is that some libraries turn off the interrupts for a some time. Those libraries are the problem.

Can you give a link to that example for the flow meter ?
I found this: https://www.bc-robotics.com/tutorials/using-a-flow-sensor-with-arduino/.
That is really not okay.

That link that you sent in your last reply was the exact website that I used for guidance with the flow meters.

I bought a couple of ESP8266s last week and they should be coming in soon. Would those be better to use for this application rather than an ethernet shield on top of an Uno?

Just slighty, about a zillion times better :o That processor runs at 3.3V, so you can connect sensors via I2C and SPI without the silly 3.3V versus 5V trouble.
Why not use the ESP32 ?
This website is very helpful to install it in the Arduino IDE and to know which pins can be used: https://randomnerdtutorials.com/.

Because the Arduino Uno is simple and straightforward, it can do timing specific tasks sometimes better than the ESP boards. Some libraries are for the Arduino Uno only. That means that not everything will suddenly magically start to work. But the Wifi connection is easy when you follow good and reliable tutorials.

josephm3502:
I bought a couple of ESP8266s last week and they should be coming in soon. Would those be better to use for this application rather than an ethernet shield on top of an Uno?

I think you are simply trying to run before you can walk, and throwing money at the problem isn't going to fix anything. The shortcomings of Uno are simply: insufficient memory, clearly not the case here - yet, insufficient pins, of which there is no evidence, or insufficient 3.3v power, again no evidence and not necessarily a good reason anyway.

Your heading is about running I2C and SPI. Everybody can do that except you, and they don't need an ESP8266 to do it.

Indeed, one potentially comforting advantage of the gear you have in your hand now is the ethernet shield you are questioning, which almost certainly has an SD slot you will need. This sits on the SPI bus and I have never heard of any complaints about them.

The ESP8266s, even if of unknown type, are probably not a waste as it is easy to run out of memory with a Uno on this sort of adventure, hence the "yet" above.

Nick_Pyner:
I think you are simply trying to run before you can walk, and throwing money at the problem isn't going to fix anything. The shortcomings of Uno are simply: insufficient memory, clearly not the case here - yet, insufficient pins, of which there is no evidence, or insufficient 3.3v power, again no evidence and not necessarily a good reason anyway.

Your heading is about running I2C and SPI. Everybody can do that except you, and they don't need an ESP8266 to do it.

Indeed, one potentially comforting advantage of the gear you have in your hand now is the ethernet shield you are questioning, which almost certainly has an SD slot you will need. This sits on the SPI bus and I have never heard of any complaints about them.

The ESP8266s, even if of unknown type, are probably not a waste as it is easy to run out of memory with a Uno on this sort of adventure, hence the "yet" above.

My heading simply asked if it was possible. I was simply just asking. Also, I am not complaining about the shield, or anything for that matter. I am just trying to get everything working, but I started this thread asking what I thought maybe was the problem but wasn't sure, hence the question mark.

I also bought the ESP8266's just to start playing with, not to use them for this specific application, but asked if it would be a better alternative.

Koepel:
Just slighty, about a zillion times better :o That processor runs at 3.3V, so you can connect sensors via I2C and SPI without the silly 3.3V versus 5V trouble.
Why not use the ESP32 ?
This website is very helpful to install it in the Arduino IDE and to know which pins can be used: https://randomnerdtutorials.com/.

Because the Arduino Uno is simple and straightforward, it can do timing specific tasks sometimes better than the ESP boards. Some libraries are for the Arduino Uno only. That means that not everything will suddenly magically start to work. But the Wifi connection is easy when you follow good and reliable tutorials.

Good to know. I thought about the ESP32 as well, but just bought a couple 8266s just to try them out. I am sure that Ill have an ESP32 or 2 in my cart very soon :). Thank you for the link as well. I will get the board installed into the IDE. My boards will be here Saturday...

Success! I was able to get the barometric pressure sensor to work with my esp8266. Not sure what was going on, but it works now.

As I was slowly coding all the other libraries in and all of the other sensors, the only one that I could not get working was the flow rate sensors. It seems like the interrupts were preventing the web page from loading. I have referred to many different tutorials for flow rate meters with ESP8266 and all use the interrupts. Is there a way to read them without interrupts or is that the only way to read these?

Thank you,
Joe M

That is the only way, unless the signals are very slow.

The ESP8266 is fast enough to check the flowmeter without interrupts, but it could be busy with Wifi and Bluetooth. Therefor also for the ESP8266 using interrupts is the best choice.

The interrupts are not the problem, but the code that waits a second and turns off the interrupts is the problem.
You can not do this and think that others things still work:

interrupts();
delay(1000);
noInterrupts();

When using the ESP8266, I suggest to remove everything from the sketch that is not from randomnerdtutorials.com. There is a whole lot of bad code out there. You seem to have a special interest in really bad examples and bad modules :o So for you, assume that 95% is wrong and will mess up your precious sketch.

Start here: ESP8266 Interrupts and Timers using Arduino IDE (NodeMCU) | Random Nerd Tutorials.

I could not find a good example for a flowmeter with the ESP8266. Sorry.

It has to be like this: espFlowMeter/espFlowMeter.ino at master · meurig/espFlowMeter · GitHub.

That sketch has these things:

// global variable, volatile
volatile int flow_frequency; // Measures flow sensor pulses

// very small interrupt function:
void flow () // Interrupt function
{
   flow_frequency++;
}

// in setup():
pinMode(RECONFIG_BUTTON_PIN, INPUT_PULLUP); // button default is HIGH
attachInterrupt(RECONFIG_BUTTON_PIN, pressedButton, FALLING);

// All the calculation, using millis() is in the loop()

Those elements are also in this sketch: Measuring Water Usage with NodeMCU +WaterFlow Sensor · GitHub.