MAX 6675 and Datalogger Sheild

Hello,

I am gingerly taking my first steps into the world of Arduino and programming in general.

I am using an Arduino UNO R3 board with an Adafruit Datalogger shield to try and set up some simple data recording of temperatures.

I have used the MAX 6675 thermocouple interface to read the temperature from a K-Type thermocouple using the examples in the respective library with success. I have also managed to get the datalogger to work in isolation using the example sketch in the Arduino IDE.

I have tried to incorporate the two separate sketches so that I can record data from the thermocouple to the SD card. I have attended to some PIN conflicts and can get the program to run on the UNO but I just get a "zero" temperature print out from the MAX 6675 chip. I suspect the problem is to do with SPI conflicts. I have had a play around with the chip select high a low to try and remedy and double checked the electrical wiring but I am not all out of ideas. Any assistance appreciated.

Code is posted below - it is my first attempt at pulling my own sketch together so it is probably not very pretty to the experienced user. Any tips appreciated.

Kind Regards

Ian

#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <MAX6675.h>

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  1000 // mills between entries (reduce to take more/faster data)

// how many milliseconds before writing the logged data permanently to disk set it to the LOG_INTERVAL to write each time (safest) 
// set it to 10*LOG_INTERVAL to write all data every 10 datareads, you could lose up to the last 10 reads if power is lost but it uses less power and is much faster!
#define SYNC_INTERVAL 5000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    1 // Wait for serial input in setup()

// the digital pins that connect to the LEDs
#define redLEDpin 4
#define greenLEDpin 3

// The analog pins that connect to the sensors
#define photocellPin 0           // analog 0
#define tempPin 1                // analog 1
#define BANDGAPREF 14            // special indicator that we want to measure the bandgap
#define aref_voltage 3.3         // we tie 3.3V to ARef and measure it with a multimeter!
#define bandgap_voltage 1.1      // this is not super guaranteed but its not -too- off

// The MAX 6675 PIN definitions
int LED1 = 5;             // Status LED Pin
int CS = 9;               // CS pin on MAX6675
int SO = 12;              // SO pin of MAX6675
int MAX_SCK = 13;         // SCK pin of MAX6675
int units = 1;            // Units to readout temp (0 = raw, 1 = ˚C, 2 = ˚F)
float temperature = 0.0;  // Temperature output variable

RTC_DS1307 RTC; // define the Real Time Clock object
const int sdChipSelect = 10; // for the data logging shield, we use digital pin 10 for the SD cs line
File logfile; // the logging file
MAX6675 temp(CS,SO,MAX_SCK,units); // Initialize the MAX6675 Library for our chip

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  digitalWrite(redLEDpin, HIGH);   // red LED indicates error

  while(1);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();
  
  // use debugging LEDs
  pinMode(redLEDpin, OUTPUT);
  pinMode(greenLEDpin, OUTPUT);
  pinMode(LED1, OUTPUT);
  
#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START

  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(sdChipSelect, OUTPUT);
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(sdChipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
  
  if (! logfile) {
    error("couldnt create file");
  }
  
  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }
  

  logfile.println("millis,stamp,datetime,light,temp,vcc,t-couple");    
#if ECHO_TO_SERIAL
  Serial.println("millis,stamp,datetime,light,temp,vcc,t-couple");
#endif //ECHO_TO_SERIAL
 
  // If you want to set the aref to something other than 5v
  analogReference(EXTERNAL);
}

