Arduino MKR Therm Shield Not Measuring Temperatures Below 0 °C

Hello,

I have two MKR Therm Shields with the Arduino MKR 1010 WiFi.
On the manufacturer's page, it states that it supports K-type thermocouples and is capable of measuring with good precision ranges from -270 to +1372 Celsius. Note: While the shield itself is not designed to withstand any extreme temperatures, external sensors can be connected that we can accurately read between -200°C to +700°C. Also, I read: The MKR Therm Shield is a great addon for MKR family boards and allows you to connect high-quality thermocouplers, and can calculate temperatures from -200°C to +700°C. An ideal solution to use for ovens, freezers, smokers, and similar environments.

I need to measure cryogenic temperatures, specifically between -100°C and -150°C, but I haven’t had any success. I receive errors below 0°C, and I do not get any valid readings below 0°C. Instead, I see a strange large number: 1073741824.00, and it does not show any negative values. Everything works fine above 0°C; I even tested it at 200°C.

I have tried a couple of K-type thermocouples. For example, the latest one is the Kingpin thermocouple:

Kingpin Thermocouple: X-Probe 2 Type-K Extreme Thermal Probe – Kingpin cooling

and

TL0024 Low Temperature Sensor/Probe, which operates between -200°C and 260°C / -328°F and 500°F (2 meters long).

TL0024 Low Temperature Sensor/Probe: PerfectPrime TL0024 Thermocouple Low Temperature Sensor/Probe, -200~260°C / -328~500°F (2 Meter Long): Amazon.com: Industrial & Scientific

I am out of ideas :frowning:

The code is simple:

cpp

Copy code

#include <Arduino_MKRTHERM.h>

void setup() {
  Serial.begin(9600);
  while (!Serial);
  if (!THERM.begin()) {
    Serial.println("Failed to initialize MKR THERM shield!");
    while (1);
  }
}

void loop() {
  Serial.print("Reference temperature ");
  Serial.print(THERM.readReferenceTemperature());
  Serial.println(" &#xB0;C");
  Serial.print("Temperature ");
  Serial.print(THERM.readTemperature());
  Serial.println(" &#xB0;C");
  Serial.println();
  delay(1000);
}

What am I doing wrong, or what is the issue? What do you recommend?
I’m out of ideas at this point.

Output:

The cooling mixture is ice + sodium chloride

Reference temperature 26.94 °C
Temperature 3.50 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.06 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 8.50 °C

Reference temperature 27.00 °C
Temperature 21.00 °C

Reference temperature 27.00 °C
Temperature 18.25 °C

Reference temperature 27.00 °C
Temperature 18.50 °C

Reference temperature 27.00 °C
Temperature 4.75 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 26.94 °C
Temperature 1073741824.00 °C

Reference temperature 26.94 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Reference temperature 27.00 °C
Temperature 1073741824.00 °C

Thank you for the responses. If anyone owns this device and has measured below zero degrees with a thermocouple, please share your code and the parameters of the thermometer. Thank you in advance

My first thought would be that there's a problem in the library you are using with a mix-up between signed and unsigned variables.

I've no experience of the MKR boards myself but I wonder if it may possibly be an issue with the serial printing routine, as I seem to recall reading some time back that serial print didn't like floats much.

You could test this by using sprintf to print the value to a null terminated c string and then use serial print to print out the resulting text.

EDIT: There may be an issue with sprintf too.

This discussion might help - see the dtostrf() function:

It's doing this wrong somehow?

I'd try using the THERM.readSensor() raw value and doing the conversion locally.

Edit: It is interesting that the result is precisely 1/4 of ULONG_MAX (2^32): "1073741824*4 = 4294967296 = 2^32"

Thank you for the responses. If anyone owns this device and has measured below zero degrees with a thermocouple, please share your code and the parameters of the thermometer. Thank you in advance

The MAX31855 device that the MKR-THERM Shield is based upon looks like it handles negatives:

