Datalogger + Thermocouple Mux

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

Hmmm... I put it on the 'scope, and it's not any sort of problem with chip-selects.

Best guess is that it wasn't spending long enough integrating the thermocouples. There were a couple of useless delay(1) statements in readThermocouple, so it was taking 64ms of the 100ms just to read the data. Altogether, it was only spending about 28ms integrating per sensor.

I adjusted the timing, and got rid of the delays in readThermocouple. It now spends the full 100ms integrating.

Here's the sketch. I added the two sections of code you posted.
wilheldp wrote:one little fix (if (channel == instead of if (channel = in the main loop).
And I fixed that. D'oh!! If I had a nickle for every time I've done that... :roll:

Tip: whenever I'm testing against a constant value, I usually put the constant on the left side of the operator. That way, the compiler will flag an error whenever I'm dumb enough to use "=" instead of "==".

Code: Select all

      if (0 == channel)                   //if we finished all 8 conversions, 
(Of course, it doesn't work when I'm dumb enough to forget to do it that way).

Code: Select all

// TCMux shield demo by Ocean Controls
// Sends the data to a serial terminal
// Type @NS1<cr> (don't type <cr> it's the carriage return character, just hit enter) to set the number of sensor to 1
// Type @NS8<cr> to set the number of sensors to 8
// Type @UD1<cr> to set the update delay to 1 second
// Type @SV<cr> to save the number of sensors and update delay variables to eeprom

#include "SD.h"

#include <string.h> //Use the string Library
#include <ctype.h>
#include <EEPROM.h>

//#define SHOWMEYOURBITS // Display the raw 32bit binary data from the MAX31855

#define PINEN 7 //Mux Enable pin
#define PINA0 4 //Mux Address 0 pin
#define PINA1 5 //Mux Address 1 pin
#define PINA2 6 //Mux Address 2 pin
#define PINSO 12 //TCAmp Slave Out pin (MISO)
#define PINSC 13 //TCAmp Serial Clock (SCK)
#define PINCS 9  //TCAmp Chip Select Change this to match the position of the Chip Select Link

int Temp[8], SensorFail[8];
float floatTemp, floatInternalTemp;
char failMode[8];
int internalTemp, intTempFrac;
unsigned int Mask;
//char data[16];
char i, channel, NumSensors =1, UpdateDelay;
char Rxchar, Rxenable, Rxptr, Cmdcomplete, R;
char Rxbuf[32];
char adrbuf[3], cmdbuf[3], valbuf[12];
int val = 0, Param;     
unsigned long conversionTime, intervalTime;

// The objects to talk to the SD card
    Sd2Card card;
    SdVolume volume;
    SdFile root;
    SdFile file;
    File logfile; // The object that represents our datafile
    const byte chipSelect = 10; // chip select pin for SD card, 10 for regular Arduino, 53 for Mega


    //*****************************************************************************************
    // 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 selectThermocouple(int channel)
{
    digitalWrite(PINCS, LOW); //Take SPI bus

    switch (channel) //select channel
    {
      case 0:
        digitalWrite(PINA0, LOW); 
        digitalWrite(PINA1, LOW); 
        digitalWrite(PINA2, LOW);
      break;
      case 1:
        digitalWrite(PINA0, HIGH); 
        digitalWrite(PINA1, LOW); 
        digitalWrite(PINA2, LOW);
      break;
      case 2:
        digitalWrite(PINA0, LOW); 
        digitalWrite(PINA1, HIGH); 
        digitalWrite(PINA2, LOW);
      break;
      case 3:
        digitalWrite(PINA0, HIGH); 
        digitalWrite(PINA1, HIGH); 
        digitalWrite(PINA2, LOW);
      break;
      case 4:
        digitalWrite(PINA0, LOW); 
        digitalWrite(PINA1, LOW); 
        digitalWrite(PINA2, HIGH);
      break;
      case 5:
        digitalWrite(PINA0, HIGH); 
        digitalWrite(PINA1, LOW); 
        digitalWrite(PINA2, HIGH);
      break;
      case 6:
        digitalWrite(PINA0, LOW); 
        digitalWrite(PINA1, HIGH); 
        digitalWrite(PINA2, HIGH);
      break;
      case 7:
        digitalWrite(PINA0, HIGH); 
        digitalWrite(PINA1, HIGH); 
        digitalWrite(PINA2, HIGH);
      break;
      default:
        Serial.print("invalid thermocouple channel: "); Serial.println(channel);
        break;
    }
    
    delay(5);                  //allow for leisurely digestion
    digitalWrite(PINCS, HIGH); //Release SPI bus; Begin conversion
}

float convertTemperature(int channel)
{
      Serial.print("#");
      Serial.print(channel+1,DEC);
      Serial.print(": ");
      if (SensorFail[channel] == 1)
      {
        Serial.print("FAIL");
        if ((failMode[channel] & 0b0100) == 0b0100)
        {
          Serial.print(" SHORT TO VCC");
        }
        if ((failMode[channel] & 0b0010) == 0b0010)
        {
          Serial.print(" SHORT TO GND");
        }
        if ((failMode[channel] & 0b0001) == 0b0001)
        {
          Serial.print(" OPEN CIRCUIT");
        }
      }
      else
      {
        floatTemp = (float)Temp[channel] * 0.25;
        Serial.print(floatTemp,2);
        //This bit doesn't work for neg temps
        /*Serial.print(Temp[channel]>>2,DEC);
        Serial.print(".");
        if ((Temp[channel] & 0b11) == 0b00)
        {
          Serial.print("00");
        }
        if ((Temp[channel] & 0b11) == 0b01)
        {
          Serial.print("25");
        }
        if ((Temp[channel] & 0b11) == 0b10)
        {
          Serial.print("50");
        }
        if ((Temp[channel] & 0b11) == 0b11)
        {
          Serial.print("75");
        }
        */
        Serial.print(" degC");
      //}
        //delay(1000);
    }//end reading sensors
    //Serial.println("");
    Serial.print(" Int: ");
    floatInternalTemp = (float)internalTemp * 0.0625;
    Serial.print(floatInternalTemp,4);
    //This doesn't work for negative values
    /*
    Serial.print(internalTemp>>4);
    Serial.print(".");
    intTempFrac = (internalTemp & 0x0F)*625;
    Serial.print(intTempFrac/1000);
    intTempFrac = intTempFrac%1000;
    Serial.print(intTempFrac/100);
    intTempFrac = intTempFrac%100;
    Serial.print(intTempFrac/10);
    intTempFrac = intTempFrac%10;
    Serial.print(intTempFrac/1);
    */
    Serial.print(" degC");
    Serial.println("");

  return floatTemp;
}

float readThermocouple(int channel)
{
    Temp[channel] = 0;
    failMode[channel] = 0;
    SensorFail[channel] = 0;
    internalTemp = 0;
    
    digitalWrite(PINCS, LOW); //Take SPI bus, stop conversion
    
    for (i=31;i>=0;i--)
    {
        digitalWrite(PINSC, HIGH);
     //   delay(1);
        
         //print out bits
       #ifdef SHOWMEYOURBITS
       if (digitalRead(PINSO)==1)
        {
          Serial.print("1");
        }
        else
        {
          Serial.print("0");
        }
        #endif
        
      if ((i<=31) && (i>=18))
      {
        // these 14 bits are the thermocouple temperature data
        // bit 31 sign
        // bit 30 MSB = 2^10
        // bit 18 LSB = 2^-2 (0.25 degC)
        
        Mask = 1<<(i-18);
        if (digitalRead(PINSO)==1)
        {
          if (i == 31)
          {
            Temp[channel] += (0b11<<14);//pad the temp with the bit 31 value so we can read negative values correctly
          }
          Temp[channel] += Mask;
          //Serial.print("1");
        }
        else
        {
         // Serial.print("0");
        }
      }
      //bit 17 is reserved
      //bit 16 is sensor fault
      if (i==16)
      {
        SensorFail[channel] = digitalRead(PINSO);
      }
      
      if ((i<=15) && (i>=4))
      {
        //these 12 bits are the internal temp of the chip
        //bit 15 sign
        //bit 14 MSB = 2^6
        //bit 4 LSB = 2^-4 (0.0625 degC)
        Mask = 1<<(i-4);
        if (digitalRead(PINSO)==1)
        {
          if (i == 15)
          {
            internalTemp += (0b1111<<12);//pad the temp with the bit 31 value so we can read negative values correctly
          }
          
          internalTemp += Mask;//should probably pad the temp with the bit 15 value so we can read negative values correctly
          //Serial.print("1");
        }
        else
        {
         // Serial.print("0");
        }
        
      }
      //bit 3 is reserved
      if (i==2)
      {
        failMode[channel] += digitalRead(PINSO)<<2;//bit 2 is set if shorted to VCC
      }
      if (i==1)
      {
        failMode[channel] += digitalRead(PINSO)<<1;//bit 1 is set if shorted to GND
      }
      if (i==0)
      {
        failMode[channel] += digitalRead(PINSO)<<0;//bit 0 is set if open circuit
      }
      
      
      digitalWrite(PINSC, LOW);
     // delay(1);
      //delay(1);
    }
    
    digitalWrite(PINCS, HIGH); //Release SPI bus
    
    return convertTemperature(channel);
}



#define CONVERSION_TIME      100            /* number of milliseconds per conversion */
#define CONVERSION_PERIOD   1000            /* start a new set of 8 conversions once every second */
#define CONVERSION_INTERVAL (CONVERSION_PERIOD - (CONVERSION_TIME * 8)) /*interval between the end of a conversion cycle and the start of the next */

void setup()   
{     
  Serial.begin(9600);  
  Serial.println("TCMUXV3");
        
           
      //initialize the SD card
      if (!SD.begin(chipSelect)) {
        error("SD Card failedor not present");
      }
      
      if (!card.init()) error("card.init, Reformat card");
      Serial.print("SD Card Initialized");
      delay(1500);

      
      
      // create a new file
      char name[] = "TEMPS00.CSV"; //File names will follow this format, and increment automatically
      for (uint8_t x = 0; x < 100; x++) {
        name[5] = x/10 + '0';
        name[6] = x%10 + '0';
        if (! SD.exists(name)) {
         logfile = SD.open(name, FILE_WRITE);
         break; 
        }
      }
      if (! logfile) {
        error(" Could not    create a file.");
      }
    

      //Write the output file header row to our file so that we can identify the data later
      logfile.println("date,time,Ch1,Ch2,Ch3,Ch4,Ch5,Ch6,Ch7,Ch8");
      logfile.flush();
      delay(350);
      
  if (EEPROM.read(511)==1)
  {
    NumSensors = EEPROM.read(0);
    UpdateDelay = EEPROM.read(1);
  }
  pinMode(PINEN, OUTPUT);     
  pinMode(PINA0, OUTPUT);    
  pinMode(PINA1, OUTPUT);    
  pinMode(PINA2, OUTPUT);    
  pinMode(PINSO, INPUT);    
  pinMode(PINCS, OUTPUT);    
  pinMode(PINSC, OUTPUT);    
  
  digitalWrite(PINEN, HIGH);   // enable on
  digitalWrite(PINA0, LOW); // low, low, low = channel 1
  digitalWrite(PINA1, LOW); 
  digitalWrite(PINA2, LOW); 
  digitalWrite(PINSC, LOW); //put clock in low
  
  channel = 0;
  selectThermocouple(channel);            //start the first conversion
  conversionTime = millis();              //timestamp start of first conversion
  intervalTime = conversionTime - CONVERSION_INTERVAL;  //back-date the start of the first conversion interval
}

void loop()                     
{
  if (millis() > (intervalTime + CONVERSION_INTERVAL))  //start a new set of 8 conversions every CONVERSION_INTERVAL milliseconds
  {
    if (millis() > (conversionTime + CONVERSION_TIME))  //every CONVERSION_TIME milliseconds
    {
      readThermocouple(channel);    //stop conversion and read the current thermocouple
      
      channel = (channel + 1) % 8;  //get the next channel number
      
      selectThermocouple(channel);  //select the next thermocouple and begin conversion
      
      conversionTime = millis();    //reset conversion time-out
      
      if (0 == channel)                   //if we finished all 8 conversions, 
        intervalTime = conversionTime;    // reset the conversion interval time-out.
      
    } //end CONVERSION_TIME
  } //end CONVERSION _INTERVAL
 
  
  if (Serial.available() > 0)    // Is a character waiting in the buffer?
  {
    Rxchar = Serial.read();      // Get the waiting character

    if (Rxchar == '@')      // Can start recording after @ symbol
    {
      if (Cmdcomplete != 1)
      {
        Rxenable = 1;
        Rxptr = 1;
      }//end cmdcomplete
    }//end rxchar
    if (Rxenable == 1)           // its enabled so record the characters
    {
      if ((Rxchar != 32) && (Rxchar != '@')) //dont save the spaces or @ symbol
      {
        Rxbuf[Rxptr] = Rxchar;
        //Serial.println(Rxchar);
        Rxptr++;
        if (Rxptr > 13) 
        {
          Rxenable = 0;
        }//end rxptr
      }//end rxchar
      if (Rxchar == 13) 
      {
        Rxenable = 0;
        Cmdcomplete = 1;
      }//end rxchar
    }//end rxenable

  }// end serial available


   
  if (Cmdcomplete == 1)
  {
    Cmdcomplete = 0;
     cmdbuf[0] = toupper(Rxbuf[1]); //copy and convert to upper case
     cmdbuf[1] = toupper(Rxbuf[2]); //copy and convert to upper case
     cmdbuf[2] = 0; //null terminate        Command = Chr(rxbuf(3)) + Chr(rxbuf(4))
     //   Command = Ucase(command)
  //Serial.println(cmdbuf);
     valbuf[0] = Rxbuf[3]; //        Mystr = Chr(rxbuf(5))
        R = Rxptr - 1;
            for (i = 4 ; i <= R ; i++)//For I = 6 To R
            {
                valbuf[i-3] = Rxbuf[i]; //Mystr = Mystr + Chr(rxbuf(i))
            }
     valbuf[R+1] = 0; //null terminate
     Param = atoi(valbuf);//   Param = Val(mystr)

     //Serial.println(Param); //   'Print "Parameter: " ; Param

       
              if (strcmp(cmdbuf,"NS")==0)       //NumSensors
              {
                   //'Print "command was ON"
                   if ((Param <= 8) && (Param > 0)) 
                   {
                      NumSensors = Param;                   
                   }
                   
              }
              if (strcmp(cmdbuf,"UD")==0)       //UpdateDelay
              {
                   //'Print "command was ON"
                   if ((Param <= 60) && (Param >= 0)) 
                   {
                      UpdateDelay = Param;                   
                   }
                   
              }
              if (strcmp(cmdbuf,"SV")==0)       //Save
              {
                   EEPROM.write(0,NumSensors);
                   EEPROM.write(1,UpdateDelay);
                   EEPROM.write(511,1);
              }
         }
}

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

I don't think it is a timing issue because I actually changed the timers to...

Code: Select all

  #define CONVERSION_TIME      1000            
    #define CONVERSION_PERIOD   10000   
during my previous tests.

I have an email in to Ocean to see if they know of any issues with competition on the SPI bus. I will report back when I hear from them (they are in Australia, so it takes about a day to communicate with them).

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

wilheldp wrote:I have an email in to Ocean to see if they know of any issues with competition on the SPI bus.
I scoped it. After initialization, pin 10 stays high the entire time. It's not an SPI problem.

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

I'm starting to think it's a soldering or hardware problem. I'm even having trouble consistently getting CardInfo to recognize my SD card immediately after formatting it with the approved SD Formatter utility.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

If you'd like, post some clear, detailed pictures of both sides of the boards, and we'll have a look.

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

I re-touched all of the solder joints on the headers on both shields, and I don't see any voids, cold joints or shorts. I've been doing some more testing today, and I no longer think it's a hardware issue.

Both of your re-written programs work, and my overall program works...as long as I take out the SD card initialization code. If I comment out everything from " if (!SD.begin(chipSelect)) {" to " error(" Could not create a file.");" the thermocouples read perfectly. If I only uncomment the !SD.begin if loop, it breaks everything. I turned on the "SHOWMEYOURBITS" functionality built into the Ocean sketch, I see that it reads all zeros from the mux whenever the SD initialization is turned on, but it gets valid data with the SD code taken out.

Is there another way to initialize the SD card? What in the world could be interfering with the Mux communications if it isn't the SPI bus?

I haven't heard back from Ocean, but before purchasing the shield, one of their tech support people told me that "it uses D4, D5, D6, D7, D13, D14 plus one of D8, D9 or D10 on the standard arduino headers." The board I got is wired to use D9 for the chip select.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

wilheldp wrote:If I comment out everything from " if (!SD.begin(chipSelect)) {" to " error(" Could not create a file.");" the thermocouples read perfectly. If I only uncomment the !SD.begin if loop, it breaks everything.
So, if I'm reading this right, the section where it creates the file is what is causing the problem?

In that case, I'm thinking that you may be running out of SRAM. When you create the file, whatever buffers and control blocks SD dynamically allocates are enough to push it over the edge.

There's plenty of wasted SRAM in that sketch, so you've got a pretty good chance of getting it to work. The easiest thing to do is to change all the literal strings in print statements so that the strings are stored in flash. You do that with the F() macro:
Example - change

Code: Select all

      Serial.print("SD Card Initialized");
to

Code: Select all

      Serial.print(F("SD Card Initialized"));
I believe that should also work with logfile.print(). However, it won't work with the error("xxxx") calls. I'd get rid of the error() function, and just in-line the error messages with Serial.print.

Next, there are a lot of arrays declared in there.

Code: Select all

int Temp[8], SensorFail[8];
float floatTemp, floatInternalTemp;
char failMode[8];
int internalTemp, intTempFrac;
unsigned int Mask;
//char data[16];
char i, channel, NumSensors =1, UpdateDelay;
char Rxchar, Rxenable, Rxptr, Cmdcomplete, R;
char Rxbuf[32];
char adrbuf[3], cmdbuf[3], valbuf[12];
int val = 0, Param;     
unsigned long conversionTime, intervalTime;
I'd have a look at the logic involved with all of them, to see if:
-you could use a smaller data type. E.g., char instead of int.
-you could get away with a smaller array
-you really need the array at all.

SensorFail and failMode seem particularly useless. That information is already in the raw temperature data.

The big arrays are in the stuff I haven't looked at - that is, what comes after the thermocouple section in loop(). But I'm willing to bet that you can find ways to cut those down as well.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

PS -

An easy thing to do is to comment out the loop() stuff underneath the thermocouple reads, so that you can also comment out the data arrays. Leave in enough to do some dummy writes to the SD file, and see if the problems go away.

If they do, it pretty much fingers SRAM as the culprit. Either that, or something to do with that section of code.

Either way, it clears the SD and TC hardware and software.

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

I have been trimming and testing. Even got a scope for monitoring what was happening, and I still can't figure it out. Below is the entirety of my current code iteration. If I comment out SD.begin(ChipSelect);, the thermocouple data is valid. With SD.begin in the code, I get all 32 degrees (0 input from the TCs converted to Fahrenheit). There is something going on behind the scenes with SD.h that is corrupting the data from the TCMux on the SPI bus, and I can't figure out what it is.

Code: Select all

    #include <string.h> //Use the string Library
    #include <ctype.h>
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>
    #include <SD.h> // SD card library 
    #include <SPI.h>
    #include <RTClib.h>

    #define PINEN 7 //Mux Enable pin
    #define PINA0 4 //Mux Address 0 pin
    #define PINA1 5 //Mux Address 1 pin
    #define PINA2 6 //Mux Address 2 pin
    #define PINSO 12 //TCAmp Slave Out pin (MISO)
    #define PINSC 13 //TCAmp Serial Clock (SCK)
    #define PINCS 9  //TCAmp Chip Select Change this to match the position of the Chip Select Link

    LiquidCrystal_I2C lcd(0x3F,20,4); //set the LCD address to 0x3F for a 20 chars and 4 line display
    int Temp;
    float floatTemp[8], floatInternalTemp;
    int internalTemp;
    unsigned int Mask;
    char i, channel;
    char name[] = "TEMPS00.CSV"; //File names will follow this format, and increment automatically  
    unsigned long conversionTime, intervalTime;
    boolean FailGND = false;
    boolean FailSHT = false;
    boolean FailOPEN = false;
    boolean SensorFail = false;
    
    RTC_DS1307 RTC; // define the Real Time Clock object
    DateTime now; //define a time object "now", from the RTClib library

    // The objects to talk to the SD card
    Sd2Card card;
    SdVolume volume;
    SdFile root;
    SdFile file;
    File logfile; // The object that represents our datafile
    const byte chipSelect = 10; // chip select pin for SD card, 10 for regular Arduino, 53 for Mega

    volatile unsigned long lastSave = 0; // time value of last save. Relies on realtime clock
    volatile unsigned long lcdTimeOut = 0; //timer for turning off LCD
    volatile byte saveInterval = 10; //time between saves (units = seconds, not millis)
    volatile byte lcdInterval= 60; //time to wait before shutting off LCD (units = seconds)
  
    #define CONVERSION_TIME      200            /* number of milliseconds per conversion */
    #define CONVERSION_PERIOD   2000            /* start a new set of 8 conversions once every second */
    #define CONVERSION_INTERVAL (CONVERSION_PERIOD - (CONVERSION_TIME * 8)) /*interval between the end of a conversion cycle and the start of the next */

    void setup()   
    {     
      //Serial.begin(9600);   
      Wire.begin(); // You must start the Wire thing before attempting to read the real time clock
      lcd.init();
      lcd.backlight();  
      lcd.setCursor(0, 0); 
         
      //initialize the SD card
      SD.begin(chipSelect);
      card.init();
      RTC.begin();
     
      // create a new file
      for (uint8_t x = 0; x < 100; x++) {
        name[5] = x/10 + '0';
        name[6] = x%10 + '0';
        if (! SD.exists(name)) {
         logfile = SD.open(name, FILE_WRITE);   
         logfile.println("date,time,Ch1,Ch2,Ch3,Ch4,Ch5,Ch6,Ch7,Ch8"); //Write the output file header row to our file so that we can identify the data later
         logfile.flush();
         delay(100);
         break; 
        }
      }

               
      now = RTC.now(); //update clock        
      
      lastSave = now.unixtime(); //initialize the lastSave value so that we can 
                                       //determine when to save to the SD card. 
      lcdTimeOut = now.unixtime();  //initialize lcdTimeOut to determine when to shut off the lcd backlight


      pinMode(PINEN, OUTPUT);     
      pinMode(PINA0, OUTPUT);   
      pinMode(PINA1, OUTPUT);   
      pinMode(PINA2, OUTPUT);   
      pinMode(PINSO, INPUT);   
      pinMode(PINCS, OUTPUT);   
      pinMode(PINSC, OUTPUT);   
     
      digitalWrite(PINEN, HIGH);   // enable on
      digitalWrite(PINA0, LOW); // low, low, low = channel 1
      digitalWrite(PINA1, LOW);
      digitalWrite(PINA2, LOW);
      digitalWrite(PINSC, LOW); //put clock in low
      digitalWrite(PINCS, HIGH);
     
      channel = 0;
      selectThermocouple(channel);            //start the first conversion
      conversionTime = millis();              //timestamp start of first conversion
      intervalTime = conversionTime - CONVERSION_INTERVAL;  //back-date the start of the first conversion interval
    }

    void loop()                     
    {
      if (millis() > (intervalTime + CONVERSION_INTERVAL))  //start a new set of 8 conversions every CONVERSION_INTERVAL milliseconds
      {
        if (millis() > (conversionTime + CONVERSION_TIME))  //every CONVERSION_TIME milliseconds
        {
          conversionTime = millis();    //reset conversion time-out
     
          readThermocouple(channel);    //stop conversion and read the current thermocouple
         
          channel = channel + 1;  //get the next channel number
         
          if (channel == 8)             //if we finished all 8 conversions,
          {
            channel = 0;
            intervalTime = millis();    // reset the conversion interval time-out.
          }
          
          Serial.println(channel+1,DEC);
          selectThermocouple(channel);  //select the next thermocouple and begin conversion
         
        } //end CONVERSION_TIME
      } //end CONVERSION _INTERVAL
      
        
      //*********************************************************************************  
      //Write data to SD card if it's time to save
      now = RTC.now(); //get current time from Real Time Clock
      
      if (now.unixtime() >= (lastSave + saveInterval)) //if new unix time is greater than
                                                       //lastSave + saveInterval
      {
        lastSave = now.unixtime(); //update lastSave value
//        logfile.print(now.unixtime());
//        logfile.print(",");  
        //logfile = SD.open(name, FILE_WRITE);
        logfile.print(now.month(), DEC);
        logfile.print("/");
        logfile.print(now.day(), DEC);
        logfile.print("/");
        logfile.print(now.year(), DEC);
        logfile.print(",");
        logfile.print(now.hour(), DEC);
        logfile.print(":");
        logfile.print(now.minute(), DEC);
        logfile.print(":");
        logfile.print(now.second(), DEC);  
        logfile.print(",");
        //now save temperatures
        for (int i=0; i<=6;i++) { //write the first 7 temperatures in a loop
        logfile.print(floatTemp[i]);
        logfile.print(","); 
        }
        logfile.println(floatTemp[7]);
        logfile.flush();
      }          
    }

    void selectThermocouple(int channel)
    {
        Serial.println("selectThermocouple");
        digitalWrite(PINCS, LOW); //Take SPI bus

        switch (channel) //select channel
        {
          case 0:
            digitalWrite(PINA0, LOW);
            digitalWrite(PINA1, LOW);
            digitalWrite(PINA2, LOW);
          break;
          case 1:
            digitalWrite(PINA0, HIGH);
            digitalWrite(PINA1, LOW);
            digitalWrite(PINA2, LOW);
          break;
          case 2:
            digitalWrite(PINA0, LOW);
            digitalWrite(PINA1, HIGH);
            digitalWrite(PINA2, LOW);
          break;
          case 3:
            digitalWrite(PINA0, HIGH);
            digitalWrite(PINA1, HIGH);
            digitalWrite(PINA2, LOW);
          break;
          case 4:
            digitalWrite(PINA0, LOW);
            digitalWrite(PINA1, LOW);
            digitalWrite(PINA2, HIGH);
          break;
          case 5:
            digitalWrite(PINA0, HIGH);
            digitalWrite(PINA1, LOW);
            digitalWrite(PINA2, HIGH);
          break;
          case 6:
            digitalWrite(PINA0, LOW);
            digitalWrite(PINA1, HIGH);
            digitalWrite(PINA2, HIGH);
          break;
          case 7:
            digitalWrite(PINA0, HIGH);
            digitalWrite(PINA1, HIGH);
            digitalWrite(PINA2, HIGH);
          break;
          default:
            lcd.setCursor(0, 0); 
            lcd.print("invalid channel: "); lcd.println(channel);
            break;
        }
       
        delay(5);                  //allow for leisurely digestion
        digitalWrite(PINCS, HIGH); //Release SPI bus; Begin conversion
    }

    float readThermocouple(int channel)
    {

        Temp = 0;
        floatTemp[channel] = 0;
        internalTemp = 0;
        FailGND = false;
        FailSHT = false;
        FailOPEN = false;
        SensorFail = false;
       
        digitalWrite(PINCS, LOW); //Take SPI bus, stop conversion
       
        for (i=31;i>=0;i--)
        {
            digitalWrite(PINSC, HIGH);
            delay(1);
           
          if ((i<=31) && (i>=18))
          {
            // these 14 bits are the thermocouple temperature data
            // bit 31 sign
            // bit 30 MSB = 2^10
            // bit 18 LSB = 2^-2 (0.25 degC)
           
            Mask = 1<<(i-18);
            if (digitalRead(PINSO)==1)
            {
              if (i == 31)
              {
                Temp += (0b11<<14);//pad the temp with the bit 31 value so we can read negative values correctly
              }
              Temp += Mask;
              //Serial.print("1");
            }
            else
            {
             // Serial.print("0");
            }
          }
          //bit 17 is reserved
          //bit 16 is sensor fault
          if (i==16)
          {
            SensorFail == digitalRead(PINSO);
          }
         
          if ((i<=15) && (i>=4))
          {
            //these 12 bits are the internal temp of the chip
            //bit 15 sign
            //bit 14 MSB = 2^6
            //bit 4 LSB = 2^-4 (0.0625 degC)
            Mask = 1<<(i-4);
            if (digitalRead(PINSO)==1)
            {
              if (i == 15)
              {
                internalTemp += (0b1111<<12);//pad the temp with the bit 31 value so we can read negative values correctly
              }
             
              internalTemp += Mask;//should probably pad the temp with the bit 15 value so we can read negative values correctly
              //Serial.print("1");
            }
            else
            {
             // Serial.print("0");
            }
           
          }
          //bit 3 is reserved
          if (i==2)
          {
            FailSHT == digitalRead(PINSO)<<2;//bit 2 is set if shorted to VCC
          }
          if (i==1)
          {
            FailGND == digitalRead(PINSO)<<1;//bit 1 is set if shorted to GND
          }
          if (i==0)
          {
            FailOPEN == digitalRead(PINSO)<<0;//bit 0 is set if open circuit
          }
         
         
          digitalWrite(PINSC, LOW);
          delay(1);
          //delay(1);
        }
       
        digitalWrite(PINCS, HIGH); //Release SPI bus
 
        if (channel <= 3) lcd.setCursor(0,channel);
          else lcd.setCursor(10,channel-4);
          
          lcd.print("Cn");
          lcd.print(channel+1);
          lcd.print(": ");
          
          if (SensorFail)
          {
            if (FailSHT)
            {
              lcd.print("VCC");
            }
            if (FailGND)
            {
              lcd.print("GND");
            }
            if (FailOPEN)
            {
              if (channel == 7)
              {
                lcd.setCursor(10,3);
                lcd.print("Int: ");
                floatInternalTemp = (float)internalTemp * 0.0625;
                floatInternalTemp = ((9 * floatInternalTemp) / 5) + 32;  //convert to Fahrenheit.
                lcd.print(floatInternalTemp,1); // If Channel 8 isn't hooked up, replace it with the internal temp
              }
              else
              {
                lcd.print("OPEN");
              }
            }
          }
          else
          {
            floatTemp[channel] = (float)Temp * 0.25;
            floatTemp[channel] = ((9 * floatTemp[channel]) / 5) + 32;  //convert to Fahrenheit.
            lcd.print(floatTemp[channel],1);
          } 
      return floatTemp[channel];

    }

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

I still think you've got a problem with running out of SRAM. If I'm not mistaken, the LCD is new to the mix, and that's only going to increase SRAM demands.

As I suggested before, the simplest thing to do to cut SRAM usage is to use the F() macro on every literal string appearing in a print or println. Serial.print, lcd.print, logfile.print - whatever. Don't be stingy - even do it with things like logfile.print(F(":"));

If that doesn't cure the problem, it should at least produce different symptoms, so you'll know you're on the right track.

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

I put the F() command in all of the lcd and logfile print commands, and it didn't fix the issue. In searching for similar issues, I found the following thread on the Arduino forums... http://forum.arduino.cc/index.php/topic ... 0.html#top.

I put the FreeRAM code in my sketch, and found that I have 718 bits/bytes? of available SRAM left. I think I may be experiencing the same problem with the SPI bus interference caused by the SD library. I need to investigate what needs to be changed to incorporate the SDfat library instead.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

You're not using the SPI library. That post is not relevant.

FreeRAM can be misleading. You need to understand how the Arduino runtime uses SRAM. Basically, SRAM is divided into three sections:

Global variables, static variables, literal strings, arrays, etc - essentially anything that is not declared inside of a function - go in the first section, located at the lowest addresses.

The middle section is called the Heap, and is where dynamic variables are allocated. This is where malloc() and free() do their thing.

The top section is called the Stack. This is where variables which are defined within functions are allocated. When you call a function, a block of data is added to the Stack. This block of data (called a "stack frame") contains the arguments to the function, the return address (where the processor jumps back to when the function exits), and the local variables for the function.

The Stack has a peculiar property: it grows downward from high addresses to lower addresses. That is, when you call a function, the stack frame is appended starting at the lowest address of the stack area.

The size of the "global variable" space is fixed at compile time, and never changes. The size of the stack space changes dynamically, depending on what your sketch is doing at any given time. The Heap is squeezed in the middle between these two spaces.

The problem is that the Heap can grow upwards while the Stack is growing downwards. They can meet in the middle and actually overlap. This is called a stack crash. There is no mechanism to prevent this from happening, and there is no warning or notification that it has happened. A stack frame can overwrite dynamic data allocated in the heap, and the result is that the sketch can start doing all sorts of bizarre things.

So FreeRAM tells you that there are 718 bytes available. But that's just a snapshot, and the number you get depends on where and when you call it.

The SD library makes heavy use of the Heap space. That's where it allocates all of its buffers.

To diagnose this, start by commenting out everything but the code related to the SD. Then start adding back in pieces of the thermocouple code. Leave the LCD entirely out of it. Call FreeRAM at various strategic locations, and see if the numbers it returns are changing.
wilheldp wrote:I put the F() command in all of the lcd and logfile print commands
And the Serial.prints, as well?

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

Do you not have to included SPI.h in order to communicate over the SPI bus for the SD shield? I think SdFat uses it to run the SPI bus at half speed.

Actually, I removed the serial.prints altogether. There were just in there for debugging purposes to tell me when certain subroutines were run.

I will run the FreeRAM routine in different places when I get home this evening. I got my code switched over to SdFat last night, and the FreeRAM reading increased from 718 to 768 bytes immediately.

Assuming I am running out of SRAM, is there anything I can do about it? Is there a way to flush variables out of SRAM when they aren't being used?

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: Datalogger + Thermocouple Mux

Post by adafruit_support_rick »

wilheldp wrote:Do you not have to included SPI.h in order to communicate over the SPI bus for the SD shield? I think SdFat uses it to run the SPI bus at half speed.
You should only need the Adafruit SD library. It does it's own SPI, and doesn't use the Arduino SPI library (thus the conflict in the post you linked). The TC doesn't use any SPI libraries at all. So, there should be no library conflicts.

You should definitely get rid of the string library - I don't think you're using it anyway...

Where did the LiquidCrystal_I2C library come from?

I can get your sketch to compile if I comment out all the of lcd stuff, however, I did get a strange thing - a redefinition of the variable 'i' between the sketch and the RTCLib. To fix that, I deleted the global definition of i:

Code: Select all

   char /*i,*/ channel;
and added it back as a local to function readThermocouple:

Code: Select all

    float readThermocouple(int channel)
    {

        Temp = 0;
        floatTemp[channel] = 0;
        internalTemp = 0;
        FailGND = false;
        FailSHT = false;
        FailOPEN = false;
        SensorFail = false;
        int i;
wilheldp wrote:Assuming I am running out of SRAM, is there anything I can do about it? Is there a way to flush variables out of SRAM when they aren't being used?
I think you've done just about everything you can do. the string library does a lot of heap work, but I don't see you actually using it anywhere. I commented out the include with no repercussions.

You may be having some sort of library conflict after all, but I don't think it's simply a matter of SPI bus arbitration.

Like I said before, I would go backwards until it works. Take out the LCD, take out the RTC (take out the #includes, too). Strip it down to just reading the TC and writing the SD. If it still doesn't work, then I might start believing in an SPI problem.

Also take out the #includes for string.h, ctype.h, and SPI.h.

wilheldp
 
Posts: 22
Joined: Tue May 07, 2013 6:14 pm

Re: Datalogger + Thermocouple Mux

Post by wilheldp »

The LCD library came from https://github.com/digistump/DigisparkA ... stal_I2C.h and was suggested by the SainSmart LCD purchase page. The LCD works like a champ, even displaying 32degF for all 8 channels as the data from the Mux is corrupted. It also displays the correct temperature data when the SD code is disabled. I used to have a bunch of fail/success messages as the SD, RTC, and logfile were initialized, but I cut those out because they were annoying.

I had the same problem with RTCLib not liking the global variable "i", but it was a known issue fixed by a later version of the library. I downloaded the newest version from GitHub, and it fixed the problem.

Does putting the variables in a sub-routine cause them to flush out of SRAM after the "return" command? If so, I can probably put more variables in their respective sub-routines to free it up during the main loop.

This doesn't bode well for actually putting a UI front end on my datalogger if I'm running out of memory just trying to read, display, and log the temperatures.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”