Go Down

Topic: Temperature spikes and drops with ds18b20 (Read 4017 times) previous topic - next topic

nrbelk

I have a waterproofed ds18b20 (https://www.adafruit.com/products/381) that I'm using to take the temperature of an aquarium.  I have it wired in parasitic mode and taking the temperature every 5 minutes.

At 5 minute intervals I query for the temp and then upload the variable to a webpage.

For the most part this works except every once in a while, I will get spikes on the webpage of 5000 and then drops to 0  (the normal value is 78).

Where do you think the problem lies? 

Here is the code I'm using to get and send the data.  This isn't all the code I'm running currently but I don't think the rest of it should be affecting it.


Code: [Select]
/*
  DNS and DHCP-based Web client

This sketch connects to a website (http://www.google.com)
using an Arduino Wiznet Ethernet shield.

Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13

created 18 Dec 2009
by David A. Mellis
modified 12 April 2011
by Tom Igoe, based on work by Adrian McEwen

*/

#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {  0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
char serverName[] = "belk.jomber.com";

OneWire ds(2);
float getTemp();

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

void setup() {
  // start the serial library:
  Serial.begin(9600);
  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    while(true);
  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("connecting...");

  // if you get a connection, report back via serial:
 
 
}

void loop()
{
  if (client.connect(serverName, 80)) {
    Serial.println("connected");
    int tempValue = 12;
    // Make a HTTP request:
    client.print("GET /track.php?type=temp&user=nathan&value");
    client.print("=");
    client.print(getTemp());
    client.println(" HTTP/1.1");
    client.println("Host: belk.jomber.com");
    client.println("User-Agent: Arduino");
    client.println("Accept: text/html");
    Serial.println("Sent stuff to server");
    client.println();
  }
  else {
    // kf you didn't get a connection to the server:
    Serial.println("connection failed");
  }
  // if there are incoming bytes available
  // from the server, read them and print them:
  //if (client.available()) {
    //char c = client.read();
    //Serial.print(c);
   while(client.connected())
  {
    // stay in this loop until the server closes the connection
    // that is the signal it is finished sending
    //Serial.print("Printing Available Data to Serial");
    while(client.available()) {
      char c = client.read();
      Serial.write(c);
    }
  }
// }

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();

    // do nothing forevermore:
  }
  delay(300000);
}

float getTemp()
{
byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
 
  if ( !ds.search(addr)) {
   // Serial.println("No more addresses.");
   // Serial.println();
    ds.reset_search();
    delay(250);
//   return;
  }
 
//  Serial.print("ROM =");
//  for( i = 0; i < 8; i++) {
//    Serial.write(' ');
//    Serial.print(addr[i], HEX);
//  }

  //if (OneWire::crc8(addr, 7) != addr[7]) {
  //    Serial.println("CRC is not valid!");
  //    return;
// }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return NULL;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
 
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
 
  present = ds.reset();
  ds.select(addr);   
  ds.write(0xBE);         // Read Scratchpad

// Serial.print("  Data = ");
// Serial.print(present,HEX);
// Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
//   Serial.print(data[i], HEX);
//   Serial.print(" ");
  }
  //Serial.print(" CRC=");
  //Serial.print(OneWire::crc8(data, 8), HEX);
// Serial.println();

  // convert the data to actual temperature

  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
    // default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  return fahrenheit;
  //Serial.print("  Temperature = ");
  //Serial.print(celsius);
  //Serial.print(" Celsius, ");
  //Serial.print(fahrenheit);
  //Serial.println(" Fahrenheit");
}

MarkT

I've never had a problem with DS18B20's with fixed power (over 920,000 readings).  So its likely to be either a parasite power issue or noise on the cable - how long and how well screened is the cable?  Does it run near mains wiring?
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

nrbelk

Thanks for the heads up.  I will look into that.  I don't know what you mean by "power near main" though.

Here is a graph that might give an idea of what is happening better than I can explain it.  The time is over the course of 2 days.  The usual temperature is 78, hence the long straight lines on the graph.

http://belk.jomber.com/view.php?user=nathan

Thanks for the help with this.

robtillaart


you can do
1) recognize the spike, and reread the value until a reasonable value comes  up
Code: [Select]
do
{
  T = getTemperature();
} while (abs(T-prevT)> 10);
prevT = T;


2) use a low pass filter
Code: [Select]

