Norcold N300.3 Digital Thermometer

After experiencing a problem this spring with my RV refrigerator I realized a permanent thermometer to show the temperature inside the box all the time would be a good way to monitor its performance. As long as it remains cold it's in good working order but rising temperatures will indicate maintenance is needed.

Looking through my parts bin, I found a 3/4 inch 3 digit 7 segment display that is perfect for displaying the temperature. A few resistors to limit the segment led currents, Arduino UNO, and a DS18B20 sensor with pullup resistor is all that is needed to prototype this project.


While the Arduino UNO is great for prototyping, it's a bit large and costly for implementation. Instead I went with an Arduino Pro Mini which also fits nicely on the backside of the 7 segment display. The Pro Mini is 100% compatible with the UNO and even includes a voltage regulator so I can easily power it with the 12v system of the RV.


During assembly I realized a minor modification to the program to put +5v on pin 11 and GND on pin 12 would allow the sensor plugs into three adjacent pins. This also allowed soldering a pullup resistor between the pins on the back of the board. After that I connected the sensor and USB adapter to load the program for a final test on the bench and confirmed it is ready.


To install the display on the refrigerator I cut a square hole in the panel to fit the display as tightly as possible. Usually I secure things like this with hot glue but this time I tried the self setting rubber Sugru and it worked well.


I was able to pull the sensor wire into the box through the same hole used by the thermostat capillary tube. The sensor was a little large for that hole so I soldered it on after pulling the wire. Added a couple layers of heat shrink to make it water proof and then clipped it next to the capillary tube on the aluminum fin.


Following is the thermometer after initial power up on my bench. It's working fine so now I can get the refrigerator put back into my camper. I'm sure this is going to be a good addition because I can easily see if my food is staying cold enough and get an early indication of problems. And, it's fairly light on DC power requiring on average only about 50 milliamps.


Using existing Arduino libraries made this a relatively easy programming task, OneWire.h for polling the temperature sensor and SevenSeg.h to drive the display. The most work was writing code to format the temperature as a string ready to show on the display. Complete source code is below.

My development platform consists of Ubuntu 12.04LTS and the Arduino IDE 1.0.5 both of which are open source and free.

DS18x20_Temperature_Switch.ino

// Simple thermometer, read temperature from ds18b20 sensor and 
// display on 3 digit 7 segment display. 
#include <OneWire.h> 
#include <SevenSeg.h>

#define ONEWIRE_GND 12
#define ONEWIRE_PWR 11
#define ONEWIRE_BUSS 10
OneWire TemperatureSensor(ONEWIRE_BUSS);  // Dallas one wire data buss pin

SevenSeg disp(2, 6, 9, 5, 4, 3, 8);
const int numOfDigits = 3;
int digitPins[numOfDigits] = {15,16,17};

unsigned long time = 0;
#define START 0
#define READY 1
byte state = START;
char tmp[5] = "---";
#define CYCLE_DELAY 4000
#define CONVERSION_DELAY 1000
int initialDelay = -CYCLE_DELAY;

void setup(){
  // ATmega328p power saving
  for(int i=0;i<2;i++) pinMode(i, INPUT_PULLUP); // unused pins
  for(int i=13;i<20;i++) pinMode(i, INPUT_PULLUP); // unused pins
  DIDR0 = 0x3F;  // disable digital inputs on adc
  PRR = 0xCF; // disable peripherals except timer0

  // configure 7 seg display
  disp.setDigitPins(numOfDigits, digitPins);
  disp.setDPPin(7);

  // configure sensor power
  pinMode(ONEWIRE_PWR, OUTPUT);
  pinMode(ONEWIRE_GND, OUTPUT);
  digitalWrite(ONEWIRE_GND, LOW);
  digitalWrite(ONEWIRE_PWR, HIGH);  
}

void loop(){
  int i;
  byte data[12];
  int16_t raw;
  float celsius, fahrenheit;

  if(millis() < time) time = millis();  // reset time when millis() wraps back to zero

  if(((millis() - time) > (CYCLE_DELAY + initialDelay))  && state == START){  // READY ds18b20 temperature READY about every 5 seconds
    TemperatureSensor.reset();       // reset one wire buss
    disp.write(tmp);  // update 7 seg display
    TemperatureSensor.skip();        // select only device
    disp.write(tmp); 
    TemperatureSensor.write(0x44);   // start temperature conversion
    disp.write(tmp);
    state = READY;
  }

  if(((millis() - time) > (CYCLE_DELAY + CONVERSION_DELAY + initialDelay)) && state == READY){   // get data from ds18b20
    TemperatureSensor.reset();
    disp.write(tmp);
    TemperatureSensor.skip();
    disp.write(tmp);
    TemperatureSensor.write(0xBE);   // Read Scratchpad
    disp.write(tmp);
    for ( i = 0; i < 9; i++) {       // 9 bytes
      data[i] = TemperatureSensor.read();
      disp.write(tmp);
    }
    if((OneWire::crc8(data, 8)!=data[8])||(memcmp(data,"\x0\x0\x0\x0\x0\x0\x0\x0\x0",9)==0)){
      strcpy(tmp, "Err");  // no data or comm error
    } 
    else {
      raw = (data[1] << 8) | data[0];  // Convert the data to temperature value
      celsius = (float)raw / 16.0;
      fahrenheit = celsius * 1.8 + 32.0;
      if(fahrenheit > 999) fahrenheit = 999; // format temperature string for 7 seg display
      else if(fahrenheit < -99) fahrenheit = -99;
      if(fahrenheit > 99.9 && fahrenheit < 1000) dtostrf(fahrenheit, 0, 0, tmp); // show decimal or not depending on size
      else if(fahrenheit >= 0 && fahrenheit < 100) dtostrf(fahrenheit, 0, 1, tmp);
      else if(fahrenheit < 0 && fahrenheit > -10) dtostrf(fahrenheit, 0, 1, tmp);
      else if(fahrenheit < -9.9 && fahrenheit > -100) dtostrf(fahrenheit, 0, 0, tmp);
      if((strlen(tmp)<numOfDigits+1)&&(strchr(tmp, '.')!=NULL)){  // right justify
        for(i=sizeof(tmp)-1;i>0;i--){
          tmp[i] = tmp[i-1];
        }
        tmp[0] = ' ';
      }
    }

    state = START;
    time = millis();
    initialDelay = 0;
  }

  disp.write(tmp);  // update 7 seg display
}