Okay, say 25°C minimum for the bed, and 100°C minimum for the extruder.
Two temperature measurements, two thermistors?
Take Wayne's suggestion and leave the ADC on the 2.048V pga setting.
For the extruder, use a reference resistor of 4.12kΩ 1% or 0.1%, tied to 3.3V.
At 100°C the thermistor resistance is 6338Ω and the ADC will see 2 volts. Use the PGA2 setting, 2.048V full scale.
As the thermistor heats up to 260°C, the ADC will report values with a resolution of a bit better than 0.5°C. That should be fine.
For the bed, use a reference resistor of 64.9kΩ 1% or 0.1% tied to 3.3V.
At 25°C, the thermistor resistance is 100kΩ and the ADC will see 2 volts. Again the 2.048V pga scale.
It will still have sufficient resolution for the 110°C hotbed.
I now see why Tracy was talking about inverting R1 and R2, and I am now hoping that schematic is correct and that the board was made correctly.
Hi Bruce, I'm sorry I confused the issue when I misspoke about inverting the arrangement of R1 and R2. I was miscontruing how the circuit was set up. Let's continue to talk about it as you set it up in post #1.
R2 is the reference connected to 3.3V and R1 is the thermistor, TDK #B57560G1104, 100kΩ at 25°C.
Are there separate thermistors for the "extruder" and for the "bed"?
The hotter it gets, the lower the resistance and the lower the voltage across R1 to be sensed by the ADC. The non-linear curve is such that the voltage steps become smaller and smaller as the temperature rises. The big advantage of an ADC with a PGA is that it is possible to write Prop code to change the gain factor dynamically. The system can maintain a high resolution of temperature across the whole range. Higher PGA gain at higher temperatures. That is the way I would do it, given that ADC. By the way, which ADC is it that you have? I use TI versions, ADSxxxx, but Microchip and others have similar parts.
Once you have the voltage across the thermistor, what do you expect to do with it? My thought is that the Prop will then solve for the resistance, R1 = R2 * 3.3 / (3.3 - Vadc). Then use that to calculate the temperature or interpolate it in a table using the data provided by TDK.
Thanks for getting back to me, because once again, I have been going in circles
Are there separate thermistors for the "extruder" and for the "bed"?
I don't have the bed made yet, but yes there will be a seperate thermistor for the bed.
By the way, which ADC is it that you have?
I have the ADS1015 from TI.
Once you have the voltage across the thermistor, what do you expect to do with it?
My intention was to use the Steinhart-Hart equation to turn the voltage reading into temperture, but I am rethinking this whole issue, however once again I may be barking up the wrong tree. It seems that a lot of people rely on tables, that further rely on the Steinhart-Hart equation. What is wrong with just calculating the output voltage from the values given in the datasheet along with the known value of the fixed resistor, and creating my own table from that data? In the source code below and in other source code that I have, when using the Steinhart-Hart equation, beta factors, thermal coefficients, and etc.. are brought in to determine the temperature. It appears that everything I need is right there in the datasheet, by just figuring out the output voltage from the voltage divide. Am I missing something???
I hope you are decent with looking at C source code.
C Source:
/**************************************************************************/
/*!
@file Adafruit_ADS1015.cpp
@author K.Townsend (Adafruit Industries)
@license BSD (see LICENSE.txt)
Ported to mbed by Arve Seljebu - arve0.github.io
Driver for the ADS1015/ADS1115 ADC
This is a library for the Adafruit MPL115A2 breakout
----> https://www.adafruit.com/products/1083
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@section HISTORY
v1.0 - First release
v1.1.1 - Ported to mbed
*/
/**************************************************************************/
#include "simpletools.h"
#include "simplei2c.h"
#include "config.h"
#include "Adafruit_ADS1015.h"
#include <math.h>
double extruder_celsius;
double bed_celsius;
/**************************************************************************/
/*!
@brief Instantiates a new ADS1015 class w/appropriate properties
*/
/**************************************************************************/
void adc_init()
{
uint8_t i2cAddress;
m_i2c = i2c_newbus(I2C_SCL, I2C_SDA, I2C_MODE);
i2cAddress = ADS1015_ADDRESS;
m_i2cAddress = i2cAddress;
m_conversionDelay = ADS1015_CONVERSIONDELAY;
m_bitShift = 4;
m_gain = GAIN_TWO; // +/-2.048V range (default)
}
void UpdateTemp(uint8_t channel)
{
int samples[NUMSAMPLES];
uint8_t i;
double average;
// take N samples in a row, with a slight delay
for (i = 0; i < NUMSAMPLES; i++)
{
// read channel the specified channel
samples[i] = readADC_SingleEnded(channel);
//pause(10);
}
// average all the samples out
average = 0;
for(i = 0; i < NUMSAMPLES; i++)
{
average += samples[i];
}
average /= NUMSAMPLES;
// convert the value to resistance
average = 4095 / average - 1;
average = SERIESRESISTOR / average;
double steinhart;
// (R/Ro)
steinhart = average / THERMISTORNOMINAL;
// ln(R/Ro)
steinhart = log(steinhart);
// 1/B * ln(R/Ro)
steinhart /= BCOEFFICIENT;
// + (1/To)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15);
// Invert
steinhart = 1.0 / steinhart;
// convert Kelvin to Celcius
steinhart -= 273.15;
if(channel == 0)
{
extruder_celsius = steinhart;
}
if(channel == 1)
{
bed_celsius = steinhart;
}
}
/**************************************************************************/
/*!
@brief Writes 16-bits to the specified destination register
*/
/**************************************************************************/
void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value)
{
unsigned char cmd[3];
cmd[0] = (char)reg;
cmd[1] = (char)(value>>8);
cmd[2] = (char)(value & 0xFF);
i2c_out(m_i2c, i2cAddress, 0, 0, cmd, 3);
}
/**************************************************************************/
/*!
@brief Reads 16-bits from the specified register
*/
/**************************************************************************/
uint16_t readRegister(uint8_t i2cAddress, uint8_t reg)
{
unsigned char data[2];
data[0] = reg; // temporary use this to send address to conversion register
i2c_out(m_i2c, i2cAddress, 0, 0, data, 1);
i2c_in(m_i2c, i2cAddress, 0, 0, data, 2);
return (data[0] << 8 | data [1]);
}
/**************************************************************************/
/*!
@brief Sets the gain and input voltage range
*/
/**************************************************************************/
void setGain(adsGain_t gain)
{
m_gain = gain;
}
/**************************************************************************/
/*!
@brief Gets a gain and input voltage range
*/
/**************************************************************************/
adsGain_t getGain()
{
return m_gain;
}
/**************************************************************************/
/*!
@brief Gets a single-ended ADC reading from the specified channel
*/
/**************************************************************************/
uint16_t readADC_SingleEnded(uint8_t channel)
{
if (channel > 3) {
return 0;
}
// Start with default values
uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE | // Disable the comparator (default val)
ADS1015_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
ADS1015_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
ADS1015_REG_CONFIG_DR_1600SPS | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
ADS1015_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default)
// Set PGA/voltage range
config |= m_gain;
// Set single-ended input channel
switch (channel) {
case (0):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
break;
case (1):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
break;
case (2):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
break;
case (3):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
break;
}
// Set 'start single-conversion' bit
config |= ADS1015_REG_CONFIG_OS_SINGLE;
// Write config register to the ADC
writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
// Wait for the conversion to complete
pause(m_conversionDelay);
// Read the conversion results
// Shift 12-bit results right 4 bits for the ADS1015
return readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
}
/**************************************************************************/
/*!
@brief Reads the conversion results, measuring the voltage
difference between the P (AIN0) and N (AIN1) input. Generates
a signed value since the difference can be either
positive or negative.
*/
/**************************************************************************/
int16_t readADC_Differential_0_1()
{
// Start with default values
uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE | // Disable the comparator (default val)
ADS1015_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
ADS1015_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
ADS1015_REG_CONFIG_DR_1600SPS | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
ADS1015_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default)
// Set PGA/voltage range
config |= m_gain;
// Set channels
config |= ADS1015_REG_CONFIG_MUX_DIFF_0_1; // AIN0 = P, AIN1 = N
// Set 'start single-conversion' bit
config |= ADS1015_REG_CONFIG_OS_SINGLE;
// Write config register to the ADC
writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
// Wait for the conversion to complete
pause(m_conversionDelay);
// Read the conversion results
uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
if (m_bitShift == 0) {
return (int16_t)res;
} else {
// Shift 12-bit results right 4 bits for the ADS1015,
// making sure we keep the sign bit intact
if (res > 0x07FF) {
// negative number - extend the sign to 16th bit
res |= 0xF000;
}
return (int16_t)res;
}
}
/**************************************************************************/
/*!
@brief Reads the conversion results, measuring the voltage
difference between the P (AIN2) and N (AIN3) input. Generates
a signed value since the difference can be either
positive or negative.
*/
/**************************************************************************/
int16_t readADC_Differential_2_3()
{
// Start with default values
uint16_t config = ADS1015_REG_CONFIG_CQUE_NONE | // Disable the comparator (default val)
ADS1015_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
ADS1015_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
ADS1015_REG_CONFIG_DR_1600SPS | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
ADS1015_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default)
// Set PGA/voltage range
config |= m_gain;
// Set channels
config |= ADS1015_REG_CONFIG_MUX_DIFF_2_3; // AIN2 = P, AIN3 = N
// Set 'start single-conversion' bit
config |= ADS1015_REG_CONFIG_OS_SINGLE;
// Write config register to the ADC
writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
// Wait for the conversion to complete
pause(m_conversionDelay);
// Read the conversion results
uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
if (m_bitShift == 0) {
return (int16_t)res;
} else {
// Shift 12-bit results right 4 bits for the ADS1015,
// making sure we keep the sign bit intact
if (res > 0x07FF) {
// negative number - extend the sign to 16th bit
res |= 0xF000;
}
return (int16_t)res;
}
}
/**************************************************************************/
/*!
@brief Sets up the comparator to operate in basic mode, causing the
ALERT/RDY pin to assert (go from high to low) when the ADC
value exceeds the specified threshold.
This will also set the ADC in continuous conversion mode.
*/
/**************************************************************************/
void startComparator_SingleEnded(uint8_t channel, int16_t threshold)
{
// Start with default values
uint16_t config = ADS1015_REG_CONFIG_CQUE_1CONV | // Comparator enabled and asserts on 1 match
ADS1015_REG_CONFIG_CLAT_LATCH | // Latching mode
ADS1015_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
ADS1015_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
ADS1015_REG_CONFIG_DR_1600SPS | // 1600(ADS1015) or 250(ADS1115) samples per second (default)
ADS1015_REG_CONFIG_MODE_CONTIN | // Continuous conversion mode
ADS1015_REG_CONFIG_MODE_CONTIN; // Continuous conversion mode
// Set PGA/voltage range
config |= m_gain;
// Set single-ended input channel
switch (channel) {
case (0):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_0;
break;
case (1):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_1;
break;
case (2):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_2;
break;
case (3):
config |= ADS1015_REG_CONFIG_MUX_SINGLE_3;
break;
}
// Set the high threshold register
// Shift 12-bit results left 4 bits for the ADS1015
writeRegister(m_i2cAddress, ADS1015_REG_POINTER_HITHRESH, threshold << m_bitShift);
// Write config register to the ADC
writeRegister(m_i2cAddress, ADS1015_REG_POINTER_CONFIG, config);
}
/**************************************************************************/
/*!
@brief In order to clear the comparator, we need to read the
conversion results. This function reads the last conversion
results without changing the config value.
*/
/**************************************************************************/
int16_t getLastConversionResults()
{
// Wait for the conversion to complete
pause(m_conversionDelay);
// Read the conversion results
uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;
if (m_bitShift == 0) {
return (int16_t)res;
} else {
// Shift 12-bit results right 4 bits for the ADS1015,
// making sure we keep the sign bit intact
if (res > 0x07FF) {
// negative number - extend the sign to 16th bit
res |= 0xF000;
}
return (int16_t)res;
}
}
H Source:
/**************************************************************************/
/*!
@file Adafruit_ADS1015.h
@author K. Townsend (Adafruit Industries)
@license BSD (see LICENSE.txt)
Ported to mbed by Arve Seljebu - arve0.github.io
This is a library for the Adafruit ADS1015 breakout board
----> https://www.adafruit.com/products/1083
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@section HISTORY
v1.0 - First release
v1.1 - Added ADS1115 support - W. Earl
v1.1.1 - Ported to mbed - Arve Seljebu
*/
/**************************************************************************/
#include "simpletools.h"
#include "simplei2c.h"
/*=========================================================================
I2C ADDRESS/BITS
-----------------------------------------------------------------------*/
#define ADS1015_ADDRESS (0x48) // 1001 000 (ADDR = GND)
/*=========================================================================*/
/*=========================================================================
CONVERSION DELAY (in mS)
-----------------------------------------------------------------------*/
#define ADS1015_CONVERSIONDELAY (1)
/*=========================================================================*/
/*=========================================================================
POINTER REGISTER
-----------------------------------------------------------------------*/
#define ADS1015_REG_POINTER_MASK (0x03)
#define ADS1015_REG_POINTER_CONVERT (0x00)
#define ADS1015_REG_POINTER_CONFIG (0x01)
#define ADS1015_REG_POINTER_LOWTHRESH (0x02)
#define ADS1015_REG_POINTER_HITHRESH (0x03)
/*=========================================================================*/
/*=========================================================================
CONFIG REGISTER
-----------------------------------------------------------------------*/
#define ADS1015_REG_CONFIG_OS_MASK (0x8000)
#define ADS1015_REG_CONFIG_OS_SINGLE (0x8000) // Write: Set to start a single-conversion
#define ADS1015_REG_CONFIG_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress
#define ADS1015_REG_CONFIG_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion
#define ADS1015_REG_CONFIG_MUX_MASK (0x7000)
#define ADS1015_REG_CONFIG_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default)
#define ADS1015_REG_CONFIG_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3
#define ADS1015_REG_CONFIG_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3
#define ADS1015_REG_CONFIG_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3
#define ADS1015_REG_CONFIG_MUX_SINGLE_0 (0x4000) // Single-ended AIN0
#define ADS1015_REG_CONFIG_MUX_SINGLE_1 (0x5000) // Single-ended AIN1
#define ADS1015_REG_CONFIG_MUX_SINGLE_2 (0x6000) // Single-ended AIN2
#define ADS1015_REG_CONFIG_MUX_SINGLE_3 (0x7000) // Single-ended AIN3
#define ADS1015_REG_CONFIG_PGA_MASK (0x0E00)
#define ADS1015_REG_CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3
#define ADS1015_REG_CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1
#define ADS1015_REG_CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default)
#define ADS1015_REG_CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4
#define ADS1015_REG_CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8
#define ADS1015_REG_CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16
#define ADS1015_REG_CONFIG_MODE_MASK (0x0100)
#define ADS1015_REG_CONFIG_MODE_CONTIN (0x0000) // Continuous conversion mode
#define ADS1015_REG_CONFIG_MODE_SINGLE (0x0100) // Power-down single-shot mode (default)
#define ADS1015_REG_CONFIG_DR_MASK (0x00E0)
#define ADS1015_REG_CONFIG_DR_128SPS (0x0000) // 128 samples per second - ADS1115 8SPS
#define ADS1015_REG_CONFIG_DR_250SPS (0x0020) // 250 samples per second - ADS1115 16SPS
#define ADS1015_REG_CONFIG_DR_490SPS (0x0040) // 490 samples per second - ADS1115 32SPS
#define ADS1015_REG_CONFIG_DR_920SPS (0x0060) // 920 samples per second - ADS1115 64SPS
#define ADS1015_REG_CONFIG_DR_1600SPS (0x0080) // 1600 samples per second - ADS1115 250SPS (default)
#define ADS1015_REG_CONFIG_DR_2400SPS (0x00A0) // 2400 samples per second - ADS1115 475SPS
#define ADS1015_REG_CONFIG_DR_3300SPS (0x00C0) // 3300 samples per second - ADS1115 860SPS
#define ADS1015_REG_CONFIG_CMODE_MASK (0x0010)
#define ADS1015_REG_CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default)
#define ADS1015_REG_CONFIG_CMODE_WINDOW (0x0010) // Window comparator
#define ADS1015_REG_CONFIG_CPOL_MASK (0x0008)
#define ADS1015_REG_CONFIG_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default)
#define ADS1015_REG_CONFIG_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active
#define ADS1015_REG_CONFIG_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted
#define ADS1015_REG_CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default)
#define ADS1015_REG_CONFIG_CLAT_LATCH (0x0004) // Latching comparator
#define ADS1015_REG_CONFIG_CQUE_MASK (0x0003)
#define ADS1015_REG_CONFIG_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions
#define ADS1015_REG_CONFIG_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions
#define ADS1015_REG_CONFIG_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions
#define ADS1015_REG_CONFIG_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default)
/*=========================================================================*/
typedef enum {
GAIN_TWOTHIRDS = ADS1015_REG_CONFIG_PGA_6_144V,
GAIN_ONE = ADS1015_REG_CONFIG_PGA_4_096V,
GAIN_TWO = ADS1015_REG_CONFIG_PGA_2_048V,
GAIN_FOUR = ADS1015_REG_CONFIG_PGA_1_024V,
GAIN_EIGHT = ADS1015_REG_CONFIG_PGA_0_512V,
GAIN_SIXTEEN = ADS1015_REG_CONFIG_PGA_0_256V
} adsGain_t;
// Instance-specific properties
uint8_t m_i2cAddress;
uint8_t m_conversionDelay;
uint8_t m_bitShift;
adsGain_t m_gain;
i2c* m_i2c;
extern double extruder_celsius;
extern double bed_celsius;
uint16_t readADC_SingleEnded(uint8_t channel);
int16_t readADC_Differential_0_1(void);
int16_t readADC_Differential_2_3(void);
void startComparator_SingleEnded(uint8_t channel, int16_t threshold);
int16_t getLastConversionResults();
void setGain(adsGain_t gain);
adsGain_t getGain(void);
uint16_t readRegister(uint8_t i2cAddress, uint8_t reg);
void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value);
void UpdateTemp(uint8_t channel);
void adc_init(void);
Software License Agreement (BSD License)
Copyright (c) 2012, Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
It appears that everything I need is right there in the datasheet, by just figuring out the output voltage from the voltage divide. Am I missing something???
For instance, going back to the 4.7K resistor, here is how a table looks when made with the datasheet. Is there any reason I could not do this? However, there is the situation of thermistors self-heating, but I read that with the 4.7K resistor, it is not that bad. Do I really need all the other steinhart- blah blah blah?
EDIT: Now let's take it a step further. I don't see how Note (1) applies when using FS = ±4.096V(1):
(1) This parameter expresses the full-scale range of the ADC scaling. In no event should more than VDD + 0.3V be applied to this device.
I do not see how this can physically happen in my situation, so it should not be a concern. My question now becomes, if that table is acceptable, without needing special code to decipher the temperatures, is the 4.096V scale accurate enough to do comparisons against the values shown?
I just found a very interesting piece of code, and since we are on the topics of 3D printers and thermistors, I thought I would share this link. The old script and old tables should now be history.
I think what you are proposing in post #36 is fine. The code can look up and interpolate in that table directly from voltage to temperature. If you don't want to mess with floating point and one of the calculation methods like Steinhart-Hart, then the table is much faster and easier with integer math. It could alternatively take it in two steps, from voltage to resistance then resistance to temperature.
At 250°C, the voltage is changing at about 2.1 mV/°C, and the resolution of the ADC on the 4.096V range happens to be 2mV. So that is okay if +/- 1°C resolution is going to be in the right ballpark for the machine.
The resolution is better at lower temperatures. 0.3 °C at room temperature, and 0.08 °C at around 100°C where the thermistor resistance is close to the 4.7kΩ reference.
Thanks for getting back to me. I have to make a run, but when I get back, I will be testings the table with a pot and see how it all works out in theory. And if everything seems fine, then I will test the thermistor within the next couple days.
Anyhow, I really appreciate all of your input on this matter.
By the way, which TI ADC do you use? And will that code work for you?
It does not look like I will get around to testing tonight, because I have gone off on another tangent.
The code can look up and interpolate in that table directly from voltage to temperature. If you don't want to mess with floating point and one of the calculation methods like Steinhart-Hart, then the table is much faster and easier with integer math. It could alternatively take it in two steps, from voltage to resistance then resistance to temperature.
What I want to do now is precompute the signal voltages into RAW ADC values and put those values into the table, instead of the signal voltages. I will put those values on the left and the temperatures on the right. By doing that, I can quickly compare the actual RAW ADC values against the RAW table values.
Bruce, I use the ADS1115. It is the 16 bit version, otherwise much the same as the 12 bit ADS1015. The same set of PGA gains, down to 8 µV steps on the most sensitive range of ±256mV.
While it is true that the inputs cannot be allowed to past the rails by more than 0.3V, it is also true and very useful in some applications (like power supply shunts) that the ADC will quite happily and accurately measure voltages that are outside the power supply limits by up to around 256mV.
That code was originally for both the ADS1015 and the ADS1115, but I removed the support for the ADS1115 during the port.
Instead of waiting for a low tolerance resistor, I used the actual resistance of a 4.7K(4602 Ohms) to create my table. I believe this is correct and the leftmost items should match ADC input, whereas the rightmost items are the temperature at that ADC reading. However I am still working on this....
// The number of value pairs in the table.
#define NUMOFPAIRS 20
// This is a table of ADC readings and corresponding temperature values for both the
// extruder and heated build platform. In determining the thermistor resistance values,
// data was extracted from the EPCOS B57560G1104F000 datasheet, because that is the
// thermistor being utilized for both the extruder and heated build platform.
//
// During the calculations of this table, a resistor having a nominal value of 4.7K,
// with a 10% tolerance was utilized, however the calculations were performed and based
// upon the actual resistance, which was 4,602 Ohms.
const uint16_t valuetable[][NUMOFPAIRS] =
{ {3950,20},
{3915,25},
{3873,30},
{3823,35},
{3766,40},
{3699,45},
{3622,50},
{3535,55},
{3439,60},
{3333,65},
{3216,70},
{3091,75},
{2957,80},
{2817,85},
{2672,90},
{2523,95},
{2373,100},
{2221,105},
{2072,110},
{1927,115},
{1786,120},
{1650,125},
{1520,130},
{1398,135},
{1283,140},
{1176,145},
{1077,150},
{985,155},
{900,160},
{823,165},
{751,170},
{686,175},
{627,180},
{573,185},
{524,190},
{480,195},
{439,200},
{402,205},
{370,210},
{339,215},
{311,220},
{287,225},
{264,230},
{243,235},
{225,240},
{207,245},
{192,250},
{177,255},
{165,260},
{153,265},
{141,270},
{131,275},
{123,280} };
As mentioned many times before, math and datasheets are just not my thing..... And apparently it is coming back to bite, because the readings, results, and actual settings just don't jive.
In my sample reading and lookup routine, I am receiving a sample of 1155, then looking it up in the table, which returns a temperature of 150 degrees for the table value of 1077. Using the sample value of 1155 and by looking at the table below, you will see that the lookup and table reading part is working correctly. According to the datasheet, at 150 degrees, the thermistor should have a resistance of 1641.9 Ohms, but when I disconnect all of the wires going to the potentiometer and take a reading, I get 10,300 Ohms. So apparently my math is off somewhere.
Problem Summary: Resistance should be at 1641.9 Ohms, but is actually 10,300 Ohms.
Sample Provided: 1155
Table Value: 1077
Return Temperature: 150
Potentiometer Setting: 10,300 Ohms
Datasheet Resistance at 150 degrees: 1641.9 Ohms (R1)
Series Resistor In Voltage Divider: 4602 Ohms (R2)
Signal Voltage Output To ADC: 0.854V (According to my calculations)
VDD: 3.3V
When calculating this particular table entry, this would have been the formula and parameters that I used:
EDIT: This don't jive, because the table value should be 1059
1077 = 0.854V * 4095 / 3.3V
or
Table Value = ADC Signal Voltage * Number of ADC Readings / VDD
Is this the correct formula for obtaining the table value?
// The number of elements in the lookup table.
#define TABLE_SIZE 53
//
// This is a table of ADC readings and corresponding temperature values for both the
// extruder and heated build platform. In determining the thermistor resistance values,
// data was extracted from the EPCOS B57560G1104F000 datasheet, because that is the
// thermistor being utilized for both the extruder and heated build platform.
//
// During the calculations of this table, a resistor having a nominal value of 4.7K,
// with a 10% tolerance was utilized, however the calculations were performed and based
// upon the actual resistance, which was 4,602 Ohms.
LOOKUPTEMP lookup_temp[TABLE_SIZE] = {
{.sample = 3950, .temp = 20},
{.sample = 3915, .temp = 25},
{.sample = 3873, .temp = 30},
{.sample = 3823, .temp = 35},
{.sample = 3766, .temp = 40},
{.sample = 3699, .temp = 45},
{.sample = 3622, .temp = 50},
{.sample = 3535, .temp = 55},
{.sample = 3439, .temp = 60},
{.sample = 3333, .temp = 65},
{.sample = 3216, .temp = 70},
{.sample = 3091, .temp = 75},
{.sample = 2957, .temp = 80},
{.sample = 2817, .temp = 85},
{.sample = 2672, .temp = 90},
{.sample = 2523, .temp = 95},
{.sample = 2373, .temp = 100},
{.sample = 2221, .temp = 105},
{.sample = 2072, .temp = 110},
{.sample = 1927, .temp = 115},
{.sample = 1786, .temp = 120},
{.sample = 1650, .temp = 125},
{.sample = 1520, .temp = 130},
{.sample = 1398, .temp = 135},
{.sample = 1283, .temp = 140},
{.sample = 1176, .temp = 145},
{.sample = 1077, .temp = 150},
{.sample = 985, .temp = 155},
{.sample = 900, .temp = 160},
{.sample = 823, .temp = 165},
{.sample = 751, .temp = 170},
{.sample = 686, .temp = 175},
{.sample = 627, .temp = 180},
{.sample = 573, .temp = 185},
{.sample = 524, .temp = 190},
{.sample = 480, .temp = 195},
{.sample = 439, .temp = 200},
{.sample = 402, .temp = 205},
{.sample = 370, .temp = 210},
{.sample = 339, .temp = 215},
{.sample = 311, .temp = 220},
{.sample = 287, .temp = 225},
{.sample = 264, .temp = 230},
{.sample = 243, .temp = 235},
{.sample = 225, .temp = 240},
{.sample = 207, .temp = 245},
{.sample = 192, .temp = 250},
{.sample = 177, .temp = 255},
{.sample = 165, .temp = 260},
{.sample = 153, .temp = 265},
{.sample = 141, .temp = 270},
{.sample = 131, .temp = 275},
{.sample = 123, .temp = 280},
};
The program is now reporting a temp of 135 and according to my understanding of the datasheet, the thermistor should have a resistance of 2386.1 Ohms at that point. However the potentiometer is still set at about 10,300 Ohms.
AAAARRRRGGGG
EDIT: Adding a copy of the new table, although it is useless.
EDIT: Assuming the current 10300 Ohm setting of the pot and 4602 Ohms for the fixed resistor, this is the formula I am using for obtainining the signal value. Is this correct?
If you don't need a temperature display you could make use of the Vdd / 2 switching point of CMOS inputs. Adjust R1 to the value of the thermistor at the target temperature. Feed the output to a prop input pin instead of the ADC. Just feed the input pin signal, inverted, to an output pin. That should give you kind of an automatic PWM signal for your heating element.
This scheme depends on the Vdd / 2 switching point being consistant. Might work.
Sorry Bruce. On the 4096V range, it is 0.002V per bit. I have a knee-jerk response to the number 4096, but the full range if really 8192mV, covering from -4096 to +4096 mV. The raw ADCcount goes from -2048 to +2048.
So ADCcount =signalVoltage * 500.
milliVolts = ADCcount * 2 on the 4.096V range, PGA=1
This formula is correct for voltage...
signalVolts = 3.3 * R1 / (R1 + R2)
2.281 = 3.3 * 10300 / (10300 + 4602) * 3.3
Redo the table using the values for R1 at temperatures...
ADCcount = 3.3 * R1 / (R1 + R2) * 500
Example
ADCcount = 500 * 3.3 * 2386 / (2386+4602) = 500 * 1.126 = 563 at 135°C
Okay, it now looks like we have something, because I have 2.98V at the signal. So redoing the table one more time ought to do it. Thanks for looking that over and giving me guidance.
Thank you very much Tracy. I would have been going in circles for days. I truly appreciate all of your assistance, patience, and the fact that you stuck it through with me.
At this point, it is basically a non-issue, since my code is now working. However, I truly did not understand it, because I am not that smart pertaining to electronics. If I implement anything, I like to fully comprehend it. And in this case, I am choosing the least path of resistance
However, please don't be discouraged, I am sure someone will find the information useful.
Comments
I now see why Tracy was talking about inverting R1 and R2, and I am now hoping that schematic is correct and that the board was made correctly.
EDIT: On second hand perhaps I don't
I thank you very very much for all your time and patience.
R2 is the reference connected to 3.3V and R1 is the thermistor, TDK #B57560G1104, 100kΩ at 25°C.
Are there separate thermistors for the "extruder" and for the "bed"?
The hotter it gets, the lower the resistance and the lower the voltage across R1 to be sensed by the ADC. The non-linear curve is such that the voltage steps become smaller and smaller as the temperature rises. The big advantage of an ADC with a PGA is that it is possible to write Prop code to change the gain factor dynamically. The system can maintain a high resolution of temperature across the whole range. Higher PGA gain at higher temperatures. That is the way I would do it, given that ADC. By the way, which ADC is it that you have? I use TI versions, ADSxxxx, but Microchip and others have similar parts.
Once you have the voltage across the thermistor, what do you expect to do with it? My thought is that the Prop will then solve for the resistance, R1 = R2 * 3.3 / (3.3 - Vadc). Then use that to calculate the temperature or interpolate it in a table using the data provided by TDK.
Thanks for getting back to me, because once again, I have been going in circles
I don't have the bed made yet, but yes there will be a seperate thermistor for the bed.
I have the ADS1015 from TI.
My intention was to use the Steinhart-Hart equation to turn the voltage reading into temperture, but I am rethinking this whole issue, however once again I may be barking up the wrong tree. It seems that a lot of people rely on tables, that further rely on the Steinhart-Hart equation. What is wrong with just calculating the output voltage from the values given in the datasheet along with the known value of the fixed resistor, and creating my own table from that data? In the source code below and in other source code that I have, when using the Steinhart-Hart equation, beta factors, thermal coefficients, and etc.. are brought in to determine the temperature. It appears that everything I need is right there in the datasheet, by just figuring out the output voltage from the voltage divide. Am I missing something???
I hope you are decent with looking at C source code.
C Source:
H Source:
For instance, going back to the 4.7K resistor, here is how a table looks when made with the datasheet. Is there any reason I could not do this? However, there is the situation of thermistors self-heating, but I read that with the 4.7K resistor, it is not that bad. Do I really need all the other steinhart- blah blah blah?
EDIT: Now let's take it a step further. I don't see how Note (1) applies when using FS = ±4.096V(1):
I do not see how this can physically happen in my situation, so it should not be a concern. My question now becomes, if that table is acceptable, without needing special code to decipher the temperatures, is the 4.096V scale accurate enough to do comparisons against the values shown?
http://blog.reprap.org/2012/06/say-goodby-to-thermistor-table-misery.html
At 250°C, the voltage is changing at about 2.1 mV/°C, and the resolution of the ADC on the 4.096V range happens to be 2mV. So that is okay if +/- 1°C resolution is going to be in the right ballpark for the machine.
The resolution is better at lower temperatures. 0.3 °C at room temperature, and 0.08 °C at around 100°C where the thermistor resistance is close to the 4.7kΩ reference.
Thanks for getting back to me. I have to make a run, but when I get back, I will be testings the table with a pot and see how it all works out in theory. And if everything seems fine, then I will test the thermistor within the next couple days.
Anyhow, I really appreciate all of your input on this matter.
By the way, which TI ADC do you use? And will that code work for you?
It does not look like I will get around to testing tonight, because I have gone off on another tangent.
What I want to do now is precompute the signal voltages into RAW ADC values and put those values into the table, instead of the signal voltages. I will put those values on the left and the temperatures on the right. By doing that, I can quickly compare the actual RAW ADC values against the RAW table values.
I will keep you posted.
While it is true that the inputs cannot be allowed to past the rails by more than 0.3V, it is also true and very useful in some applications (like power supply shunts) that the ADC will quite happily and accurately measure voltages that are outside the power supply limits by up to around 256mV.
I'm sorry, I can't help with the C code.
That code was originally for both the ADS1015 and the ADS1115, but I removed the support for the ADS1115 during the port.
Instead of waiting for a low tolerance resistor, I used the actual resistance of a 4.7K(4602 Ohms) to create my table. I believe this is correct and the leftmost items should match ADC input, whereas the rightmost items are the temperature at that ADC reading. However I am still working on this....
In my sample reading and lookup routine, I am receiving a sample of 1155, then looking it up in the table, which returns a temperature of 150 degrees for the table value of 1077. Using the sample value of 1155 and by looking at the table below, you will see that the lookup and table reading part is working correctly. According to the datasheet, at 150 degrees, the thermistor should have a resistance of 1641.9 Ohms, but when I disconnect all of the wires going to the potentiometer and take a reading, I get 10,300 Ohms. So apparently my math is off somewhere.
Problem Summary: Resistance should be at 1641.9 Ohms, but is actually 10,300 Ohms.
Sample Provided: 1155
Table Value: 1077
Return Temperature: 150
Potentiometer Setting: 10,300 Ohms
Datasheet Resistance at 150 degrees: 1641.9 Ohms (R1)
Series Resistor In Voltage Divider: 4602 Ohms (R2)
Signal Voltage Output To ADC: 0.854V (According to my calculations)
VDD: 3.3V
When calculating this particular table entry, this would have been the formula and parameters that I used:
EDIT: This don't jive, because the table value should be 1059
1077 = 0.854V * 4095 / 3.3V
or
Table Value = ADC Signal Voltage * Number of ADC Readings / VDD
Is this the correct formula for obtaining the table value?
When the 4.096V range is chosen, the resolution is 0.001V 0.002V per bit.
Okay, if I understand you correctly.....
I should take all of my calculated signal voltages and multiply them by 1000 and those results become my actual "sample" values in the table.
Is that correct?
I will create a new table in the meantime, like stated above.
The program is now reporting a temp of 135 and according to my understanding of the datasheet, the thermistor should have a resistance of 2386.1 Ohms at that point. However the potentiometer is still set at about 10,300 Ohms.
AAAARRRRGGGG
EDIT: Adding a copy of the new table, although it is useless.
EDIT: Assuming the current 10300 Ohm setting of the pot and 4602 Ohms for the fixed resistor, this is the formula I am using for obtainining the signal value. Is this correct?
SIGNAL = R1 / (R1 + R2) * VDD
2.281 = 10300 / (10300 + 4602) * 3.3
This scheme depends on the Vdd / 2 switching point being consistant. Might work.
Sandy
So ADCcount =signalVoltage * 500.
milliVolts = ADCcount * 2 on the 4.096V range, PGA=1
This formula is correct for voltage...
signalVolts = 3.3 * R1 / (R1 + R2)
2.281 = 3.3 * 10300 / (10300 + 4602) * 3.3
Redo the table using the values for R1 at temperatures...
ADCcount = 3.3 * R1 / (R1 + R2) * 500
Example
ADCcount = 500 * 3.3 * 2386 / (2386+4602) = 500 * 1.126 = 563 at 135°C
ADCcount = 500 * 3.3 * 100000 / (100000+4602) = 500 * 3.155 = 1577 at 25°C
Okay, it now looks like we have something, because I have 2.98V at the signal. So redoing the table one more time ought to do it. Thanks for looking that over and giving me guidance.
Yep, that did her...
It was very difficult to adjust that pot.... Very sensitive But here are the results I came up with:
Thank you very much Tracy. I would have been going in circles for days. I truly appreciate all of your assistance, patience, and the fact that you stuck it through with me.
Bruce
A lot of good suggestions ... to be sure ... here's one more (file it or flush it)
I put it in a PDF because I don't know how to use the BB codes to integrate graphs?
... Tim
At this point, it is basically a non-issue, since my code is now working. However, I truly did not understand it, because I am not that smart pertaining to electronics. If I implement anything, I like to fully comprehend it. And in this case, I am choosing the least path of resistance
However, please don't be discouraged, I am sure someone will find the information useful.
Amen to that ... I like to know what's going on under the hood. But for those interested?
[post=1150885]This Post[/post] has details of theory for V2F.
... Tim