The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Day: December 12, 2012

  • Arduino Snippets: Temperature Measurement

    Temperature seems an obvious thing to measure, so a bit of rummaging disgorged a classic LM335 temperature sensor that produce an output voltage directly calibrated in Kelvin at 10 mV/K: room temperature runs 296 K = 2.96 V. Nothing could be easier than this:

    LM335 Temperature Sensor

    The downside: a 1 °C temperature change corresponds to only 10 mV, which is barely two LSB of the Arduino ADC. In round numbers, a 1 °F change = 1 LSB, which doesn’t leave much room for measurement noise. I average five successive readings, which may be excessive, but the result seems stable enough:

    const float AVREF = 4.94;                    // Arduino analog reference
    
    #define TAVG 5
    
    float ReadLM335(byte Pin) {
    float Kelvin;
    
    	Kelvin = (float)analogRead(Pin);
    	for (byte i = 1; i < TAVG; i++)
    		Kelvin += (float)analogRead(Pin);
    
    	return Kelvin * (100.0 * AVREF) / (TAVG * 1024.0);
    }
    

    For better accuracy, you must measure VCC on the Arduino board and plug that into the AVREF constant, because the ADC reference voltage comes from the power supply. If you’re powering the Arduino from a USB port, then don’t bother worrying about analog conversion accuracy, because VCC depends on which PC you use, the USB cable length, what load current you draw from the regulator, and probably the phase of the moon.

    The magic number 100.0 converts 10 mV/K to K.

    The four character DL1414 LED display works well enough for the kind of temperatures you might find around a human being and, if you have an LED bargraph display, you may as well throw that into the mix, too.

    LM335 Temperature Sensor – 19 C

    The bargraph has RRYYGGYYRR LEDs, so I scaled the temperature at 5 °C/bar and put 0 °C on the bottom of the display, which means 15-19 and 20-24 °C occupy the green bars in the middle. Fingertip temperatures light up the two yellow bars and body heat gets you into the red, so it’s a reasonable display. Just to show it works, here’s a closer look (0 °C is on the right, but you can reverse that easily enough):

    LM335 Temperature Sensor – 25 C

    The Arduino source code:

    // LM335 Temperature sensor sensor
    // Ed Nisley - KE4ANU - November 2012
    
    //#include <stdio.h>
    //#include <math.h>
    
    //----------
    // Pin assignments
    
    const byte PIN_TEMPERATURE = A1;			// Temperature sensor - LM335 = 10 mV/K
    
    const byte PIN_MOSI = 8;			// data to shift reg
    const byte PIN_SCK  = 6;			// shift clock to shift reg
    const byte PIN_RCKB  = 7;			// latch clock for LED Bargraph
    const byte PIN_RCKC  = 12;			// latch clock for LED character display
    
    const byte PIN_HEARTBEAT = 13;				// DO - Arduino LED
    
    //----------
    // Constants
    
    const int UPDATEMS = 1000;					// update LEDs only this many ms apart
    
    const float AVREF = 4.94;					// Arduino analog reference
    const float KTOC = -273.2;					// Kelvin to Centigrade offset
    
    const float BARSCALE = 5.0;					// degrees per bar increment
    
    #define TCCRxB 0x02							// Timer prescaler
    
    #define LED_SIZE		4				// chars per LED
    #define LED_DISPLAYS	1				// number of displays
    #define LED_CHARS		(LED_DISPLAYS * LED_SIZE)
    
    union DL1414_ {
    	word ShiftWord;				// word overlay
    	struct {					// bitfield sent to the display
    	unsigned int Addr:2;
    	unsigned int NotWrite:1;
    	unsigned int Ctl3_7:5;			// unused bits
    	unsigned int Data:7;
    	unsigned int Data7:1;			// unused bit
    	} ShiftBits;
    };
    
    //----------
    // Globals
    
    int Temperature, BaseTemperature;
    
    word LEDBits;
    
    char LEDCharBuffer[LED_CHARS + 1] = "HELO";		// raw char buffer, can be used as a string
    
    unsigned long MillisNow;
    unsigned long MillisThen;
    
    //-- Helper routine for printf()
    
    int s_putc(char c, FILE *t) {
      Serial.write(c);
    }
    
    //-- Send bits to LED bar driver register
    
    void SetBarBits(word Pattern) {
    
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,Pattern >> 8);
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,Pattern & 0x00ff);
    
    	digitalWrite(PIN_RCKB,HIGH);
    	digitalWrite(PIN_RCKB,LOW);
    
    }
    
    void PulsePinHigh(byte PinID) {
    	digitalWrite(PinID,HIGH);
    	digitalWrite(PinID,LOW);
    }
    
    //-- Write single char to DL1414
    
    void WriteLEDChar(char Char,char CharID) {
    
    	union DL1414_ DL1414;
    
    	DL1414.ShiftBits.Data = Char & 0x7F;
    	DL1414.ShiftBits.Addr = ~CharID & 0x03;		// reverse order of chars
    
    	DL1414.ShiftBits.NotWrite = 1;				// set up data and address
    
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
    	PulsePinHigh(PIN_RCKC);
    
    	//	delay(1000);
    
    	DL1414.ShiftBits.NotWrite = 0;				// write the character
    
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
    	digitalWrite(PIN_RCKC,HIGH);
    	PulsePinHigh(PIN_RCKC);
    
    	//	delay(1000);
    
    	DL1414.ShiftBits.NotWrite = 1;				// disable write
    
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
    	shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
    	PulsePinHigh(PIN_RCKC);
    
    	//	delay(1000);
    
    }
    
    void WriteLEDString(char *pString) {
    
    	for (byte i=0; (i < LED_CHARS) && *pString; ++i)
    		WriteLEDChar(*pString++,i);
    
    	return;
    }
    
    //-- Sample temperature with a dab of averaging
    
    #define TAVG 5
    
    float ReadLM335(byte Pin) {
    float Kelvin;
    
    	Kelvin = (float)analogRead(Pin);
    	for (byte i = 1; i < TAVG; i++)
    		Kelvin += (float)analogRead(Pin);
    
    	return Kelvin * (100.0 * AVREF) / (TAVG * 1024.0);
    }
    
    //------------------
    // Set things up
    
    void setup() {
      pinMode(PIN_HEARTBEAT,OUTPUT);
      digitalWrite(PIN_HEARTBEAT,LOW);	// show we arrived
    
    //  TCCR1B = TCCRxB;					// set frequency for PWM 9 & 10
    //  TCCR2B = TCCRxB;					// set frequency for PWM 3 & 11
    
      pinMode(PIN_MOSI,OUTPUT);
      digitalWrite(PIN_MOSI,LOW);
    
      pinMode(PIN_SCK,OUTPUT);
      digitalWrite(PIN_SCK,LOW);
    
      pinMode(PIN_RCKB,OUTPUT);
      digitalWrite(PIN_RCKB,LOW);
    
      pinMode(PIN_RCKC,OUTPUT);
      digitalWrite(PIN_RCKB,LOW);
    
      Serial.begin(9600);
      fdevopen(&s_putc,0);				// set up serial output for printf()
    
      printf("Temperature sensor - LM335\r\nEd Nisley - KE4ZNU - November 2012\r\n");
    
      BaseTemperature = KTOC + ReadLM335(PIN_TEMPERATURE);
    
      WriteLEDString(LEDCharBuffer);
    
      LEDBits = 0x5555;
      SetBarBits(LEDBits);
    
      printf("Base Temperature: %d C\n",(int)BaseTemperature);
    
      delay(1000);
    
      MillisThen = millis();
    
    }
    
    //------------------
    // Run the test loop
    
    void loop() {
    
    	MillisNow = millis();
    
    	if ((MillisNow - MillisThen) > UPDATEMS) {
    		digitalWrite(PIN_HEARTBEAT,HIGH);
    
    		Temperature = KTOC + ReadLM335(PIN_TEMPERATURE);
    
    		printf("Temperature: %d C\n",(int)Temperature);
    
    		LEDBits = 0x0200 >> (1 + (int)(Temperature/BARSCALE));	// move upward on display!
    		SetBarBits(LEDBits);
    
    		sprintf(LEDCharBuffer,"%-3dC",(int)Temperature);
    		WriteLEDString(LEDCharBuffer);
    
    		digitalWrite(PIN_HEARTBEAT,LOW);
    
    		MillisThen = MillisNow;
    	}
    
    }