Staring at this line, it looks like there's one too many 'F's in the RHS mask:

I don't have the hardware to check, but it looks like a bug to me.

I'd probably re-write the whole thing to use a signed int32_t for rawword and let the compiler handle the sign extending.

DaveX, Thank you very much! Your understanding of the situation is exceptional, You hit the nail on the head! You are a genius! I edited the MKRTHERM.cpp file, and it's now working. I also need to measure liquid nitrogen temperature, and I'll let you know the results.

here is codediff quickview link:

Output:
Temperatuur: 22.75 °C
Temperatuur: 22.50 °C
Temperatuur: 22.50 °C
Temperatuur: 22.50 °C
Temperatuur: 22.75 °C
Temperatuur: 22.50 °C
Temperatuur: 22.50 °C
Temperatuur: 22.50 °C
Temperatuur: 22.75 °C
Temperatuur: 22.50 °C
Temperatuur: 22.50 °C
Temperatuur: 22.75 °C
Temperatuur: 22.75 °C
Temperatuur: 22.75 °C
Temperatuur: 22.75 °C
Temperatuur: 22.50 °C
Temperatuur: 22.25 °C
Temperatuur: -2.25 °C
Temperatuur: -3.00 °C
Temperatuur: -4.00 °C
Temperatuur: -4.50 °C

edited MKTHERM.cpp

cpp

