DS18B20 temp sensor works and then it doesn't

I’ve bought all my Arduino gear from Core Electronics, but have been assembling a weather station by Cactus.io, see here:

Arduino Weather Station Project - Overview (cactus.io)

My attraction to this project was because it worked with the RG11 Rain Sensor.

The project uses two sensors capable of sensing temperature, the DS18B20 and the BME280; but the temperature reading of the BME280 is ignored in favor of that of the DS18B20. I can monitor the readings of the weather station using either a USB connection to the local computer or an Ethernet connection from any computer on my home LAN. When I do this, I get the same result from the DS18B20: It reads the temperature correctly from the DS18B20 for a while, and then the reading drops to zero. If I power down the Arduino board for a while and then restart it, the DS18B20 again reads correctly for a while and then drops to zero.

So I am trying to figure out what is wrong. Is the DS18B20 faulty and should I order another? Is the Arduino board faulty? Is my Sketch faulty? I have certainly put in a pull-up resistor of 4.7k ohms between the 5 volt supply of the Arduino board and the 1 wire signal line to D9. And when the DS18B20 works at first it gives a very accurate reading.

As for the Sketch, I copied it from the web page of the project and I copy it below. Everything else seems to work fine. So I hope someone can give me some pointers. (Oh, there is an artifact in the readings where some numbers are preceded by an “A” with a squiggly line on top.)

Greg

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

#include "TimerOne.h"
#include <math.h>

#include "cactus_io_DS18B20.h"
#include "cactus_io_BME280_I2C.h"

#define Bucket_Size 0.01 // bucket size to trigger tip count
#define RG11_Pin 3 // digital pin RG11 connected to
#define TX_Pin 8 // used to indicate web data tx
#define DS18B20_Pin 9 // DS18B20 Signal pin on digital 9

#define WindSensor_Pin (2) // digital pin for wind speed sensor
#define WindVane_Pin (A2) // analog pin for wind direction sensor
#define VaneOffset 0 // define the offset for caclulating wind direction

volatile unsigned long tipCount; // rain bucket tip counter used in interrupt routine
volatile unsigned long contactTime; // timer to manage any rain contact bounce in interrupt routine

volatile unsigned int timerCount; // used to count ticks for 2.5sec timer count
volatile unsigned long rotations; // cup rotation counter for wind speed calcs
volatile unsigned long contactBounceTime; // timer to avoid contact bounce in wind speed sensor

long lastTipcount; // keep track of bucket tips
float totalRainfall; // total amount of rainfall detected

volatile float windSpeed;
int vaneValue; // raw analog value from wind vane
int vaneDirection; // translated 0 - 360 wind direction
int calDirection; // calibrated direction after offset applied
int lastDirValue; // last recorded direction value

float minTemp; // keep track of minimum recorded temp
float maxTemp; // keep track of maximum recorded temp

// Create DS18B20, BME280 object
DS18B20 ds(DS18B20_Pin); // on digital pin 9
BME280_I2C bme; // I2C using address 0x77

// Here we setup the web server. We are using a static ip address and a mac address
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 45);
EthernetServer server(80); // create a server listing on 192.168.1.45 port 80

void setup() {

// setup rain sensor values
lastTipcount = 0;
tipCount = 0;
totalRainfall = 0;

// setup anemometer values
lastDirValue = 0;
rotations = 0;

// setup timer values
timerCount = 0;

ds.readSensor();
minTemp = ds.getTemperature_C();
maxTemp = ds.getTemperature_C();

// disable the SD card by switching pin 4 High
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);

// start the Ethernet connection and server
Ethernet.begin(mac, ip);
server.begin();

if (!bme.begin()) {
// Serial.println("Could not find BME280 sensor, check wiring!");
while (1);
}

pinMode(TX_Pin, OUTPUT);
pinMode(RG11_Pin, INPUT);
pinMode(WindSensor_Pin, INPUT);
attachInterrupt(digitalPinToInterrupt(RG11_Pin), isr_rg, FALLING);
attachInterrupt(digitalPinToInterrupt(WindSensor_Pin), isr_rotation, FALLING);

// setup the timer for 0.5 second
Timer1.initialize(500000);
Timer1.attachInterrupt(isr_timer);

sei();// Enable Interrupts
}

void loop() {

ds.readSensor();
bme.readSensor();

// update min and max temp values
if(ds.getTemperature_C() < minTemp) {
minTemp = ds.getTemperature_C();
}
if(ds.getTemperature_C() > maxTemp) {
maxTemp = ds.getTemperature_C();
}

// update rainfall total if required
if(tipCount != lastTipcount) {
cli(); // disable interrupts
lastTipcount = tipCount;
totalRainfall = tipCount * Bucket_Size;
sei(); // enable interrupts
}

// listen for incoming clients
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
digitalWrite(TX_Pin,HIGH);
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // connection closed completion of response
client.println("Refresh: 10"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html><body>");
digitalWrite(TX_Pin,HIGH); // Turn the TX LED on
client.print("<span style=\"font-size: 26px\";><br>  Temperature is ");
client.print(ds.getTemperature_C());
client.println(" °C<br>");
client.print("%<br>  Humidity is ");
client.print(bme.getHumidity());
client.println(" %<br>");
client.print("%<br>  Pressure is ");
client.print(bme.getPressure_MB());
client.println(" mb%<br>");
client.print("%<br>  Wind Speed is ");
client.print(windSpeed);
client.println(" mph<br>");
getWindDirection();
client.print("%<br>  Direction is ");
client.print(calDirection);
client.println(" °<br>");
client.print("%<br>  Rainfall is ");
client.print(totalRainfall);
client.println(" mm<br>");
client.print("%<br>  Minimum Temp ");
client.print(minTemp);
client.println(" °C<br>");
client.print("%<br>  Maximum Temp ");
client.print(maxTemp);
client.println(" °C</span>");
client.println("</body></html>");
digitalWrite(TX_Pin,LOW); // Turn the TX LED off
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
}

// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}

// Interrupt handler routine for timer interrupt
void isr_timer() {

timerCount++;

if(timerCount == 5) {
// convert to mp/h using the formula V=P(2.25/T)
// V = P(2.25/2.5) = P * 0.9
windSpeed = rotations * 0.9;
rotations = 0;
timerCount = 0;
}
}

// Interrupt handler routine that is triggered when the rg-11 detects rain
void isr_rg() {

if((millis() - contactTime) > 15 ) { // debounce of sensor signal
tipCount++;
totalRainfall = tipCount * Bucket_Size;
contactTime = millis();
}
}

// Interrupt handler routine to increment the rotation count for wind speed
void isr_rotation() {

if((millis() - contactBounceTime) > 15 ) { // debounce the switch contact
rotations++;
contactBounceTime = millis();
}
}

// Get Wind Direction
void getWindDirection() {

vaneValue = analogRead(WindVane_Pin);
vaneDirection = map(vaneValue, 0, 1023, 0, 360);
calDirection = vaneDirection + VaneOffset;

if(calDirection > 360)
calDirection = calDirection - 360;

if(calDirection > 360)
calDirection = calDirection - 360;
}
1 Like

Hi Greg,

Interesting, I wonder whether it’s being caused by the chip not being an actual Maxim 18B20 in the thermometer. Can you please send us a screenshot of the serial output when running this with your DS18B20 on pin 7 for One-Wire? Thanks.

Hi Bryce,

Thanks for the reply.

I have attached the screenshot of the serial monitor. The one-wire of the DS18B20 was attached to D7 instead of D9, as you suggested. The output seems the same, with zero for the DS18B20. I purchased the DS18B20 from Core Electronics in the last week, so if it is counterfeit, you might have some problems.

Greg

Hi Greg,

Yes, we’re currently looking into the stock of DS18B20 that we’ve got and will be running returns and refunds processes case by case depending on the situation. Can you try with this script I’ve put below for you, there’s a couple in the repo and this should output the number of deviations your chip has from a genuine Maxim18b20 (which should be 0).

/*
 * Copyright Chris Petrich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 *   
 *   File:    discover_fake_DS18B20.ino
 *   Author:  Chris Petrich
 *   Version: 22 Oct 2019 
 *   
 *   Source:  https://github.com/cpetrich/counterfeit_DS18B20/
 *   Documentation:  https://github.com/cpetrich/counterfeit_DS18B20/
 *   
 * This demonstration script performs authenticity tests on DS18B20 sensors by
 * evaluating ROM code and Scratchpad Register. It uses documented ROM commands 
 * F0h and 55h and Function Commands 44h, 4Eh and BEh, only.
 * It does not test the power-up state and it does not write to or test the EEPROM.
 * It is INTENDED for EDUCATIONAL PURPOSES, only.
 * There may be circumstances under which the sketch permanently damages one-wire 
 * sensors in obvious or non-obvious ways.
 * (I don't think it does that to authentic Maxim sensors, but I won't guarantee
 * anything. See licence text for details.)
 * 
 * 
 * This script is written for Arduino. Wiring:
 * 
 * CPU Vcc         -------o------ DS18B20 Vcc
 *                        |
 *                       [R]  <- choose resistor appropriate for supply voltage and current that the microcontroller is able to sink.
 *                        |
 * CPU pin_onewire -------o------ DS18B20 data
 * 
 * CPU GND         -------------- DS18B20 GND
 * 
 */

// Tested with OneWire Version 2.3
// https://github.com/PaulStoffregen/OneWire
#include "OneWire.h"


#define pin_onewire 7
#define pin_LED 13

#define Comm Serial


OneWire *ds;

void print_hex(uint8_t value) {
  if (value < 16) Comm.write('0');
  Comm.print(value, HEX);
}

void print_array(uint8_t *data, int n, char sep = ',') {  
  int idx;
  for (idx=0; idx<n; idx++) {
    print_hex(data[idx]);
    if (idx != n-1)
      Comm.write(sep);
  }
}

bool read_scratchpad(uint8_t *addr, uint8_t *buff9) {
  ds->reset();
  ds->select(addr);
  ds->write(0xBE); // read scratchpad
  int idx;
  for (idx=0; idx<9; idx++)
    buff9[idx] = ds->read();
  return 0 == OneWire::crc8(buff9, 9);
}

void setup() {
  Comm.begin(115200);
  Comm.println();  
  
  digitalWrite(pin_LED, HIGH);
  pinMode(pin_LED, OUTPUT);  

  ds = new OneWire(pin_onewire);
  
  {
    // output file name without leading path
    char *file = __FILE__;
    int i;
    for (i = strlen(file); i > 0; i--)
      if ((file[i] == '\\') || (file[i] == '/')) {
        i++;
        break;  
      }    
    Comm.print(F("\n--- # "));
    Comm.println(&file[i]);
  }
  digitalWrite(pin_LED, LOW);
  Comm.println(F("This sketch will test DS18B20 sensors attached to"));
  Comm.print(F("  pin "));
  Comm.print(pin_onewire, DEC);
  Comm.println(F(" for differences with Maxim Integrated-produced DS18B20"));
  Comm.println(F("  using only functionality documented in the datasheet and in"));
  Comm.println(F("  Maxim Application Note AN4377."));  
  Comm.println();
}

void loop() {
  // ROM address of current sensor
  uint8_t addr[8];  
  // buffers for scratchpad register
  uint8_t buffer0[9];
  uint8_t buffer1[9];
  uint8_t buffer2[9];
  uint8_t buffer3[9];
  // flag to indicate if validation
  //  should be repeated at a different
  //  sensor temperature
  bool t_ok;

  ds->reset_search();
  while (ds->search(addr)) {
    int fake_flags = 0;
    
    print_array(addr, 8, '-');
    if (0 != OneWire::crc8(addr, 8)) {
      // some fake sensors can have their ROM overwritten with
      // arbitrary nonsense, so we don't expect anything good
      // if the ROM doesn't check out
      fake_flags += 1;
      Comm.print(F(" (CRC Error -> Error.)"));
    }

    if ((addr[6] != 0) || (addr[5] != 0) || (addr[0] != 0x28)) {
      fake_flags += 1;
      Comm.print(F(": ROM does not follow expected pattern 28-xx-xx-xx-xx-00-00-crc. Error."));
    } else {
      Comm.print(F(": ROM ok."));
    }    
    Comm.println();
    
    if (!read_scratchpad(addr, buffer0)) read_scratchpad(addr, buffer0);
    
    Comm.print(F("  Scratchpad Register: "));
    print_array(buffer0, 9, '/');
    if (0 != OneWire::crc8(buffer0, 9)) {
      // Unlikely that a sensor will mess up the CRC of the scratchpad.
      // --> Assume we're dealing with a bad connection rather than a bad 
      //     sensor, dump data, and move on to next sensor.
      Comm.println(F("  CRC Error. Check connections or replace sensor."));
      continue;      
    }
    Comm.println();

    // Check content of user EEPROM. Since the EEPROM may have been programmed by the user earlier
    // we do not use this as a test. Rather, we dump this as info.
    Comm.print(F("  Info only: Scratchpad bytes 2,3,4 ("));
    print_array(buffer0+2,3,'/');
    Comm.print(F("): "));
    if ((buffer0[2] != 0x4b) || (buffer0[3] != 0x46) || (buffer0[4] != 0x7f))
      Comm.println(F(" not Maxim default values 4B/46/7F."));
    else
      Comm.println(F(" Maxim default values."));


    Comm.print(F("  Scratchpad byte 5 (0x"));
    print_hex(buffer0[5]);
    Comm.print(F("): "));
    if (buffer0[5] != 0xff) {
      fake_flags += 1;
      Comm.println(F(" should have been 0xFF according to datasheet. Error."));
    } else {
      Comm.println(F(" ok."));
    }

    Comm.print(F("  Scratchpad byte 6 (0x"));
    print_hex(buffer0[6]);
    Comm.print(F("): "));
    if ( ((buffer0[6] == 0x00) || (buffer0[6] > 0x10)) || // totall wrong value
         ( ((buffer0[0] != 0x50) || (buffer0[1] != 0x05)) && ((buffer0[0] != 0xff) || (buffer0[1] != 0x07)) && // check for valid conversion...
           (((buffer0[0] + buffer0[6]) & 0x0f) != 0x00) ) ) { //...before assessing DS18S20 compatibility.
      fake_flags += 1;
      Comm.println(" unexpected value. Error.");
    } else
      Comm.println(" ok.");
    
    Comm.print(F("  Scratchpad byte 7 (0x"));
    print_hex(buffer0[7]);
    Comm.print(F("): "));
    if (buffer0[7] != 0x10) {
      fake_flags += 1;
      Comm.println(F(" should have been 0x10 according to datasheet. Error."));
    } else {
      Comm.println(F(" ok."));
    }

    // set the resolution to 10 bit and modify alarm registers    
    ds->reset();
    ds->select(addr);
    ds->write(0x4E); // write scratchpad. MUST be followed by 3 bytes as per datasheet.
    ds->write(buffer0[2] ^ 0xff);
    ds->write(buffer0[3] ^ 0xff);
    ds->write(0x3F);
    ds->reset();

    if (!read_scratchpad(addr, buffer1)) read_scratchpad(addr, buffer1);
    
    Comm.print(F("  0x4E modifies alarm registers: "));
    if ((buffer1[2] != (buffer0[2] ^ 0xff)) || (buffer1[3] != (buffer0[3] ^ 0xff))) {
      fake_flags += 1;
      Comm.print(F(" cannot modify content as expected (want: "));
      print_hex(buffer0[2] ^ 0xff);
      Comm.write('/');
      print_hex(buffer0[3] ^ 0xff);
      Comm.print(F(", got: "));
      print_array(buffer1+2, 2, '/');
      Comm.println(F("). Error."));      
    } else
      Comm.println(F(" ok."));

    Comm.print(F("  0x4E accepts 10 bit resolution: "));
    if (buffer1[4] != 0x3f) {
      fake_flags += 1;
      Comm.print(F(" rejected (want: 0x3F, got: "));
      print_hex(buffer1[4]);
      Comm.println(F("). Error."));
    } else
      Comm.println(F(" ok."));

    Comm.print(F("  0x4E preserves reserved bytes: "));
    if ((buffer1[5] != buffer0[5]) || (buffer1[6] != buffer0[6]) || (buffer1[7] != buffer0[7])) {
      fake_flags += 1;
      Comm.print(F(" no, got: "));
      print_array(buffer1+5, 3, '/');
      Comm.println(F(". Error."));
    } else
      Comm.println(F(" ok."));    

    // set the resolution to 12 bit
    ds->reset();
    ds->select(addr);
    ds->write(0x4E); // write scratchpad. MUST be followed by 3 bytes as per datasheet.
    ds->write(buffer0[2]);
    ds->write(buffer0[3]);
    ds->write(0x7f);
    ds->reset();

    if (!read_scratchpad(addr, buffer2)) read_scratchpad(addr, buffer2);
    
    Comm.print(F("  0x4E accepts 12 bit resolution: "));
    if (buffer2[4] != 0x7f) {
      fake_flags += 1;
      Comm.print(F(" rejected (expected: 0x7F, got: "));
      print_hex(buffer2[4]);
      Comm.println(F("). Error."));
    } else
      Comm.println(F(" ok."));

    Comm.print(F("  0x4E preserves reserved bytes: "));
    if ((buffer2[5] != buffer1[5]) || (buffer2[6] != buffer1[6]) || (buffer2[7] != buffer1[7])) {
      fake_flags += 1;
      Comm.print(F(" no, got: "));
      print_array(buffer2+5, 3, '/');
      Comm.println(F(". Error."));
    } else
      Comm.println(F(" ok."));

    Comm.print("  Checking byte 6 upon temperature change: ");
    if (( ((buffer2[0] == 0x50) && (buffer2[1] == 0x05)) || ((buffer2[0] == 0xff) && (buffer2[1] == 0x07)) ||
         ((buffer2[6] == 0x0c) && (((buffer2[0] + buffer2[6]) & 0x0f) == 0x00)) ) &&
         ((buffer2[6] >= 0x00) && (buffer2[6] <= 0x10)) ){
      // byte 6 checked out as correct in the initial test but the test ambiguous.
      //   we need to check if byte 6 is consistent after temperature conversion
            
      // We'll do a few temperature conversions in a row.
      // Usually, the temperature rises slightly if we do back-to-back
      //   conversions.
      int count = 5;
      do {
        count -- ;
        if (count < 0)
          break;
        // perform temperature conversion
        ds->reset();
        ds->select(addr);
        ds->write(0x44);
        delay(750);
        
        if (!read_scratchpad(addr, buffer3)) read_scratchpad(addr, buffer3);
        
      } while ( ((buffer3[0] == 0x50) && (buffer3[1] == 0x05)) || ((buffer3[0] == 0xff) && (buffer3[1] == 0x07)) ||
                ((buffer3[6] == 0x0c) && (((buffer3[0] + buffer3[6]) & 0x0f) == 0x00)) );
      if (count < 0) {
        Comm.println(F(" Inconclusive. Please change sensor temperature and repeat."));
        t_ok = false;
      } else {
        t_ok = true;
        if ((buffer3[6] != 0x0c) && (((buffer3[0] + buffer3[6]) & 0x0f) == 0x00)) {
          Comm.println(F(" ok."));
        } else {
          fake_flags += 1;
          Comm.print(F(" Temperature LSB = 0x"));
          print_hex(buffer3[0]);
          Comm.print(F(" but byte 6 = 0x"));
          print_hex(buffer3[6]);
          Comm.println(F(". Error."));
        }
      }
    } else {
      Comm.println(F("not necessary. Skipped."));
      t_ok = true;
    }

    Comm.print(F("  --> "));
    if (!t_ok) {
      Comm.print(F("DS18S20 counterfeit test not completed, otherwise sensor"));
    } else 
      Comm.print(F("Sensor"));
      
    if (fake_flags == 0) {
      Comm.println(F(" responded like a genuie Maxim."));
      Comm.println(F("      Not tested: EEPROM, Parasite Power, and undocumented commands."));
    } else {
      Comm.print(F(" appears to be counterfeit based on "));
      Comm.print(fake_flags, DEC);
      Comm.println(F(" deviations."));
      if (fake_flags == 1) {
        Comm.println(F("  The number of deviations is unexpectedly small."));
        Comm.println(F("  Please see https://github.com/cpetrich/counterfeit_DS18B20/"));
        Comm.println(F("  to help interpret the result."));
      }
    }
    Comm.println();
  } // done with all sensors
  Comm.println(F("------------------------------------------------"));
  delay(1000);
  digitalWrite(pin_LED, digitalRead(pin_LED) == HIGH ? LOW : HIGH);
}
1 Like

Hi Greg,

Is the issue reproducible if you isolate and run only the code for the DS18B20? You’re running a bunch of interrupts which can wreak havoc with comms.

Alternatively, If you disable interrupts just before reading from the ds18b20, then reenable immediately after reading do you still have this issue?

Also, re counterfeit sensors; depends on the supplier. I doubt eg. Sparkfun or Adafruit Ds18b20’s are counterfeit. If Dfrobot or waveshare it’s a greater possibility.

1 Like

Hi Bryce,

I tried compiling this sketch and it came up with a message “Error compiling for Arduino Uno”. This made me wonder whether I have the wrong board in the first place. My board is the Freetronics EtherTen, but I could not find that board listed in the Board names of the Arduino IDE.

Greg

1 Like

The Uno is the right selection. The Etherten is just an Uno with some parts permanently connected to it. Checkout the quickstart guide:

Did you install the OneWire library? If not, that’ll be the cause. Also, I like to leave verbose output on - those errors tend to be very descriptive and helpful sorting the issue.

Any luck disabling interrupts? Fyi, a ds18b20 needs a complete command or it will hang just like you’re describing. If an interrupt fires off in the middle of reading from the sensor that’ll do exactly what you’re describing.

1 Like

Hi Greg,

Might not help you, but the following is how I used a DS18B20 purchased from Core Electronics.
Temperature / clock / data logger using the DS18B20 battery powered. Has been running for 1.5 years.
The Arduino wakes every 30 seconds reads the temperature sensor and saves the reading on a SD card. The battery gets recharged about every 3 months. So the device only gets reset every 3 months.
4k7 ohm pullup on data line.

#include <OneWire.h>
#include <DallasTemperature.h>

#define TEMP            9               // DS18B20 Temperature Sensor digital pin 9

OneWire oneWire(TEMP);                  // Setup a oneWire instance
DallasTemperature sensors(&oneWire);    // Setup a sensors instance

sensors.requestTemperaturesByIndex(0);  // reads the first device
temp_1 = sensors.getTempCByIndex(0);    // get the temperature

All the best hope you get it working. The libraries do all the hard stuff making use much easier.

Regards
Jim

3 Likes

Hi all, the DS18B20 is now working and I thank you for all your posts in support of my problem. The support is outstanding.

3 Likes

That’s excellent, glad to hear that you’ve got it set up. If there’s anything else that we can do for you please let us know. Have a Merry Christmas!