T = 0.85 * T + 0.15 * getTemperature();

The new read value will only count for 15% of the read value. Spikes will be tampered, but the effect is that T smoothes a bit, becomes "slower"in following the real temp

3) a mix
Code: [Select]

T = getTemperature();
if (abs(T-prevT) > 10) T = 0.85 * prevT + 0.15 * T;
prevT = T;


hope this helps

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

MarkT

#4
Apr 04, 2012, 07:30 pm Last Edit: Apr 04, 2012, 07:39 pm by MarkT Reason: 1
Mains wiring will carry big spikes from fluorescent light fittings, relays switching - run your sensor cable parallel to such wiring and you may pick up such interference especially if you have paid attention to shielding.

One way to test is to run the temperature sensing loop constantly and seeing if failure rates are related to where cabling runs.

If the errors are gross it is easy to filter, however if they are sometimes small perhaps you should always read the temperature twice and reject if different by more than a few LSBs.

One of the problems with parasite power is that you can't have decoupling on the sensor so noise pickup can be troublesome - I don't think its really worth it just to replace a 3 wire cable with a 2 wire cable...

[oh and I forgot to ask: what value pull-up resistor are you using?]
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

el_supremo

Those errors wouldn't get through if you were checking the CRC (*).
You need to fix the code so that if the CRC fails, you read the temperature again. And, just in case, put a limit of, say, 3 consecutive CRC errors in which case return the temperature as zero and give up this time through.
Code: [Select]

  if(OneWire::crc8(data, 9)) {
    // CRC failed. Handle the error
  }
  // CRC is OK.


Pete
* assuming that the error is from the sensor. If the error occurs elsewhere, the CRC check won't help. But it won't hurt and it should be there anyway.
Don't send me technical questions via Private Message.

el_supremo

Replace your getTemp function with this.
Code: [Select]

const unsigned char t_mask[4] = {0x7, 0x3, 0x1, 0x0};
float getTemp()
{
  byte i;
  // This isn't actually used
  byte present = 0;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  // Limit the number of consecutive failures
  int loop_count = 3;

  while(loop_count--) {
    if ( !ds.search(addr)) {

      ds.reset_search();
      delay(250);
      continue;
    }

    if (OneWire::crc8(addr, 8)) {
      //    Serial.println("CRC of address is not valid!");
      continue;
    }
    Serial.println();

    // the first ROM byte indicates which chip
    // We know this is a DS18B20 so don't bother checking it


    ds.reset();
    ds.select(addr);
    ds.write(0x44,1);         // start conversion, with parasite power on at the end

    // The spec says 750mS is worst case. I've been using 800. 1000 is overkill
    delay(1000);
    // we might do a ds.depower() here, but the reset will take care of it.

    present = ds.reset();
    ds.select(addr);   
    ds.write(0xBE);         // Read Scratchpad

    for ( i = 0; i < 9; i++) {           // we need 9 bytes
      data[i] = ds.read();
    }

    // Make sure the CRC of the data is OK
    if(OneWire::crc8(data, 9)) {
      // report CRC failure
      // Serial.println("CRC of data failed");
      // and try again
      continue;
    }

    // convert the data to actual temperature

    unsigned int raw = (data[1] << 8) | data[0];
    cfg = (data[4] & 0x60) >> 5;
    raw &= ~t_mask[cfg];
    celsius = (float)raw / 16.0;
    fahrenheit = celsius * 1.8 + 32.0;
    return fahrenheit;
  }
  // return an obviously incorrect temperature to indicate that three consecutive
  // reads of the temperature failed.
  // For an aquarium this temperature would be a disaster :-)
  // If the outside temperature is being measured then return, say, -60 or +180
  return 0;
}



If the search for the device or either of the CRCs fails, it reads the temperature again and returns zero if it fails after three retries.
I have also changed the code so that it removes some unnecessary tests - you know you've got a DS18B20 so there's no need for the code to make sure. If you add more sensors or change the sensor type, you'll have to add the test back in.
I've also added my fix for a bug in the original version. The code only worked if you were reading with the default 12-bit precision. Any other precision would return incorrect results. I haven't got a DS18S20 so I haven't tested the original code to see if it works with other than the default precision.

Pete
Don't send me technical questions via Private Message.

nrbelk

Thank you all so much for your responses.  I will try some of these solutions when I get the chance.  Thank you all again!

Go Up