/**
* Basic ground temperature thermometer.
*
*
* This document assumes you are working with the 503 thermistor that
* comes with RadioShack's "Sidekick basic kit for Aruduino"
*
*
* Configuration:
*
* This software is preconfigured to work with the GE NTC 503 in the
* following circuit configuration. Because the base resistance of the
* resistor can vary by about 5% there is an optional calibration
* phase, but we've generally found you can get within a degree or two.
*
* Ground <---/\/\/ 50k ohm /\/\---> A0 <--- /\/\/ NTC 503 /\/\-> +5V
*
* To get the output this design also assumes that you have, a series
* of 9 LEDS connected to digital pins 2-10 inclusive, wired in series
* with a resistor between about 300 and 600 ohms.
*
* How temperature recording works
*
* There are four distinct steps that go into turning a sensor value
* into a temperature. First we have to address the non-linearity of
* the resistor network that we use to read the sensor. Our A/D
* converter values do not linearly translate into resistances.
*
* The above circult uses a pulldown resistor; if we measure the
* voltage between the thermistor and the resistor it will follow the
* following formuala:
*
* V0 = V * R / (R0 + R)
*
* Where:
*
* "V" is the voltage of the circuit. For our purposes we can use the
* maximum range of the A/D converter as our unit.
* R is the resitance of our thermistor
* R0 is the resistance of our pull
* V0 is the value our A/D converter will read
*
* The second step is to apply any calibration factor we might have.
* THe "base resistance" of the sensor in the Radioshack kit is 5%,
* this can result in a change of a few degrees, expecially in the
* higher temperatures. GE 503's also vary in theor "B" value - that
* is the rate at which they respond to temperature changes. We don't
* at this time try to calibrate that, but might in the future.
*
*
* We generate a "correction factor" that we multiple our computed
* resistance by. The foruma for this is:
*
* C = Rexp / Rcal
*
* Where:
*
* Rexp is the resistance we expect for our thermistor at 0C
*
* Rcal is the actual resistance resistance we measured during our
* calibration phase.
*
* So how do we get Rexp? We can compute it from the Steinhard-Hart
* equation. Radioshack doesn't seem to provide a "B" value for this
* thermistor, but 2700 seems about right.
*
* The computation for Rexp is:
*
* Rexp = R0 * exp(B * (1/T - 1/T0))
*
* Where:
* R0: The resistance at T0
* T0: The reference temperature of this thermisor's rated
* resistance (typically 25C, or 298.15K)
* B: The "B" parameter (we're guessing at 4150)
* T: The temperature we want to compute the resistance for
*
* Once we know the current resistance of the sensor we need to
* convert this into Kelvin. Again, the formual for this is
* non-linear. Because GE provides a B parameter value, we use the B
* parameter with in the Steinhard-Hart equation as described in the
* wikipedia (http://en.wikipedia.org/wiki/Thermistor)
*
* So to generate a temperature we compute:
*
* T = (1 / (1 / T0 + 1 / B * log(R / R0)))
*
* Where:
* T0: The reference "rated" temp of this thermistor (298.15K)
* B: The B value (4150)
* R: The current resistance (adjusted in our calibration cycle)
* R0: The rated resistance at T0
*
* We then translate this into a series of 9 bits that describes the
* temperature as quarte degrees C above -20C. This value is shown
* with a novel encoding that permits the storage of values between
* [0, 255 + 16] to be stored and decoded within 9 bits without
* abigioutly in the event of reversal.
*
* The following function is applied to the temperature value: The
* temperature value is broken into two nibbles, the second bit
* reversed with a single bit between them indicating which of the
* nibbles is greater. This is done to ensure the temepature can be
* unabigiously decoded whether read from the sensor forward or
* backward.
*
* Wnyc has a decoder at http://project.wnyc.org/cicadas/
*
*/
#include
#define THERMOMETER 0
#define SET_LOW 12
/* The resistance expected of an NTC 503 at 0C
* This is computed by R = R0 * e**(B*1/T - 1/T0) where:
*
* R0 is the resistance at T0 kelvin
* B is the NTC B parameter
* T is the temperature the resistor is actually at
* R is the anticipated resistance
*/
const float B = 2700;
const float R0 = 50000;
const float T0 = 298.15;
const float expected_resistance = R0 * exp(B * (1/273.15 - 1/T0));
/* If you change the pulldown resistors change this. */
const float pulldown_resistance = 50000;
/* If reverse the direction of the resistors and sensor, you have
* what's called a pull up, not pull down. Set this to true.
*/
const bool using_pullups = false;
/* THe number of A/D samples to take to reduce the impact of noise.
* This is especially important if reading while connected to a USB
* jack.
*/
const int temp_samples = 16;
/* The max value the A/D converter shows. For an adrunio this will
* always be 1023 */
const int AD_MAX = 1023;
void setup()
{
Serial.begin(19200);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, INPUT);
pinMode(12, INPUT);
/* Turn on the pull up resistors. See
* http://arduino.cc/en/Tutorial/DigitalPins
*/
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
}
/* Set one of the bits in the display
*
* bit: the position from 0..8 inclusive. Which is right and left?
* Doesn't matter with our encoding!
*
* value: true is on, false is off.
*/
void setBit(int bit, boolean value) {
digitalWrite(bit + 2, value ? HIGH : LOW);
}
/* Given a sensor value and the resistance of the pulldown resistor
compute the sensed resistance */
float compute_resistance(int value, float pulldown_resistance) {
if (using_pullups)
value = 1024 - value;
return -(pulldown_resistance * (float(value) - 1024.0) / float(value));
}
/* Given a resistance and R0, T0 and B value describing the NTC thermistor
* return the temperature in kelvin.
*/
float compute_temperature(float resistance, float R0, float T0, float B) {
Serial.println("Compute temperature");
Serial.print("Resistance: ");
Serial.println(resistance);
Serial.print("R0: ");
Serial.println(R0);
Serial.print("T0: ");
Serial.println(T0);
Serial.print("B: ");
Serial.println(B);
return 1/(1.0 / T0 + (1.0 / B) * log(resistance / R0));
}
float compute_temperature(float resistance) {
return compute_temperature(resistance, R0, T0, B);
}
void all_on() {
int i;
for(i=0; i < 9;i++) {
setBit(i, true);
}
}
void error_flash() {
int i;
for(i=0; i<9; i++)
setBit(i, i & 1);
delay(500);
for(i=0; i<9; i++)
setBit(i, ~(i & 1));
delay(500);
}
void all_off() {
int i;
for(i=0; i < 9; i++ ) {
setBit(i, false);
}
}
/* Write a value between [0..271] inclusive to the display in a way
* such that reversing the bits doesn't matter. The decoder is at
* www.wnyc.org/cicadas and
* http://github.com/wnyc/sensors/javascript/reversible_bits.js
*/
void write(unsigned int i) {
Serial.print("Temp: ");
Serial.print((i * 9.0 - 80.0 ) / 20.0);
Serial.println("F");
Serial.print("Encoded value: ");
Serial.println(i);
if (i > 255 + 16) {
all_on();
return ;
}
if (i > 255) {
i -= 255;
setBit(0, i & 1);
setBit(1, i & 2);
setBit(2, i & 4);
setBit(3, i & 8);
setBit(4, true);
setBit(5, i & 1);
setBit(6, i & 2);
setBit(7, i & 4);
setBit(8, i & 8);
return;
}
setBit(0, i & 1);
setBit(1, i & 2);
setBit(2, i & 4);
setBit(3, i & 8);
setBit(4, ((i >> 4) & 0x0f) < ( i & 0x0f));
setBit(5, i & 128);
setBit(6, i & 64);
setBit(7, i & 32);
setBit(8, i & 16);
}
int measure_temperature() {
int i;
int sum = 0;
for(i=0;i