Go Down

Topic: Smartec SMT172 temperature sensor library (Read 2609 times) previous topic - next topic

EdwinCroissant

Jan 18, 2016, 05:25 pm Last Edit: Feb 16, 2016, 10:17 am by EdwinCroissant
Hello, I am currently involved in a project that requires accurate temperature measurements between the 75 to 100 degrees Celsius with the emphasis around 78 degrees Celsius.  The Dallas DS18B20 with it's conversion time of 750 ms is just a little bit to slow and resolution of 0.0625 degrees Celsius is also a little bit to low.

Now I found a sensor that is probably better suitable:

The SMT172 is a ultra-low power, high accuracy temperature sensor. The output signal is puls width modulated (PWM) where the duty cycle is proportional to the measured temperature value. For more details see the home page of the manufacturer Smartec and the documentation.
Accuracy can be up to 0.25 degree Celsius for a limited temperature range and the resolution up to 0.01 degree Celsius.

Unfortunately no example code or library for the Arduino is available for this sensor.

So I made my own.

I used Timer1 and the Input Capture Pin 1 to measure the duty cycle as dictated in the documentation.

I want to give the credits to:

Nick Gammon: Timing an interval using the input capture unit
Michael Dreher: High-Speed capture mit ATmega Timer
Michael P. Flaga: InputCapture.ino

Without their examples this library was not possible :) 
 

I did some programming in Forth and Delphi in the past but I just started with the Arduino and C++ and I know just enough of this programming language to be dangerous  :)   

So I respectfully ask if an expert can review my code. The example sketch in the library works but there is that nagging feeling that this is just not all of it.

Thank you in advance.

Edwin


PS, please check end of thread for the latest version :)

BobAndriesse

#1
Jan 19, 2016, 01:11 pm Last Edit: Jan 19, 2016, 02:12 pm by BobAndriesse
Hi Edwin,

First of all, thank you for putting time and energy in the SMT172 library. I am sure that it is going to be very useful for a lot of people and I am sure that your library will also be another piece of the IOT puzzle.

You have correctly stated the values for accuracy and resolution as you see them on their webpage, but I was informed that this is going to be changed a bit.

Information for other readers:
Fully understanding these specs takes some very good reading. I would just like to point out the following. When you apply the linear equation that translates the dutycycle value to a temperature, then you get an absolute accuracy of 0.25°C. This is related to the fact that the SMT172 had to be compatible to its predecessor the SMT160, so old software could still be used.  The SMT172 allows for a better accuracy, if you use the quadratic formula (you find all this in the datasheet). If you do this, the absolute accuracy is better than 0.1°C.  Since the noise is better than 0.001°C you can get a resolution close to this value. (The Smartec SMTAS usb temperature boards show the temperatures with a resolution of 0.01°C.)

Another difference between the SMT160 and the SMT172 is that the SMT172 uses so called dynamic element matching (DEM), which is the reason why measurements have to be taken over a multiple of 8 periodes, in order to achive the stated accurary.

You can find the full specs in:
http://www.smartec-sensors.com/assets/files/pdf/sensors/SMT172-Datasheet-V12.0.pdf.

Bob




EdwinCroissant

BobAndriesse, thank you, it was a pleasure doing this,  first library in C++ and by the looks of it, it seems to work as intended :)

Some extra information, I followed the information in the datasheet:

  • Capture the first falling edge and store the value of the timer in startTime
  • Capture the rising edge and store the value of the timer in tempTime
  • Capture the second falling edge and add the difference between the value of the timer and the tempTime to highTime
  • Repeat until we have a multiple of 8 cycles from the sensor.
  • On the last falling edge add the difference between the value of the timer and the tempTime to the highTime and store the value of the timer as endTime.
  • Duty cycle is now the highTime divided by the difference between the endTime and the startTime.
  • Temperature is calculated with the second order relation as stated in the documentation.

The accuracy I stated in the OP is the accuracy of the TO92 sensor. And yes, the TO18 has an accuracy of 0.1 degree Celsius.