void loop(void)
{
  DateTime now;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  digitalWrite(greenLEDpin, HIGH);
  
  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");  
#endif


  now = RTC.now();   // fetch the time
  // log time
  logfile.print(now.unixtime()); // seconds since 1/1/1970
  logfile.print(", ");
  logfile.print('"');
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print('"');
#if ECHO_TO_SERIAL
  Serial.print(now.unixtime()); // seconds since 1/1/1970
  Serial.print(", ");
  Serial.print('"');
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print('"');
#endif //ECHO_TO_SERIAL


  // Read Analogue pins
  analogRead(photocellPin);
  delay(10); 
  int photocellReading = analogRead(photocellPin);  
  
  analogRead(tempPin); 
  delay(10);
  int tempReading = analogRead(tempPin);    
  
  // converting that reading to voltage, for 3.3v arduino use 3.3, for 5.0, use 5.0
  float voltage = tempReading * aref_voltage / 1024;  
  float temperatureC = (voltage - 0.5) * 100 ;
  float temperatureF = (temperatureC * 9 / 5) + 32;
  
  
  
  
  // Read the temp from the MAX6675
        digitalWrite(CS, HIGH);
        digitalWrite(sdChipSelect, LOW);
	temperature = temp.read_temp();
		digitalWrite(LED1, HIGH);
                digitalWrite(LED1, LOW);

	if(temperature < 0) {                   
		// If there is an error with the TC, temperature will be < 0
		Serial.print("Thermocouple Error on CS");
		Serial.println( temperature ); 
		digitalWrite(LED1, HIGH);
	} else {
		digitalWrite(LED1, LOW);
	}
 
  
  
  
  logfile.print(", ");    
  logfile.print(photocellReading);
  logfile.print(", ");    
  logfile.print(temperatureF);
  logfile.print(", ");    
  logfile.print(temperature);
#if ECHO_TO_SERIAL
  Serial.print(", ");   
  Serial.print(photocellReading);
  Serial.print(", ");    
  Serial.print(temperatureF);
  Serial.print(", ");    
  Serial.print(temperature);
#endif //ECHO_TO_SERIAL

  // Log the estimated 'VCC' voltage by measuring the internal 1.1v ref
  analogRead(BANDGAPREF); 
  delay(10);
  int refReading = analogRead(BANDGAPREF); 
  float supplyvoltage = (bandgap_voltage * 1024) / refReading; 
  
  logfile.print(", ");
  logfile.print(supplyvoltage);
#if ECHO_TO_SERIAL
  Serial.print(", ");   
  Serial.print(supplyvoltage);
#endif // ECHO_TO_SERIAL

  logfile.println();
#if ECHO_TO_SERIAL
  Serial.println();
#endif // ECHO_TO_SERIAL

  digitalWrite(greenLEDpin, LOW);

  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  
  // blink LED to show we are syncing data to the card & updating FAT!
  digitalWrite(redLEDpin, HIGH);
  logfile.flush();
  digitalWrite(redLEDpin, LOW);
  
}

I have just double checked wiring and is ok as I can run the data logger example sketch on its own fine and the MAX6675 Example sketch in its own too so I am guessing it is something to do with 'my' coding.

Any help appreciated.....

I am hoping somebody can help. Is there extra info I can provide or a different way of phrasing the question!!!??

I didn't look carefully at your code but you shouldn't have to play around with the chip selects in your sketch. The SD library manages this by itself and whatever MAX6675 library you're using probably does so as well.

These two lines look doomed to create a failure:

digitalWrite(sdChipSelect, LOW);
temperature = temp.read_temp();

Chip select is usually active low. So it appears that you're enabling the SD card and then trying to read the temperature chip. This probably ends up enabling two chip selects at the same time.

Try removing all of the calls to digitalWrite() for sdChipSelect and CS.

Thanks for replying.

In added the following line as without it the SD card would not initialise in the original code!?

digitalWrite(sdChipSelect,LOW);

Why do you suspect the following line is doomed to failure? It works fine in the standalone MAX6675 example code?

temperature = temp.read_temp();

I will have another play tonight.

I meant the two lines together. The first one, the digitalWrite call, enables the SD card. You don't want the SD card enabled when you're trying to talk to the temperature chip.

But I'm just guessing.

Good luck.

This is starting to bug me now. Whatever I seem to try is not helping, I just keep getting a "zero" reading from the sensor.

I have removed the digitaWrite() statements and simplified the code to make debugging easier (inserted below).

I don't have anything fancy going on with the wiring; it is just as per the pinout definitions in the code. I do not have any pull up resistors installed.

Anybody got any bright ideas.....???

Thanks in advance

#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <MAX6675.h>

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  1000 // mills between entries (reduce to take more/faster data)

// how many milliseconds before writing the logged data permanently to disk set it to the LOG_INTERVAL to write each time (safest) 
// set it to 10*LOG_INTERVAL to write all data every 10 datareads, you could lose up to the last 10 reads if power is lost but it uses less power and is much faster!
#define SYNC_INTERVAL 5000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    1 // Wait for serial input in setup()

// The MAX 6675 PIN definitions
int LED1 = 5;             // Status LED Pin
int CS = 9;               // CS pin on MAX6675
int SO = 12;              // SO pin of MAX6675
int MAX_SCK = 13;         // SCK pin of MAX6675
int units = 1;            // Units to readout temp (0 = raw, 1 = ˚C, 2 = ˚F)
float temperature = 0.0;  // Temperature output variable

RTC_DS1307 RTC; // define the Real Time Clock object
const int sdChipSelect = 10; // for the data logging shield, we use digital pin 10 for the SD cs line
File logfile; // the logging file
MAX6675 temp(CS,SO,MAX_SCK,units); // Initialize the MAX6675 Library for our chip

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);

  while(1);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();
  
