THMOD-I2C thermocouple reader module

Hi,

I figured out how to get a temperature reading from a THMOD-I2C I2C thermocouple reader.

It was more complicated than I expected, with two interpolated lookup tables with numbers gleaned from the datasheet. Anyway I'm there now, so I thought I'd share my efforts with the class. Hope it's of use to someone.

It works by polling the cold junction temperature reading from a thermistor next to the terminals on the THMOD board, and using that to look up a correction factor for the thermocouple voltage reading. Then it looks up the corrected thermocouple voltage from another lookup table, and interpolates from that to find the hot junction temperature.

The lookup tables are stored in program memory at the beginning. There are 3 models of THMOD, each with different temperature ranges and different lookup tables. You'll need to uncomment the one that suits your THMOD.

Enjoy!

#include <Wire.h>

// Pins: Standard: SDA:A4  SCL:A5
//       Mega:     SDA:D20 SCL:D21

// Lookup tables for THMOD conversion
const int DigitTable[] PROGMEM = {512, 3072, 5632, 8192, 10752, 13312, 15872, 18432, 20992, 23552, 26112, 28672, 31232}; // 300, 800, 1360
//const int CorrectionTable[] PROGMEM = {-1156, -778, -392, 0, 397, 798, 1203, 1612, 2023, 2436, 2851, 3267, 3682}; // 300
//const int CorrectionTable[] PROGMEM = {-578, -389, -196, 0, 199, 399, 602, 806, 1012, 1218, 1426, 1634, 1841}; // 800
const int CorrectionTable[] PROGMEM = {-385, -259, -131, 0, 132, 266, 401, 537, 674, 812, 950, 1089, 1227}; // 1360
//const int TemperatureTable[] PROGMEM = {-150, -100, -50, 0, 50, 100, 200, 300, 400}; // 300
//const int ThermoVoltageTable[] PROGMEM = {6609, 8946, 10611, 12500, 14523, 16596, 20638, 24709, 28897}; // 300
//const int TemperatureTable[] PROGMEM = {-150, -100, -50, 0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300}; // 800
//const int ThermoVoltageTable[] PROGMEM = {3305, 4473, 5309, 6250, 7262, 8298, 10319, 12355, 14449, 16572, 18703, 20815, 22888, 24913, 26888, 28810, 30669, 32455}; // 800
const int TemperatureTable[] PROGMEM = {-150, -100, -50, 0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1370}; // 1360
const int ThermoVoltageTable[] PROGMEM = {2203, 2982, 3537, 4167, 4841, 5532, 6879, 8236, 9632, 11048, 12468, 13876, 15258, 16609, 17925, 19206, 20446, 21637, 22440}; // 1360

void setup()
{
  Serial.begin(9600);
  Serial.println("Setup...");
  Wire.begin(); // join i2c bus (address optional for master)
  PORTC = (1 << PORTC4) | (1 << PORTC5);   //enable I2C pullups
}

void loop()
{
  float TCTemperature = ThermocoupleRead(0x78);
  Serial.print("Temp: ");
  Serial.println(TCTemperature);
  
  delay(200);
}

float ThermocoupleRead(char TCAddress)
{
  int XHi;
  int XLo;
  int YHi;
  int YLo;
  byte a;

  // Read thermocouple voltages
  Wire.requestFrom(TCAddress, 4);
  
  int ThermoVoltage = Wire.read() * 256;
  ThermoVoltage = (ThermoVoltage + Wire.read());
  
  int ColdJunction = Wire.read() * 256;
  ColdJunction = (ColdJunction + Wire.read());
  
  Wire.endTransmission();
  
  // Use lookup table for cold junction compensation
  XHi = 0;
  a = 0;
  while (XHi < ColdJunction)
  {
    XLo = XHi;
    YLo = YHi;
    XHi = pgm_read_word_near(DigitTable + a);
    YHi = pgm_read_word_near(CorrectionTable + a);
    a++;
  }
  ThermoVoltage = ThermoVoltage + ((long)(YHi - YLo) * (long)(ColdJunction - XLo) / (XHi - XLo)) + YLo;
  
  // Use lookup table for thermocouple temperature
  XHi = 0;
  a = 0;
  while (XHi < ThermoVoltage)
  {
    XLo = XHi;
    YLo = YHi;
    XHi = pgm_read_word_near(ThermoVoltageTable + a);
    YHi = pgm_read_word_near(TemperatureTable + a);
    a++;
  }
  
  // Return temperature
  return ((float)(YHi - YLo) / (float)(XHi - XLo)) * (ThermoVoltage - XLo) + YLo;
}

Have a look at - Arduino Playground - MultiMap - might be usefull ....

Thanks, that looks pretty helpful. I thought something like that must exist but I couldn't find it, so I did it myself.

Paul

I have a Hygrosens THMOD thermocouple module. I try to use your code to let it communicate with Arduino Ethernet.
Not yet succesfull, but I am busy with your code.

It works forgot the pullup resistors

if wire.read() returns 0, ColdJunction will be 0 ==> there will be a divide by zero error. modified the code to catch this..

float ThermocoupleRead(char TCAddress)
{
  int XHi = 0;
  int XLo = 0;
  int YHi = 0;
  int YLo = 0;
  byte a = 0;

  // Read thermocouple voltages
  Wire.requestFrom(TCAddress, 4);
  
  int ThermoVoltage = Wire.read() * 256;
  ThermoVoltage += Wire.read();
  
  int ColdJunction = Wire.read() * 256;
  ColdJunction += Wire.read();
  
  Wire.endTransmission();
  
  // Use lookup table for cold junction compensation
  ColdJunction = constrain(ColdJunction, 1, 31232);     // be sure it is within array DigitTable - prevents possible divide by zero  XHi == XLo if COldJunction == 0
  while (XHi < ColdJunction)
  {
    XLo = XHi;
    YLo = YHi;
    XHi = pgm_read_word_near(DigitTable + a);
    YHi = pgm_read_word_near(CorrectionTable + a);
    a++;
  }

  ThermoVoltage = ThermoVoltage + ((long)(YHi - YLo) * (long)(ColdJunction - XLo) / (XHi - XLo)) + YLo;   // <<<<<< HERE <<<<

  // Use lookup table for thermocouple temperature
  XHi = 0;
  a = 0;
  while (XHi < ThermoVoltage)
  {
    XLo = XHi;
    YLo = YHi;
    XHi = pgm_read_word_near(ThermoVoltageTable + a);
    YHi = pgm_read_word_near(TemperatureTable + a);
    a++;
  }
  
  // Return temperature
  return ((float)(YHi - YLo) / (float)(XHi - XLo)) * (ThermoVoltage - XLo) + YLo;
}

the lookup can be sped up by first looking up the index and then fetch the 4 values

  while (XHi < ColdJunction)
  {
    XLo = XHi;
    YLo = YHi;
    XHi = pgm_read_word_near(DigitTable + a);
    YHi = pgm_read_word_near(CorrectionTable + a);
    a++;
  }

==>

  a = 0;
  while (pgm_read_word_near(DigitTable + a) < ColdJunction) a++;  // search right index first
  XHi = pgm_read_word_near(DigitTable + a);
  XLo = pgm_read_word_near(DigitTable + a-1);
  YLo = pgm_read_word_near(CorrectionTable + a-1);
  YHi = pgm_read_word_near(CorrectionTable + a);

Be aware you must make a 0 entry in the lookup tables to let this work properly.