Syntax

 startTemperature(oversampling);
    Initialize Timer1 to measure the duty cycle at the Input Capture Pin 1
    oversampling: between 0 and 30
    SMT172 datasheet dictates a multiple of eight sensor cycles to be measured
    amount depending on clock speed and required resolution
    For 16Mhz 3 oversamples (32 sensor cycles) will give a stable resolution of 0.01 C
    with a measuring time of about 15 ms
    Returns false if already running
 getTemperature();
    Return temperature measurement from previous startTemperature command
    in degrees Celsius, return a maximum negative float when called while busy
 isReady();   
    Return true if measurement is complete return, 0 when called while busy
 getCycles();        
    Return the amount of clock cycles for the sensor cycle train,
    return 0 when called while busy
 getHighCycles();   
    Return the amount of clock cycles for the time the sensor cycles were high,
    return 0 when called while busy

I did some time measurements with digitalWriteFast to control an output pin and found that including setting and clearing the pin the falling edge interupt takes 5.5 us and the raising edge interrupt takes 2.5 us to complete. At a sensor frequency of 4 kHz this means that the smallest duty cycle that can be measured is about 1% and the longest about 97,8% (if correctly calculated)

I calculate the duty cycle and the temperature with floating point arithmetic, I wonder if integer arithmetic would give an improvement in accuracy.

BobAndriesse

Today I had the time to hook up a SMT172 and an Arduino board and run the example sketch with your library. This worked well, as was to be expected.

I noticed one thing, however, which is that the values (measuring time, dutycycle en therefor the temperature) being displayed (printed) in the serial monitor were not stable. The temperature value should not change more than +-0.01°C, as long as the sensors temperature does not change, because the DC is extremely stable. Measuring duration times can vary a bit, but not as much as I have seen on the Arduino. This (+-0.01°C) is what you will see if you monitor the readings from the Smartec smtas04 board, for example.

So, the question is, what happens? I have not found the answer yet and my first question to you is, how stable are your readings? It is possible that my problem has to do with the fact that I connected the sensor output to both the Arduino input pin and the Smtas board, so I could compare notes. Since the SMT172 output can drive quite long cables, this should pose no problem.  I will have a look at the signal with my scope a.s.a.p., just to make sure.

In the specified temp range from -45°C to 130°C, the DC will range from 0.11 to 0.93, so that is OK. A good way to get more information about the effect of the fall and rise time of the Arduino pins is to use a signal generator with a 50% DC square wave output, instead of the SMT172, because then you know what values you should get (but I don't know if you have this kind of equipment).

EdwinCroissant

Bob, thank you for testing my libray :)

I noticed one thing, however, which is that the values (measuring time, dutycycle en therefor the temperature) being displayed (printed) in the serial monitor were not stable. [...] So, the question is, what happens? I have not found the answer yet and my first question to you is, how stable are your readings?
With a TO18 running at ~2250 Hz very stable, with a TO92 running at ~ 3200 Hz not so stable as I found out in the mean time. Measuring time was 15 milliseconds for the TO18 and 10 milliseconds for the TO92. Less clock cycles so more noise. So to get a more stable reading increase the oversampling from 3 to 5.

With an oversampling of 30 and a measuring time of 110 milliseconds I get a reasonable stable reading in the mK range  :)

So I am going to change the code in such way that a minimum amount of clock cycles are counted and that the sensor cycles are dividable by 8. Maybe I add a parameter for the desired accuracy that determines the minimum amount of clock cycles.

With timer 2 I can generate a PWM that Timer1 can measure. So I am going to try that to.

oakey22

This looks fantastic, have you made any more progress on this Edwin? I should have some of the sensors arriving tomorrow so can have a play with this :)

EdwinCroissant

This looks fantastic, have you made any more progress on this Edwin? I should have some of the sensors arriving tomorrow so can have a play with this :)
Yes, I have been making some progress.

I solved how to set the oversampling, there is some information in the data-sheet of the predecessor, the SMT16030, that shows an equation to calculate the error. I am using that to get the minimum clock cycles needed to get a certain resolution based on the processor speed and the sensor frequency.  The reverse is also possible: to get the error of the measurement.

I checked the duty cycle with Timer2 as PWM and with a DMM that can also measure the duty cycle and those figures match.

Now I spent the last couple of days chasing a glitch that result in bogus output so now and then. It seems that when a negative edge is occurring on the same moment the interrupts of the timer are enabled the high byte of the Input Capture Register (or maybe the TEMP register) contains a bogus value resulting in a startTime that is greater then the endTime. I solved this (I hope) by discarding the first captured value. The amount of measured cycles is therefore increased by one.

So stay tuned :)

oakey22

Fantastic work Edwin, i have just tested it on my Uno and its running fine at the moment :)

Didn't work too well on my mega, i presume that is down to the timers used?

Would it be possible to include the updated code?

EdwinCroissant

#8
Jan 26, 2016, 03:37 pm Last Edit: Jan 26, 2016, 03:42 pm by EdwinCroissant
Attached is version 1.1 of the SMT172 library.
Major difference is the way the required resolution is set for the sensor.
To set the resolution make one measurent with SMT172::startTemperature(0)
Calculate the required minimum amount of clock cycles with SMT172::getMinClockCycles(error)
Error is the mean error in Celsius so if you want 0.01 C resolution make the error 0.005 C

This is what I get:




EdwinCroissant

Didn't work too well on my mega, i presume that is down to the timers used? Would it be possible to include the updated code?
If I recall correctly the ICP1 on the Mega is not connected. There are two other 16 bit timers that expose their ICP pin, there is ICP4 on Arduino pin 49 and ICP5 on pin 48 according to this pinout.

I will try tomorrow if this works on the Mega as well.


oakey22

Thanks Edwin, this is working fine on my Uno still :)

I just cant believe how fast these temp sensors are responding, they blow the ds18b20 out the water in terms of speed and resolution

EdwinCroissant

If I recall correctly the ICP1 on the Mega is not connected. There are two other 16 bit timers that expose their ICP pin, there is ICP4 on Arduino pin 49 and ICP5 on pin 48 according to this pinout.

I will try tomorrow if this works on the Mega as well.


With some simple search and replace I got it working with Timer 4 on the Mega :D .
Will modify the library so all four 16 bit timers can be used:
Timer 1 for uno and micro
Timer 3 for micro and micro pro
Timer 4 and 5 for the mega.

oakey22

Sounds good Edwin, out of interest what did you have to change to make it work with the Mega, i have had a look through the code but dont know much about the timers aspect of it.

oakey22

Sorted it now, forgot to change the clock speed to CS40 :)

EdwinCroissant

I have added the functionality to detect if a sensor is connected by using the Output Compare Register 1A.

I also found out that I'm probably the first idiot that started the 16 bit timer on the Arduino with the detection of a falling edge. Let me explain this a little bit:

The book says thou start your measurement on a falling edge for a multiple of eight cycles for maximum accuracy. So I did, resulting in sometimes an inconsistent measurement.  I solved that by waiting for the second falling edge.  With the addition of a time out function the problem reappeared. When the timeout was set at 400 Hz I got spurious timeouts at 600Hz and the closer to 400 Hz the frequenter the timeouts appeared.

The waiting for the second falling edge was not very elegant so I rewrote part of the code to start the 16 bit timer with the detection of the rising edge instead. To my utterly surprise the inconsistent readings were gone. According to the book the SMT172 minimum frequency is 500Hz. When a frequency of less then 400 Hz is detected the library signals a disconnected sensor. A floating input will generate a 50 or 60 Hz signal which is well below the 400 Hz threshold. And with 400 Hz I mean 400 Hz, It will run happily at 401 Hz and will not run at 399 Hz ;D

With this detection in place I brought some sense in the syntax (I hope  :) ):

Code: [Select]
SYNTAX:
 uint8_t setError(float error)
  measures 8 consecutive cycles from the sensor to calculate and set the minimum
  amount of clock cycles for the required standard deviation of the sampling noise
return 0 when busy, 1 when success, 2 when not connected within 2.5 ms after
loss of signal
 startTemperature();
initialize Timer1 to measure the duty cycle at the Input Capture Pin 1
 uint8_t getStatus();
return 0 when busy, 1 when success, 2 when not connected within 2.5 ms after
loss of signal
 float getTemperature();
return the temperature from the previous startTemperature command
in degrees Celsius
 float getError();
  return the standard deviation of the sampling noise
 float getFrequency();
  return the frequency of the sensor
 float getDutyCycle();
  return the duty cycle of the sensor
 float getTime();
  return the measuring time in seconds

Go Up