#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START

  // initialize the SD card
  pinMode(sdChipSelect, OUTPUT);
  pinMode(CS, OUTPUT);
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:

  
  // see if the card is present and can be initialized:
  if (!SD.begin(sdChipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
  
  if (! logfile) {
    error("couldnt create file");
  }
  
  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }
  

  logfile.println("datetime,t-couple");    
#if ECHO_TO_SERIAL
  Serial.println("datetime,t-couple");
#endif //ECHO_TO_SERIAL

}

void loop(void)
{
  DateTime now;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  // log milliseconds since starting
  uint32_t m = millis();

  now = RTC.now();   // fetch the time
  // log time
  
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print('"');
#if ECHO_TO_SERIAL
 
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
#endif //ECHO_TO_SERIAL

  // Read the temp from the MAX6675

	temperature = temp.read_temp();
           
	if(temperature < 0) {                   
		// If there is an error with the TC, temperature will be < 0
		Serial.println("Thermocouple Error");
	} else {
	}
 
  logfile.print(", ");    
  logfile.println(temperature);
  
#if ECHO_TO_SERIAL
  Serial.print(", ");    
  Serial.println(temperature);
#endif //ECHO_TO_SERIAL

  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  
  logfile.flush();
  
}

I wonder if mixing hardware and software SPI is causing a problem? I tried that just now with my board (I have three SPI devices) and it didn't go too well. I'm not all that clear on who drives the SCLK and MOSI pins between hardware SPI writes. Maybe it doesn't matter, but it's something to look into.

Can you temporarily move your MAX6675 clock and data lines to something other than pins 11-13?

Or you can ignore me as I'm just guessing again.

Thanks again for your reply.

Worth a try, but I thought the idea of SPI was to minimise number of pins used to connect multiple sources! I will try out your theory and also look for an alternative max 6675 library (when I manage to get the laptop back off my wife!)

The SPI bus is meant to be shared but there are different modes of operation. If you have devices that use the bus in conflicting ways then each library has to reconfigure it prior to access, otherwise it won't work properly. Many libraries don't bother to configure the bus other than once at initialization.

Thanks jboyton

I tried reinitialising the library for a read of the MAX6675 before the temperature read as follows:

MAX6675 temp(CS,SO,MAX_SCK,units);
temperature = temp.read_temp();

But it did not help!

So I have changed MAX_SCK from 13 to 5 and SO 12 to 16 and it worked. ::slight_smile:

Now the problem is I have run out of pins for my project! :frowning:

The MAX 6675 code is below. Can you see anything there that may cause an obvious conflict with the SD card SPI access (arduino default library #include <SD.h>)

/*
  MAX6675.cpp - Library for reading temperature from a MAX6675.

  This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
  http://creativecommons.org/licenses/by-sa/3.0/
*/

#include <MAX6675.h>

MAX6675::MAX6675(uint8_t CS_pin, uint8_t SO_pin, uint8_t SCK_pin, uint8_t units)
{
    pinMode(CS_pin, OUTPUT);
    pinMode(SO_pin, INPUT);
    pinMode(SCK_pin, OUTPUT);

    digitalWrite(CS_pin, HIGH);

    _CS_pin = CS_pin;
    _SO_pin = SO_pin;
    _SCK_pin = SCK_pin;
    _units = units;
}

float MAX6675::read_temp()
{
    uint16_t value = 0;
    uint8_t error_tc = 0;
    float temp = 0.0;

    /*
      Initiate a temperature conversion. According to MAX's tech notes FAQ's
      for the chip, Line going high initiates a conversion, which means, we
      need to clock the chip low to high to initiate the conversion, then wait
      for the conversion to be complete before trying to read the data from
      the chip.
    */
    digitalWrite(_CS_pin,LOW);
    delay(2);
    digitalWrite(_CS_pin,HIGH);
    delay(220);

    /* Read the chip and return the raw temperature value */

    /*
      Bring CS pin low to allow us to read the data from
      the conversion process
    */
    digitalWrite(_CS_pin,LOW);

    /* Cycle the clock for dummy bit 15 */
    digitalWrite(_SCK_pin,HIGH);
    delay(1);
    digitalWrite(_SCK_pin,LOW);

     /*
      Read bits 14-3 from MAX6675 for the Temp. Loop for each bit reading
      the value and storing the final value in 'temp'
    */
    for (int i=11; i>=0; i--) {
        digitalWrite(_SCK_pin,HIGH);
        value += digitalRead(_SO_pin) << i;
        digitalWrite(_SCK_pin,LOW);
    }

    /* Read the TC Input inp to check for TC Errors */
    digitalWrite(_SCK_pin,HIGH);
    error_tc = digitalRead(_SO_pin);
    digitalWrite(_SCK_pin,LOW);

    /*
      Read the last two bits from the chip, faliure to do so will result
      in erratic readings from the chip.
    */
    for (int i=1; i>=0; i--) {
        digitalWrite(_SCK_pin,HIGH);
        delay(1);
        digitalWrite(_SCK_pin,LOW);
    }

    // Disable Device
    digitalWrite(_CS_pin, HIGH);

    /*
      Keep in mind that the temp that was just read is on the digital scale
      from 0˚C to 1023.75˚C at a resolution of 2^12.  We now need to convert
      to an actual readable temperature (this drove me nuts until I figured
      this out!).  Now multiply by 0.25.  I tried to avoid float math but
      it is tough to do a good conversion to ˚F.  THe final value is converted
      to an int and returned at x10 power.

      2 = temp in deg F
      1 = temp in deg C
      0 = raw chip value 0-4095
    */
    if(_units == 2) {
        temp = (value*0.25) * 9.0/5.0 + 32.0;
    } else if(_units == 1) {
        temp = (value*0.25);
    } else {
        temp = value;
    }

    /* Output negative of CS_pin if there is a TC error, otherwise return 'temp' */
    if(error_tc != 0) {
        return -_CS_pin;
    } else {
        return temp;
    }
}

I have had a look through the library code and the only thing I can think is that SD.h could be leaving the SDK pin in an incompatible status for a read.

The only other thing that jumps out to me is:

/*
Initiate a temperature conversion. According to MAX's tech notes FAQ's
for the chip, Line going high initiates a conversion, which means, we
need to clock the chip low to high to initiate the conversion, then wait
for the conversion to be complete before trying to read the data from
the chip.

Is this compatible with SD.h? I would have thought so since SD.h should have released control of the SCK & SO pin during the temp.read_temp(); routine call.....

Any observations or recommendations for tweaking the library code?

Many thanks

UPDATE

I just changed MAX_SCK & SO individually to isolate the culprit. It is the SCK pin that is causing the trouble.

Would be great if I can get to the bottom of why there is a conflict with the SCK pin in the library code. Anyone with experienced eyes been able to take a quick look in post #10 and see if anything jumps out?

Ugh! Who wrote that library? I don't consider myself to be a very good source of knowledge in this area but that looks terrible to me.

The constructor MAX6675::MAX6675() is called when the object is instantiated. This is the very first thing the program does, before it runs any of the real code, so it shouldn't be doing any real actions like writing to pins. At that stage of the program execution, the pins may not even be writable.

For setting up pins etc, that's usually accomplished with a begin() function.

The actual temperature reading code is bit-banging the SPI in a very inefficient way. You could probably use the hardware SPI and get a speedup of 1000 times faster.

Nonetheless, it works on its own, so why doesn't it work when hardware SPI is active?

If the Arduino is in SPI slave mode then SCK is an input, not an output. But it shouldn't be in slave mode unless the hardware CS pin is low. It looks like this is the sdCardSelect in your code, which is an output and should be written high while communicating with the MAX6675.

For my own projects where I am using SPI devices that possibly have different configurations, I always save and restore the SPCR register so that the different configurations can work without interference. For example:

MAX6675::begin() {
  //set up SPI stuff - call SPI.begin(), which modifies the SPCR register
  _spcr = SPCR; //save my preferred configuration of SPCR
}

MAX6675::readTemp() {
  byte temp_spcr = SPCR; //save the other device's preferred SPCR
  SPCR = _spcr; //overwrite SPCR with the configuration we set up for ourselves
  //...
  //do SPI communications here
  //...
  SPCR = temp_spcr; //restore the other device's SPCR settings
}

(Actually I also save and restore SPCR in the begin() function too.)

Maybe you should save some more of the registers governing the SPI pins (the port registers) and restore them after each temperature reading.

Ugh! Who wrote that library? I don't consider myself to be a very good source of knowledge in this area but that looks terrible to me.

That bad?

You could probably use the hardware SPI and get a speedup of 1000 times faster

The max clock rate for this device is about 4 MHz and 2 MHz is recommended. I wrote a software SPI library that runs at 2 MHz so 1000 seems like a stretch.

It looks like this device needs SPI mode 1. The default SPI mode is zero so that could cause the wrong value to be read.

If you want to use hardware SPI, try mode 1 at 2 MHz.

If you have enough pins, bit bang is probably a good choice for this device so I don't think the author deserves the wrath of MorganS.

We getting error " no fuction for call to max6675(int&...) the code not work