/*
  This file is part of the Arduino_MKRTHERM library.
  Copyright (c) 2019 Arduino SA. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "MKRTHERM.h"

THERMClass::THERMClass(int cs, SPIClass& spi) :
  _cs(cs),
  _spi(&spi),
  _spiSettings(4000000, MSBFIRST, SPI_MODE0)
{
}

int THERMClass::begin()
{
  int32_t rawword;  // Change uint32_t to int32_t

  pinMode(_cs, OUTPUT);
  digitalWrite(_cs, HIGH);
  _spi->begin();

  rawword = readSensor();
  if (rawword == 0xFFFFFF) {
    end();
    return 0;
  }

  return 1;
}

void THERMClass::end()
{
  pinMode(_cs, INPUT);
  digitalWrite(_cs, LOW);
  _spi->end();
}

uint32_t THERMClass::readSensor()
{
  uint32_t read = 0x00;

  digitalWrite(_cs, LOW);
  delayMicroseconds(1);

  _spi->beginTransaction(_spiSettings);

  for (int i = 0; i < 4; i++) {
    read <<= 8;
    read |= _spi->transfer(0);
  }

  _spi->endTransaction();
  digitalWrite(_cs, HIGH);

  return read;
}

float THERMClass::readTemperature()
{
  int32_t rawword;  // Change uint32_t to int32_t
  float celsius;

  rawword = readSensor();

  // Check for reading error
  if (rawword & 0x7) {
    return NAN; 
  }

  // The temperature is stored in the last 14 bits 
  // sent by the Thermocouple-to-Digital Converter
  rawword >>= 18; // Drop the lower 18 bits

  // No need for manual sign extension, int32_t handles it
  celsius = rawword * 0.25f;

  return celsius;
}

float THERMClass::readReferenceTemperature()
{
  uint32_t rawword;
  float ref;

  rawword = readSensor();

  // Ignore the first 4 FAULT bits
  rawword >>= 4;

  // The cold junction reference temperature is stored in the first 11 bits
  // sent by the Thermocouple-to-Digital Converter
  rawword = rawword & 0x7FF;

  // Check the sign bit and convert to negative value if needed.
  if (rawword & 0x800) {
    ref = (0xF800 | (rawword & 0x7FF)) * 0.0625;
  } else {
    // Multiply by the LSB value
    ref = rawword * 0.0625f;
  }

  return ref;
}

THERMClass THERM;

And here is original code:

cpp

/*
  This file is part of the Arduino_MKRTHERM library.
  Copyright (c) 2019 Arduino SA. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "MKRTHERM.h"

THERMClass::THERMClass(int cs, SPIClass& spi) :
  _cs(cs),
  _spi(&spi),
  _spiSettings(4000000, MSBFIRST, SPI_MODE0)
{
}

int THERMClass::begin()
{
  int32_t rawword;  // Change uint32_t to int32_t

  pinMode(_cs, OUTPUT);
  digitalWrite(_cs, HIGH);
  _spi->begin();

  rawword = readSensor();
  if (rawword == 0xFFFFFF) {
    end();
    return 0;
  }

  return 1;
}

void THERMClass::end()
{
  pinMode(_cs, INPUT);
  digitalWrite(_cs, LOW);
  _spi->end();
}

uint32_t THERMClass::readSensor()
{
  uint32_t read = 0x00;

  digitalWrite(_cs, LOW);
  delayMicroseconds(1);

  _spi->beginTransaction(_spiSettings);

  for (int i = 0; i < 4; i++) {
    read <<= 8;
    read |= _spi->transfer(0);
  }

  _spi->endTransaction();
  digitalWrite(_cs, HIGH);

  return read;
}

float THERMClass::readTemperature()
{
  int32_t rawword;  // Change uint32_t to int32_t
  float celsius;

  rawword = readSensor();

  // Check for reading error
  if (rawword & 0x7) {
    return NAN; 
  }

  // The temperature is stored in the last 14 bits 
  // sent by the Thermocouple-to-Digital Converter
  rawword >>= 18; // Drop the lower 18 bits

  // No need for manual sign extension, int32_t handles it
  celsius = rawword * 0.25f;

  return celsius;
}

float THERMClass::readReferenceTemperature()
{
  uint32_t rawword;
  float ref;

  rawword = readSensor();

  // Ignore the first 4 FAULT bits
  rawword >>= 4;

  // The cold junction reference temperature is stored in the first 11 bits
  // sent by the Thermocouple-to-Digital Converter
  rawword = rawword & 0x7FF;

  // Check the sign bit and convert to negative value if needed.
  if (rawword & 0x800) {
    ref = (0xF800 | (rawword & 0x7FF)) * 0.0625;
  } else {
    // Multiply by the LSB value
    ref = rawword * 0.0625f;
  }

  return ref;
}

THERMClass THERM;

here is my grafana screenshot ( ice+NaCl mixture)

The temperature in liquid nitrogen is higher than it should be, and calibration is needed. If anyone has experience with calibration, please share your insights

the sensor needs to be calibrated. now the temperature in liquid nitrogen is higher. if anyone has experience, please share.

No answer, only a few physics questions pop up.

  • what is the expected temperature?
  • what is the air pressure?
  • how much mass must be cooled? Recall that can take time is mass is large.
  • how well is the thermal isolation? Does heat leak in?
  • do you have a calibrated temperature sensor to compare?

Here is calibrated LN2 graph:

And code:

cpp

Copy code

#include <WiFiNINA.h>
#include <PubSubClient.h>
#include <Arduino.h>
#include <Arduino_MKRTHERM.h>
#include <math.h>  // for pow function

// WiFi seadistused
const char* ssid = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Sisesta oma WiFi SSID
const char* password = "xxxxxxxxxx"; // Sisesta oma WiFi parool

// MQTT serveri andmed
const char* mqtt_server = "xxxxxxxxx";
const char* mqtt_user = "xxxxxxxx";
const char* mqtt_password = "xxxxxxxxxxxx";
const char* mqtt_topic = "thermocouple1/temperature";
const char* clientId = "arduinotherm1";

// Termopaar
THERMClass myTherm; // Muutke 'THERM' näiteks 'myTherm'

// Non Linear Thermocouple Compensation Coefficients
// Used to calculate actual temp from voltage from the MAX31855
const float c0 = 0;
const float c1 = 27.0;   // alandatud
const float c2 = -0.20;   // hoia
const float c3 = 0.75;    // alandatud
const float c4 = 0.40;    // hoia
const float c5 = 0.12;    // alandatud
const float c6 = 0.018;   // hoia
const float c7 = 0.001;    // hoia
const float d0 = 0;
const float d1 = 24.0;   // alandatud
const float d2 = -0.70;    // hoia
const float d3 = 0.045;    // hoia
const float d4 = -0.002;   // hoia
const float d5 = 0.00005;  // hoia
const float d6 = -0.0000005; // hoia
const float d7 = 0;        // hoia
const float MAXc = 41.276;  // µV/°C

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
    Serial.begin(9600);

    // Ühendu WiFi võrguga
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);

    // Oota, kuni WiFi on ühendatud
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }

    Serial.println("\nWiFi connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    // Seadista MQTT server
    mqttClient.setServer(mqtt_server, 1883);

    // Termopaar
    if (myTherm.begin() == 0) {
        Serial.println("Termopaar ei initsialiseeru!");
        while (1); // Jääb lõksu
    }
    Serial.println("Termopaar initsialiseeritud.");
}

float TCComp(float external, float internal) {
    float TCTemp;
    float Vout = MAXc * (external - internal) / 1000;  // calculate volts from external and internal temperatures in millivolts

    // Apply non-linear temperature compensation
    if (Vout < 0) {
        TCTemp = internal + c0 + (c1 * Vout) + (c2 * pow(Vout, 2)) + (c3 * pow(Vout, 3)) + (c4 * pow(Vout, 4)) + (c5 * pow(Vout, 5)) + (c6 * pow(Vout, 6)) + (c7 * pow(Vout, 7));  // Below Zero
    } else {
        TCTemp = internal + d0 + (d1 * Vout) + (d2 * pow(Vout, 2)) + (d3 * pow(Vout, 3)) + (d4 * pow(Vout, 4)) + (d5 * pow(Vout, 5)) + (d6 * pow(Vout, 6)) + (d7 * pow(Vout, 7));  // Above Zero
    }

    return TCTemp;
}

void loop() {
    // Kontrolli, kas MQTT on ühendatud, ja ühildu
    if (!mqttClient.connected()) {
        reconnect();
    }
    mqttClient.loop();

    float internalTemp = myTherm.readReferenceTemperature();  // seadme ümbritseva temperatuuri lugemine
    float externalTemp = myTherm.readTemperature();           // termopaari temperatuuri lugemine

    // Rakenda kompensatsioon
    float compensatedTemp = TCComp(externalTemp, internalTemp);

    if (isnan(compensatedTemp)) {
        Serial.println("Kompenseeritud temperatuuri lugemine ebaõnnestus!");
    } else {
        Serial.print("Kompenseeritud temperatuur: ");
        Serial.print(compensatedTemp);
        Serial.println(" °C");

        // Teisenda temperatuur stringiks ja saada MQTT serverisse
        char temperatureStr[6];
        snprintf(temperatureStr, sizeof(temperatureStr), "%.2f", compensatedTemp);
        mqttClient.publish(mqtt_topic, temperatureStr);

        Serial.println("Kompenseeritud temperatuur saadetud MQTT serverisse");
    }

    delay(1000); // Oodake 1 sekund enne järgmise lugemise tegemist
}

void reconnect() {
    // Oota, kuni oleme ühenduses
    while (!mqttClient.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Ühenda, kui oleme ühenduses
        if (mqttClient.connect(clientId, mqtt_user, mqtt_password)) {
            Serial.println("connected");
        } else {
            Serial.print("failed, rc=");
            Serial.print(mqttClient.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

In order to make all relevant information available to any who are interested in this subject, I'll share a link to the formal bug report submitted by @DaveX regarding the incorrect handling of negative temperatures by the "Arduino_MKRTHERM" library: