NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Support Forum » Meat Thermometer Tutorial Issues

April 29, 2012
by gsmoove
gsmoove's Avatar

Hello,

I'm a complete newbie with this stuff. I successfully completed the meat thermometer tutorial but thought I'd give myself the extra challenge of lighting an LED when the raw temp was greater then 100. This actually worked, except that once the light turns on the temperature value immediately rises about 20 degrees and the LCD turns off. Once the temp goes back below 100 and the LED turns off the temperature value drops 20 degrees and the LCD turns back on.

I'm not sure if this is because of some basic programming concept that I haven't grasped or variation in the voltage once the LED starts drawing energy. Anyone have any idea? I've attached the code I patched together.

// meat_thermometer.c
// for NerdKits with ATmega168
// Author: Humberto Evans
// based on temp sensor code by Mike Robbins
// support@nerdkits.com

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);  
}

//scale used to calibrate the sensor
#define th 176.5 //174.87 //high value read in boiling water
#define tl 37.7 //37.35   //low value read in freezing water
double sa;
double sb;
double scale(double sample){
  return sa*sample+sb;
}

double lastTrans = 0;
double a;
//Implements the inverse transfer functions
  //        (x[n] - a*x[n-1])
  //  y[n]= -----------------  
  //              (1-a)

double transFunc(double sample){
  double answer = (sample - a*lastTrans)/(1-a);
  lastTrans = sample;
  return answer;
}

double lastLow = 0;
double filterTau;
//Implments the lowpass filter to supress noise
// y[n] = tau*x[n] + (1-tau)*y[n-1]
double lowPass(double sample){
  double answer = filterTau*sample + (1-filterTau)*lastLow;
  lastLow = answer;
  return answer;
}

int main() {
  // set LED pin
  DDRC |= (1<<PC4);

  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_clear_and_home();

  lcd_line_four();
  fprintf_P(&lcd_stream, PSTR("  www.NerdKits.com  "));
  lcd_line_four();
  fprintf_P(&lcd_stream, PSTR("  www.NerdKits.com  "));

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;

  // must initialize global constants before they are used.
  // filterTau and a are tuned experimentally to get the right responce. 
  filterTau = 0.15;
  a = 0.99;
  sa = (212.0-32.0)/(th-tl);
  sb = 212.0-sa*th;

  lastTrans = lastLow = adc_read(); // get an initial reading to initialize the filters

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    temp_avg = scale(temp_avg); //get the scaled raw value
    double trans = transFunc(temp_avg); //apply the predictive transfer function
    double low = lowPass(trans); //low pass filter
    // write message to LCD
    lcd_home();
    fprintf_P(&lcd_stream, PSTR("Raw:      %7.2f"), temp_avg);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Transfer: %7.2f "), trans);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("Low Pass: %7.2f"), low);

    // write message to serial port
    printf_P(PSTR("%.2f %.2f %.2f %.2f %.2f\r\n"), temp_avg,trans,low,a,filterTau);

    // LED control
    if(temp_avg>100){
      PORTC |= (1<<PC4);
    }
    else if(temp_avg<100){
      PORTC &= ~(1<<PC4);
    }
  }

  return 0;
}
April 30, 2012
by Rick_S
Rick_S's Avatar

Two guesses on my part.

  1. You are running off battery
  2. You did not use a current limiting resistor on the LED

Your analysis is most likely correct in that when you turn on the LED the current draw is such that there is no longer adequate power to fully operate the LCD.

If you aren't currently using one, place a resistor between pin PC4 and your LED (220 Ohm to 1K Ohm should be fine). Secondly, make sure you use a fresh battery.

Rick

April 30, 2012
by gsmoove
gsmoove's Avatar

Thanks Rick. I'll try tonight. I thought this might be the issue but I was too tired to experiment with it last night.

Post a Reply

Please log in to post a reply.

Did you know that the Timer/Counter modules on the microcontroller can be configured to output a PWM (Pulse Width Modulation) signal? Learn more...