[SOLVED] Need SPI Help! Multiple Slaves interfering

Hello all,
I am using an Arduino Mega 2560 with (4) MAX31855 Thermocouple breakoutboards from Adafruit, and an SD shield from (http://www.seeedstudio.com/depot/sd-card-shield-p-492.html?cPath=132_134) Oh I am also using a DS1307 real time clock but that works over I2C and I am not having any issues with that at all.

Individually I can read the TC’s just fine (using the MAX31855 library from rocketscream). I can also read and write to the SD card perfectly fine (using the SdFat library optimized for the Mega 2560). I can do everything I need to just find individually, but when I try to log the temperature readings onto the SD card… they arent getting along. I have been digging into this for a few days and have determined that if i even initialize the SD card, all my thermocouple’s stop reading temperatures. Now I should note that I have this thing built and am making no changes to hardware or pins.

I think this may be due to the fact that the either the thermocouple boards are not synced on the clocking signal that the SD card is on. So I tried every speed option the SdFat.h library allows (SPI_DIV3_SPEED, SPI_HALF_SPEED, SPI_FULL_SPEED, etc) and nothing changed the serial output.

Please see the attached code, with lines 49-57 commented out this is the serial output:

2013/2/22 9:55:2
C1 = 26.50
C2 = 27.50
C3 = 26.75
C4 = 26.25

2013/2/22 9:55:3
C1 = 26.25
C2 = 27.50
C3 = 26.50
C4 = 26.00

With the lines enabled and the SD card is initalized, this is what reads out:

2013/2/22 9:56:12
C1 = 0.00
C2 = 0.00
C3 = 0.00
C4 = 0.00

2013/2/22 9:56:13
C1 = 0.00
C2 = 0.00
C3 = 0.00
C4 = 0.00

Anyhelp I can get would be greatly appreciated! Thanks.

ThermoCouple_Code.txt (2.81 KB)

The SD card shield is explicitly marked as NOT compatible with the Mega. How did you get it to work then? Have you rewired it? If yes, tell us what you did.

The MAX31855 library (https://github.com/rocketscream/MAX31855) doesn't use the hardware SPI, so you should use other pins than the SPI pins to connect the thermocouple breakouts to your Mega.

Yes... I cut pins 10, 11, 12, and 13 off of the SD Shield and have run jumpers from the top header pins over to the SPI pins on the Mega (50, 51, 52, and 53). There are 2 reasons this SD Shield is listed as not Mega compatible. #1 because of the pin setting, and #2 the SdFat libraries up until a week ago were not compatible. Well I have worked around it and, yes you can use this shield on the Mega2560 with this libray: http://code.google.com/p/beta-lib/downloads/detail?name=SdFatBeta20130207.zip&can=2&q=

If you look at my code attached to the original post here is how I have the SPI pins wired up. It is all wired correctly and working. I can read the thermocouples without the SD card code, and I can read and write to the SD card just fine. But I cannot read the thermocouples WHILE the sdcard is initialized.

//Setup SPI Pins
int ChipSelect1 = 4; //Thermocouple #1
int ChipSelect2 = 5; //Thermocouple #2
int ChipSelect3 = 6; //Thermocouple #3
int ChipSelect4 = 7; //Thermocouple #4
int ChipSelect5 = 8; //SD Card Shield
int SlaveIn = 51; //MOSI set by default
int SlaveOut = 50; //MISO set by default
int SCLK = 52; //set by default

Yes... I cut pins 10, 11, 12, and 13 off of the SD Shield and have run jumpers from the top header pins over to the SPI pins on the Mega (50, 51, 52, and 53).

The SS-Pin has to be pin 10 because that pin is not controlled by the SPI hardware.

If you look at my code attached to the original post here is how I have the SPI pins wired up. It is all wired correctly and working. I can read the thermocouples without the SD card code, and I can read and write to the SD card just fine. But I cannot read the thermocouples WHILE the sdcard is initialized.

I read your code and that's why I was telling you to change the pins. You cannot use the SPI pins as digital I/O pins AND hardware SPI I/Os at the same time. Many pins of the Arduino are mulitplexed meaning they could have several usages depending on the configuration. But if you configure the SPI pins to be used by the SPI hardware you cannot use them as standard digital I/O pins concurrently. But that's what you're trying to do.

Thanks for the help thus far. and I have a jumper coming from pin 10 on the SD shield to pin 8 on the board. Once again... [u]everything is wired and working correctly[/u]. This is not a hardware issue. I can read the TC's and I can also read and write to the SD card. But I need help reading the TC's [u]while[/u] the SD card is initialized.

pylon: The MAX31855 library (https://github.com/rocketscream/MAX31855) doesn't use the hardware SPI, so you should use other pins than the SPI pins to connect the thermocouple breakouts to your Mega.

I already have them connected to other pins (listed above)... so how do I fix this on the software issue. The SD.h and SdFat.h libraries callout SPI.h which causes the MAX31855 to no longer read out. The same issue happens if I remove all of the SD card coding and include SPI.h and simply add SPI.begin(); to my sketch. That causes the same issue.

So how do I work around the SPI? And it looks like it should be compliant, just probably a matter of getting or re-writing the library to use the SPI library functions. Chip Select active is LOW just like hardware, etc. And

I have the data sheet and I know what bits are transferred in what order. Is there a simple example of a similar sensor library I could use as an example of transferring bits with the standard SPI callouts.

I already have them connected to other pins (listed above)

No, you haven't:

int SlaveIn = 51; //MOSI set by default
int SlaveOut = 50; //MISO set by default
int SCLK = 52; //set by default

I wrote: take other pins for the MAX31855s, not just the SS pins, all of them.

So how do I work around the SPI?

By using other pins for the MAX31855 or rewriting that library to use the SPI hardware.

pylon:

I wrote: take other pins for the MAX31855s, not just the SS pins, all of them.

Ahh I got you now. So before I start with this... I thought SPI required a common clock and slave buses and only chip select was independant?

Just a few clarification questions: 1. Are you suggesting to make one independent clock and slave pins for the TCs? Or an independant pin for each one? I can read all 4 as it is wired now to defaults single pin clock.

  1. Then have a separate pins for the SD card alternate from the hardware defaults? Or can I continue to use the hardware defaults for the SD card?

Ahh I got you now. So before I start with this... I thought SPI required a common clock and slave buses and only chip select was independant?

SPI does not require that, it just allows it. If you're using the hardware SPI you have the clock and data lines only once and have to share them among different devices. But the library you are using for reading the thermocouples IS NOT USING THE HARDWARE SPI, it uses a software emulation of SPI which you can choose any combination of digital I/O pins for. That's why I wrote: you either have to rewrite that library to use the hardware SPI or move the connections (all of them!) to the MAX31855s to other pins, pins which are not used by the hardware SPI.

  1. Are you suggesting to make one independent clock and slave pins for the TCs? Or an independant pin for each one? I can read all 4 as it is wired now to defaults single pin clock.

Choose a pin for clock (let's say 24), one for MISO (maybe 25) and one for MOSI (can be 26), then you can leave the SS pins as they are (you need a separate one for every device).

  1. Then have a separate pins for the SD card alternate from the hardware defaults? Or can I continue to use the hardware defaults for the SD card?

The SD card library uses the hardware SPI (you cannot specify pin numbers for SCLK, MISO and MOSI, just the SS pin), so you're only free to choose the SS pin, all the other pins are fixed.

The processor SPI is pretty simple. You have to have SS as an output, otherwise it goes into slave mode. Other than that, you can share the (hardware) SPI pins (MOSI/MISO/SCK) amongst different devices. You can change clock modes, etc. while the devices are inactive.

Once you activate one device by bringing its SS low, it should all work.

@Nick Gammon: That's right but the library the OP is using doesn't use the SPI hardware but emulates SPI in software. Using the SPI hardware (for the SD card) and the same pins as digital IOs conflicts (if you don't take extreme care) and that's why I suggested to use other pins (the Mega has enough of them) for the software emulation.

If he wants to, I suppose, but why buy a dog and bark yourself? The hardware SPI is there to be used.

Especially if the library has to be changed anyway.

Especially if the library has to be changed anyway.

Why does the library have to be changed anyway? For accessing the temperature sensors the speed of the hardware SPI isn't needed and this way the library is usable without support for the SPI library (ATtiny). Best would be to have both alternatives available.

pylon: Why does the library have to be changed anyway?

I thought you might have to edit it to change the pins, but it seems they are sent in the constructor, so that's OK.

Big thanks to pylon! I was able to solve this by using alternate pins and letting the software SPI functions read from pins seperate from the hardware SPI bus. I have got all 4 channels working and incorporated into an an example SdFat data logger. its working great!

/*************************************************** 
 * This is an example data logger for 4 thermocouple channels
 Based on Arduino Mega2560, Seeed Studio SD Shield, (4) MAX31855 Breakout Boards, and one DS1307 Real Time Clock from Adafruit
 
 Based on SdFat Data Logger Example, and Adafruit tutorials
  ****************************************************/

#include <Adafruit_MAX31855.h>
#include <Wire.h>
#include <RTClib.h>
#include <SdFat.h>
#include <SdFatUtil.h>

// A simple data logger for the Arduino analog pins
#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()
#define DATA_HEADER "Date, Time, TC#1(C), TC#1(F), TC#2(C), TC#2(F), TC#3(C), TC#3(F), TC#4(C), TC#4(F)"

// Serial print stream
ArduinoOutStream cout(Serial);

RTC_DS1307 RTC; // define the Real Time Clock object
SdFat sd;  // filesystem object 
ofstream logfile; // the logging file

// format date/time
ostream& operator << (ostream& os, DateTime& dt) {
  os << dt.year() << '/' << int(dt.month()) << '/' << int(dt.day()) << ", ";
  os << int(dt.hour()) << ':' << setfill('0') << setw(2) << int(dt.minute());
  os << ':' << setw(2) << int(dt.second()) << setfill(' ');
  return os;
}
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
    DateTime now = RTC.now();
  
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(now.year(), now.month(), now.day());
  
  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(now.hour(), now.minute(), now.second());
}

const int SlavePin1 = 49; //Thermocouple #1
const int SlavePin2 = 47; //Thermocouple #2
const int SlavePin3 = 45; //Thermocouple #3
const int SlavePin4 = 43; //Thermocouple #4
const int SlavePin5 = 10; //SD Card
const int MockSlaveOut = 41;
const int MockClock = 39; 

Adafruit_MAX31855 thermocouple1(MockClock, SlavePin1, MockSlaveOut);
Adafruit_MAX31855 thermocouple2(MockClock, SlavePin2, MockSlaveOut);
Adafruit_MAX31855 thermocouple3(MockClock, SlavePin3, MockSlaveOut);
Adafruit_MAX31855 thermocouple4(MockClock, SlavePin4, MockSlaveOut);

//*****************************************************************************************
// Error handling loop, from the tutorials at ladyada.net listed above
// If there is a problem accessing the SD card, this loop gets called to display a cryptic
// error message. 
void error(char *str)
{
  Serial.print("error: ");
  Serial.print(str);
  while(1);
}
//****************************************************************************************

void setup() {
  digitalWrite(53, HIGH); //Power to    Thermocouple Boards
  Serial.begin(9600);
  cout << endl;
  
#if WAIT_TO_START
  // use pstr to store string in flash and save RAM
  cout << pstr("Type any character to start") << endl;
  while (!Serial.available());
#endif WAIT_TO_START

  // connect to RTC
  Wire.begin();  
  if (!RTC.begin()) error("RTC failed");

  // set date time callback function
  //SdFile::dateTimeCallback(dateTime);
  
  // send date/time to Serial
  DateTime now = RTC.now();
  cout << now << endl;

  // see if the card is present and can be initialized:
  if (!sd.begin(SlavePin5, SPI_HALF_SPEED)) {
    // print detailed error message and halt
    sd.initErrorHalt();
  }
  // create a new file
  char filename[] = "LOG00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[3] = i/10 + '0';
    filename[4] = i%10 + '0';
    if (sd.exists(filename)) continue;
    logfile.open(filename);
    break;
  }
  if (!logfile) error("file create failed");
  
  cout << "Logging to: " << filename << endl;

  logfile << pstr(DATA_HEADER) << endl;
#if ECHO_TO_SERIAL
  cout << pstr(DATA_HEADER) << endl;
#endif ECHO_TO_SERIAL


}

void loop() {
  // use buffer stream to format line
  char buf[100];
  obufstream bout(buf, sizeof(buf));
  
  // fetch the time
  DateTime now = RTC.now();
  
  // log time
  bout << now <<", ";;
  
  //fetch Thermocouple Data  
  float c1 = thermocouple1.readCelsius();
  float f1 = thermocouple1.readFarenheit();
  float c2 = thermocouple2.readCelsius();
  float f2 = thermocouple2.readFarenheit();
  float c3 = thermocouple3.readCelsius();
  float f3 = thermocouple3.readFarenheit();
  float c4 = thermocouple4.readCelsius();
  float f4 = thermocouple4.readFarenheit();
  bout << c1 << ", " << f1 << ", " << c2 << ", " << f2 << ", " << c3 << ", " << f3 << ", " << c4 << ", " << f4;
 
  // write data to file 
  logfile << buf << endl;
  
  logfile.flush();
  
  // check for errors
  if (!logfile) error("write data failed");
  
#if ECHO_TO_SERIAL
  cout << buf << endl;
#endif ECHO_TO_SERIAL
   
  delay(1500);
}

Congrats on solving your problem! :grinning:

I was curious though as to if this would be doable using an Arduino UNO, (4) Adafruit MAX31850K Breakout Boards, a large breadboard, and an Adafruit SD Shield. No need for the Real Time Clock.

And if so, would I need to change anything specifically to the code included in the latest reply? Any thoughts?

Thank you!