Author Topic: Improved/optimized DS18B20/1Wire read  (Read 56664 times)

ColinR

  • Full Member
  • ***
  • Posts: 176
Improved/optimized DS18B20/1Wire read
« on: October 05, 2014, 04:50:15 PM »
Hello all,

I've been picking apart my code to do some optimization, and an obvious target was the read of 1Wire devices, the DS18B20 in particular. Currently, I use the OneWire library, without the DallasTemperature library, which I didn't much care for.

What all 1Wire read routines have in common is that they issue the Convert command prior to reading a measured value. For the DS18B20, this is the Convert T command, 0x44. Depending on the resolution, the conversion takes from 75-750ms, per the datasheet.

There are three main issues I had with the implementation using OneWire as in the examples:
  • There was no provision for setting the resolution. While this is available in the DallasTemperature library, it just didn't make sense to me to rewrite all of my lean code around this library to get this one feature.
  • Wait times are hardcoded. Per the datasheet, it is possible to check with the device for status after issuing the Convert T command to know exactly when the temperature conversion is complete. In most implementations, however, this wait time is hard-coded, and typically VERY generously. For example, in a read operation at 12 bits that actually takes <600ms, the hardcoded wait time is 1000ms. This is a 40% savings, ladies and gentlemen. Futhermore, most test code does not even adjust the delay time for different resolution. This is just insane. A ready that should take 60-75ms will implement a wait of 1000ms (?!)
  • Everything is done synchronously. Nothing else can happen while we are waiting for this conversion This is just stupid and lazy coding, and the real reason I dug into this. If anything else in your program depends on timing, forget using the default 1Wire read code

So to easily work out the first two items, I created this example (code box 1 below). Run it after substituting your 1Wire pin, and you'll get something like this for reading 9, 10, 11, and 12 bit resolutions:

dsaddress:2838FF400500004E,
Conversion took: 76 ms
Raw Scratchpad Data:
50 1 0 0 1F FF 10 10 21
Temp (C): 21.00

dsaddress:2838FF400500004E,
Conversion took: 150 ms
Raw Scratchpad Data:
50 1 0 0 3F FF 10 10 51
Temp (C): 21.00

dsaddress:2838FF400500004E,
Conversion took: 298 ms
Raw Scratchpad Data:
50 1 0 0 5F FF 10 10 C1
Temp (C): 21.00

dsaddress:2838FF400500004E,
Conversion took: 596 ms
Raw Scratchpad Data:
4F 1 0 0 7F FF 1 10 37
Temp (C): 20.94


A HUGE improvement over stock wait times. With the accuracy of the DS18B20, it really doesn't make much sense to use 12 bits, so 10 bits saves me loads in timing. I left in a bunch of original code that's been commented out so you can see how it was done previously.

Now, we can also separate conversion commands and reading the data back. If you have multiple sensors, you actually want to use the Skip ROM and Convert T commands to tell all devices on the bus to convert simultaneously, but that's another topic. In the meantime while conversion is taking place, we can do other stuff.

So we separate our read DS18B20 routine into find, set resolution, send conversion command, and finally read temperature. Between the last two, we just continually check in our loop to see that data is ready, and when it is, we read it. Pretty simple, but SUPER EFFECTIVE. You can see we lose a little due to overhead, but still, plenty fast, and we can do other stuff at the same time.

Enjoy!
C

Temp (C): 21.50
Elapsed time (ms): 99
Temp (C): 21.50
Elapsed time (ms): 170
Temp (C): 21.62
Elapsed time (ms): 321
Temp (C): 21.56
Elapsed time (ms): 618



Code box 1:
Code: [Select]
#include <OneWire.h>

#define LED 9
#define SERIAL_BAUD   115200

void setup(void) {
  Serial.begin(SERIAL_BAUD);
}

void loop(void) {
  for (int i=9;i<13;i++){
    handleOWIO(6,i);
    Serial.println();
  }
 
  delay(1000); 
  Blink(LED,3);
}

void handleOWIO(byte pin, byte resolution) {
  int owpin = pin;
 
  // Device identifier
  byte dsaddr[8];
  char dscharaddr[16];
  OneWire myds(owpin);
  getfirstdsadd(myds,dsaddr);
 
  Serial.print(F("dsaddress:"));
  int j;
  for (j=0;j<8;j++) {
    if (dsaddr[j] < 16) {
      Serial.print('0');
    }
    Serial.print(dsaddr[j], HEX);
  }
  sprintf(dscharaddr,"%02x%02x%02x%02x%02x%02x%02x%02x",dsaddr[0],dsaddr[1],dsaddr[2],dsaddr[3],dsaddr[4],dsaddr[5],dsaddr[6],dsaddr[7]);
  Serial.println(',');
 
  // Data

  Serial.println(getdstemp(myds, dsaddr, resolution));
 
} // run OW sequence

void getfirstdsadd(OneWire myds, byte firstadd[]){
  byte i;
  byte present = 0;
  byte addr[8];
  float celsius, fahrenheit;
 
  int length = 8;
 
  //Serial.print("Looking for 1-Wire devices...\n\r");
  while(myds.search(addr)) {
    //Serial.print("\n\rFound \'1-Wire\' device with address:\n\r");
    for( i = 0; i < 8; i++) {
      firstadd[i]=addr[i];
      //Serial.print("0x");
      if (addr[i] < 16) {
        //Serial.print('0');
      }
      //Serial.print(addr[i], HEX);
      if (i < 7) {
        //Serial.print(", ");
      }
    }
    if ( OneWire::crc8( addr, 7) != addr[7]) {
        //Serial.print("CRC is not valid!\n");
        return;
    }
     // the first ROM byte indicates which chip

    //Serial.print("\n\raddress:");
    //Serial.print(addr[0]);
   
    return;
  }
}


float getdstemp(OneWire myds, byte addr[8], byte resolution) {
  byte present = 0;
  int i;
  byte data[12];
  byte type_s;
  float celsius;
  float fahrenheit;
 
  switch (addr[0]) {
    case 0x10:
      //Serial.println(F("  Chip = DS18S20"));  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      //Serial.println(F("  Chip = DS18B20"));
      type_s = 0;
      break;
    case 0x22:
      //Serial.println(F("  Chip = DS1822"));
      type_s = 0;
      break;
    default:
      Serial.println(F("Device is not a DS18x20 family device."));
  }
 
  // Get byte for desired resolution
  byte resbyte = 0x1F;
  if (resolution == 12){
    resbyte = 0x7F;
  }
  else if (resolution == 11) {
    resbyte = 0x5F;
  }
  else if (resolution == 10) {
    resbyte = 0x3F;
  }
 
  // Set configuration
  myds.reset();
  myds.select(addr);
  myds.write(0x4E);         // Write scratchpad
  myds.write(0);            // TL
  myds.write(0);            // TH
  myds.write(resbyte);         // Configuration Register
 
  myds.write(0x48);         // Copy Scratchpad
 
 
  myds.reset();
  myds.select(addr);
 
  long starttime = millis();
  myds.write(0x44,1);         // start conversion, with parasite power on at the end
  while (!myds.read()) {
    // do nothing
  }
  Serial.print("Conversion took: ");
  Serial.print(millis() - starttime);
  Serial.println(" ms");

  //delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
 
  present = myds.reset();
  myds.select(addr);   
  myds.write(0xBE);         // Read Scratchpad

  //Serial.print("  Data = ");
  //Serial.print(present,HEX);
  Serial.println("Raw Scratchpad Data: ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = myds.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;
  Serial.print("Temp (C): ");
  //Serial.println(celsius);
  return celsius;
}

void Blink(byte PIN, int DELAY_MS)
{
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN,HIGH);
  delay(DELAY_MS);
  digitalWrite(PIN,LOW);
}

Code Block 2:
Code: [Select]
#include <OneWire.h>

#define LED 9
#define SERIAL_BAUD   115200

OneWire myds(6);
byte readstage;
byte resolution;
unsigned long starttime;
unsigned long elapsedtime;
byte dsaddr[8];

void setup(void) {
  Serial.begin(SERIAL_BAUD);
  readstage = 0;
  resolution = 12;
}

void loop(void) {
 
  if (readstage == 0){
      getfirstdsadd(myds,dsaddr);
      dssetresolution(myds,dsaddr,resolution);
      starttime = millis();
      dsconvertcommand(myds,dsaddr);
      readstage++;
  }
  else {
      if (myds.read()) {
        Serial.println(dsreadtemp(myds,dsaddr, resolution));
       
        Serial.print("Elapsed time (ms): ");
        elapsedtime = millis() - starttime;
        Serial.println(elapsedtime);
        readstage=0;
        if (resolution == 12){
          resolution = 9;
        }
        else {
          resolution ++;
        }
      }
  }
 
  Blink(LED,5);
}

void getfirstdsadd(OneWire myds, byte firstadd[]){
  byte i;
  byte present = 0;
  byte addr[8];
  float celsius, fahrenheit;
 
  int length = 8;
 
  //Serial.print("Looking for 1-Wire devices...\n\r");
  while(myds.search(addr)) {
    //Serial.print("\n\rFound \'1-Wire\' device with address:\n\r");
    for( i = 0; i < 8; i++) {
      firstadd[i]=addr[i];
      //Serial.print("0x");
      if (addr[i] < 16) {
//        Serial.print('0');
      }
//      Serial.print(addr[i], HEX);
      if (i < 7) {
        //Serial.print(", ");
      }
    }
    if ( OneWire::crc8( addr, 7) != addr[7]) {
        Serial.print("CRC is not valid!\n");
        return;
    }
     // the first ROM byte indicates which chip

    //Serial.print("\n\raddress:");
    //Serial.print(addr[0]);
   
    return;
  }
}

void dssetresolution(OneWire myds, byte addr[8], byte resolution) {
   
  // Get byte for desired resolution
  byte resbyte = 0x1F;
  if (resolution == 12){
    resbyte = 0x7F;
  }
  else if (resolution == 11) {
    resbyte = 0x5F;
  }
  else if (resolution == 10) {
    resbyte = 0x3F;
  }
 
  // Set configuration
  myds.reset();
  myds.select(addr);
  myds.write(0x4E);         // Write scratchpad
  myds.write(0);            // TL
  myds.write(0);            // TH
  myds.write(resbyte);         // Configuration Register
 
  myds.write(0x48);         // Copy Scratchpad
}

void dsconvertcommand(OneWire myds, byte addr[8]){
  myds.reset();
  myds.select(addr);
  myds.write(0x44,1);         // start conversion, with parasite power on at the end
 
}

float dsreadtemp(OneWire myds, byte addr[8], byte resolution) {
  byte present = 0;
  int i;
  byte data[12];
  byte type_s;
  float celsius;
  float fahrenheit;
 
  switch (addr[0]) {
    case 0x10:
      //Serial.println(F("  Chip = DS18S20"));  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      //Serial.println(F("  Chip = DS18B20"));
      type_s = 0;
      break;
    case 0x22:
      //Serial.println(F("  Chip = DS1822"));
      type_s = 0;
      break;
    default:
      Serial.println(F("Device is not a DS18x20 family device."));
  }
 
  present = myds.reset();
  myds.select(addr);   
  myds.write(0xBE);         // Read Scratchpad

  //Serial.print("  Data = ");
  //Serial.print(present,HEX);
//  Serial.println("Raw Scratchpad Data: ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = myds.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;
  Serial.print("Temp (C): ");
  //Serial.println(celsius);
  return celsius;
}

void Blink(byte PIN, int DELAY_MS)
{
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN,HIGH);
  delay(DELAY_MS);
  digitalWrite(PIN,LOW);
}
« Last Edit: October 05, 2014, 05:08:56 PM by ColinR »
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Improved/optimized DS18B20/1Wire read
« Reply #1 on: October 05, 2014, 10:43:06 PM »
Awesome, very nice work sir. And if you couple that DS temp reading with John's equation for temperature compensation (and lowering the bitrate and bandwidth) you will get a highly accurate temperature compensated transceiver which will yield a multi fold increase in range.
Details here:
https://lowpowerlab.com/forum/index.php/topic,357.0.html

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #2 on: October 06, 2014, 12:04:00 AM »
Rad!

I just can't believe that this code doesn't exist already. Hanging your micro for a full second to read temperature just seems nutso, especially when a huge point of using a micro is that it's fast!

I updated the Arduino site: http://playground.arduino.cc/Learning/OneWire

And added an article on my blog: http://www.cupidcontrols.com/2014/10/moteino-arduino-and-1wire-optimize-your-read-for-speed/

Cheers,
C
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

kiwisincebirth

  • Jr. Member
  • **
  • Posts: 69
Re: Improved/optimized DS18B20/1Wire read
« Reply #3 on: October 06, 2014, 01:12:56 AM »
Thanks for the good work Colin, two questions

1. Would it be possible to initiate a reading, then put the Moteino into a deep sleep for a few seconds (to a few minutes),  wake up and retrieve the reading, transmit over RFM radio, then sleep again. I am thinking of conserving battery power while the measurement is being taken.

2. Do you intend to turn your code into a library? If not do you object to someone else doing it for you?

Thanks.

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #4 on: October 06, 2014, 02:20:43 AM »
Thanks.

Powering down would not work, as the DS18B20 pulls about 1mA during the conversion process. This is the main caveat of issuing a simultaneous conversion for all devices on the network - if you tell 100 devices to convert simultaneously, for example, you can end up with supply issues.

I don't have plans to put this into a library, but have no objections to anybody else doing so. The resolution and write alarm registers are critically important commands that should be accessible, as well as the separation of the convert and read functions. I know of folks who use the two alarm bytes to store data, as they stick around in nonvolatile memory.

Colin
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #5 on: October 06, 2014, 05:12:01 PM »
Ok, here is an interesting wrinkle that is relevant for me:

Unless you retain the ds object and do not issue another reset or select command on the bus, you can not successfully continue to poll for data readiness status. Let me break this down:

When you read on a 1Wire bus, you do the following:
  • Issue a reset pulse to a bus designated by a pin. All devices 'listen up'. According to the data sheet, this does not interrupt a temperature conversion command
  • Issue a select/match command with the ROM.
  • Issue other commands

What I've discovered, however, is the following:
  • If I issue a conversion command and continue to check status (using the ds.read() as mentioned in the first post), I can tell when the conversion is complete.
  • If I reinstantiate the OneWire object, issue a reset and a select/match command and resume checking for data using ds.read(), I immediately get a response that indicates data is ready, when it clearly cannot be (for example, 10ms into a 600ms conversion time)


What this seems to indicate to me is that you can only use the ds.read() to check for data status before issuing another reset and match on the bus. The reason this matters to me is that I have the possibility of multiple OneWire buses, and so don't want to use a single, global OneWire object, or allocate memory space for a whole array of OneWire objects. I would like to be able to reinstantiate the object and keep the scope to within the subfunctions.

For now, the solution appears to be enforcing a resolution-dependent gap in time between convert and read. This of course would not use a delay function, but would use other trickery to notify the read scratchpad function when data are ready. The key point is that the conversion and read are totally independent, in that they each reinstantiate a new OneWire object.

Colin
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #6 on: October 08, 2014, 04:39:48 PM »
Thanks for the good work Colin, two questions

1. Would it be possible to initiate a reading, then put the Moteino into a deep sleep for a few seconds (to a few minutes),  wake up and retrieve the reading, transmit over RFM radio, then sleep again. I am thinking of conserving battery power while the measurement is being taken.

Thanks.

Now that I think more about this, it is certainly possible to sleep the MCU and Radio, as long as the DS18B20 is not parasite-powered. The datasheet says "Zero standby power required", so as soon as it is done with its conversion process, you could certainly wake back up and read it.

By default, this is the way my mote code is set up. It sleeps everything at the end of each loop for some set time. On the first loop, it would run the conversion process, and by the time it ran the loop again, it would be time to read the scratchpad. That should work well!

C
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

tve

  • NewMember
  • *
  • Posts: 14
Re: Improved/optimized DS18B20/1Wire read
« Reply #7 on: October 09, 2014, 02:14:48 AM »
I similarly didn't like the OneWire library for temperatures and derived my own, which uses 10-bit resolution and initiates a conversion on all sensors at the same time and then returns so the arduino loop can proceed. I use a fixed conversion timeout, but I don't think it matters that much if you return control anyway. It sucks if the processor is busy-waiting for 1000ms instead 600ms, but with 10 bits you're talking ~180ms plus/minus a few and there's no busy-wait anyway.
The library is most likely too idiosyncratic for others, but maybe someone else can derive some inspiration (I also looked at a bunch of others to arrive at my code). I noticed that I need to fix the indentation, ooops.
The code is at https://github.com/tve/widuino/tree/master/nodes/OwTemp

scottpenrose

  • NewMember
  • *
  • Posts: 13
Re: Improved/optimized DS18B20/1Wire read
« Reply #8 on: January 05, 2015, 06:16:44 PM »
Great code. Thanks. I wrote a state machine 2 years ago for DS18B20, but never maintained it very well. Better to start again.

There is code out there worth a look at as well - I used for another project DallasTemperature - http://milesburton.com/Dallas_Temperature_Control_Library

It does non blocking reads like this and also allows setResolution etc built in. Has been around for about 5 years.

Although it uses no delays - non blocking, the OneWire both these are dependent on, still use delays. They are small but have caused me problems in the past when doing very accurate time based readings.

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #9 on: January 05, 2015, 06:28:36 PM »
I looked at the DallasTemperature code and mentioned it on my blog where I talk about this topic. I came to the conclusion that it was too much overhead for the tiny extra features I wanted out of it. I didn't realize there were additional delays elsewhere in OneWire though, so I'll take a look at that. It's been satisfactory running it non-blocking as I did in the first post.

I'll probably end up writing my own when I get around to using a bunch of the random 1Wire devices I have around here with micros.

C
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

scottpenrose

  • NewMember
  • *
  • Posts: 13
Re: Improved/optimized DS18B20/1Wire read
« Reply #10 on: January 07, 2015, 08:19:42 PM »
Yes that library is quite large. Flexible, but large. I think I will try your code out as an experiment as it is better fitness for purpose and the 328s are so small (I have been working a lot lately with ARMs with 64K, so back to 2K and 328s has been a reminder of small systems). Thanks again.

Scott

obstler42

  • NewMember
  • *
  • Posts: 12
Re: Improved/optimized DS18B20/1Wire read
« Reply #11 on: January 21, 2015, 03:30:44 PM »
hi,

I'm trying to get your improved code (code1 on top) running, but can't seem to get it to work. It gets the address just fine but is stuck in the while loop forever. Last serial output is:

dsaddress:28FF04B275040020,

and from adding more serial debug output I know for sure that it is stuck in the while loop:

Code: [Select]
	myds.write(0x44, 1);         // start conversion, with parasite power on at the end
while (!myds.read()) {
// do nothing
}

Using the <DallasTemperature.h> lib and the sample code on http://www.hobbytronics.co.uk/ds18b20-arduino works just fine (but inefficient)... do you have any ideas what could go wrong here?

thanks.

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #12 on: January 26, 2015, 11:20:16 PM »
Not sure. Does the other code sample work? You probably want to use it anyway, since it's non-blocking. Let me have a think on that.

C
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

hexium

  • NewMember
  • *
  • Posts: 3
Re: Improved/optimized DS18B20/1Wire read
« Reply #13 on: February 17, 2015, 03:10:24 AM »
Hi and thanks for this great work. I have a simple (possibly noob) question:

- If the "raw" variable is declared as an (unsigned int) type, does this mean we cannot get negative values from this code?

Thanks!

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #14 on: February 17, 2015, 09:52:30 PM »
Good question. The simple answer is no. From page four here in the datasheet, you can see that the temperature is contained in two bytes of twos complement: http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf

Looking at the code here, however, I can see that there is not provision for negative temperatures, and you would indeed get strange results. I know I've written this code somewhere else before, but apparently it didn't make it over here. Oops, just found it. Here you go:

Code: [Select]
float calctemp(byte data[]) {
  float celsius;
 
  unsigned int TReading = (data[1] << 8) + data[0];
  unsigned int SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  celsius = float(TReading)/16;
 
  if (SignBit){
    celsius = celsius * -1;
  }
  return celsius;
}
« Last Edit: February 18, 2015, 02:11:55 AM by ColinR »
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

hexium

  • NewMember
  • *
  • Posts: 3
Re: Improved/optimized DS18B20/1Wire read
« Reply #15 on: February 18, 2015, 07:37:32 AM »
Thank you very much for the prompt and accurate response!

lormic

  • NewMember
  • *
  • Posts: 1
Re: Improved/optimized DS18B20/1Wire read
« Reply #16 on: February 21, 2015, 07:22:02 PM »
Hi, I am new in the Arduino World. I am trying to use your code to read the temperatures of two DS18B20 sensors. Eventually I must read the tenfold of DS18B20 sensors. But for now I only get the temperature of one of the two sensors. The sketch only reads sensor 1 (see image attached). When I pull sensor 1 out of the breadboard the sketch returns the values of sensor 2. What am I doing wrong? (btw my sensors don't have the white wire, mine only have red, black and yellow wire)

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #17 on: February 22, 2015, 12:41:18 AM »
The code as-is only reads the first sensor. I've been meaning to write multi-sensor code and haven't gotten around to it. I just haven't had the need with my microcontrollers. I use a 1Wire bus master on my Pi gateway for multidrop scenarios.

For multiple sensors, you'd declare a OneWire object, and each time you perform the search function, you should get the next ROM. When you want to go back to the first ROM, use the reset command and you may again iterate over them. Here is a good description of the command set of the OneWire library: http://www.pjrc.com/teensy/td_libs_OneWire.html

C
« Last Edit: February 22, 2015, 12:55:48 AM by ColinR »
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

ColinR

  • Full Member
  • ***
  • Posts: 176
Re: Improved/optimized DS18B20/1Wire read
« Reply #18 on: February 22, 2015, 12:57:19 AM »
Oh also, it's not mentioned specifically, but if you have multiple DS18B20s, you can use the Skip command before you issue a T convert to simultaneously issue convert commands to all devices on the bus. Saves loads of time. Just watch current draw.

C
CuPID Controls :: Open Source browser-based sensor and device control
Interfaceinnovations.org/cupidcontrols.html
cupidcontrols.com

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Improved/optimized DS18B20/1Wire read
« Reply #19 on: August 23, 2016, 01:34:00 PM »
I just came across this thread and wanted to contribute my code.  I've been fooling with these sensors for years with an eye towards eventually letting a tiny uC take over temperature measurements for my home brewery.  As others have noted, the Dallas and OneWire libraries are bloated and slow to say the least so I boiled them down to bit banging and delays.  Nice features here include a framework intended to let the uC sleep or do other work while the temperature conversion is happening and elimination of the external pullup resistor.  Also the program size itself is the smallest by far I have ever seen.

NROO means No Resistor and Object Oriented:
https://github.com/cdl1051/DS18B20_NROO

This project only uses 686 bytes of flash so it can fit on even the smallest ATTiny.

Apprentice17

  • NewMember
  • *
  • Posts: 5
Re: Improved/optimized DS18B20/1Wire read
« Reply #20 on: January 07, 2019, 09:14:02 AM »
@Colin R: you mentioned one can iterate over the number of sensors and then reset to go on. Isn't it better to create an array with all the detected sensors and declare them as sensor1 sensor2... instead or searching them again, and again, and again?
At least in case of power consumption I could imagine an sensor-arra could save some energy and might be faster.

But I have no idea how to implement this, yet.

I really like your code, as it frees ressources so I can do multiple things instead of waiting ;)
But as I hav econnected two sensors right now (and later on it might be 3-4 sensors) I wonder how to scale it in a proper way.
Any suggestions?

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Improved/optimized DS18B20/1Wire read
« Reply #21 on: January 07, 2019, 11:21:36 AM »
@Colin R: you mentioned one can iterate over the number of sensors and then reset to go on. Isn't it better to create an array with all the detected sensors and declare them as sensor1 sensor2... instead or searching them again, and again, and again?
At least in case of power consumption I could imagine an sensor-arra could save some energy and might be faster.
I'll have to look at my code, but this is exactly how my DS sensor library works.  It's a wrapper for standard one wire code and the intent is to do the search once in setup, create an array of found sensors, and then, during read, do a 'bulk read' as ColinR suggested and then, as I want to report each result, I simply do a .getTemp(i) where 'i' is the index into the table of found sensors.  You should be able to implement fairly easily with your own wrapper using any OneWire library.

HeneryH

  • Full Member
  • ***
  • Posts: 229
Re: Improved/optimized DS18B20/1Wire read
« Reply #22 on: January 07, 2019, 06:24:35 PM »
I'm using one of the standard Dallas libraries and it works well for me (not time sensitive in my case) except it is uses more power than I'd like.  I had to double up my SuperCaps on the solar unit and even still if I have two cloudy days it will sometimes get too low on power. 

Then since I have a very basic power circuit it never comes back up properly on recharge.  I'd have to add some circuitry to hold the Mote in reset until a threadhold is met on the recharge curve.  Currently I need to manually reset the unit after a low-power event and I had hoped to seal it in a glass box which makes a reset hard.

Sergegsx

  • Jr. Member
  • **
  • Posts: 87
  • Country: es
Re: Improved/optimized DS18B20/1Wire read
« Reply #23 on: January 08, 2019, 01:24:34 AM »
I'm using one of the standard Dallas libraries and it works well for me (not time sensitive in my case) except it is uses more power than I'd like.  I had to double up my SuperCaps on the solar unit and even still if I have two cloudy days it will sometimes get too low on power. 

Then since I have a very basic power circuit it never comes back up properly on recharge.  I'd have to add some circuitry to hold the Mote in reset until a threadhold is met on the recharge curve.  Currently I need to manually reset the unit after a low-power event and I had hoped to seal it in a glass box which makes a reset hard.

Please let us know if you implement this minimum voltage threshold. I am interested as I have the same issue. thank you.

Apprentice17

  • NewMember
  • *
  • Posts: 5
Re: Improved/optimized DS18B20/1Wire read
« Reply #24 on: January 08, 2019, 02:28:11 AM »
@TomWS: Sounds great. Where do I find your lib? And do you have an example on how to implement (two or more sensors)?
How can I ensure I always get the same order for the sensors?
Let's say sensor 1 is CPU, sensor 2 is GPU. I need to make sure the IDs don't switch. Didn't get the criterion on how the searchresult is enumerated.

On ColinR's code I got CPU as Sensor 1. When I connected a second sensor (GPU-Sensor) this was sensor 1 and I couldn't manage to read the CPU sensor anymore.


As I'm working with an rotary encoder to create my menu, I cannot use the original Dallas-lib as I wouldn't be able to read out the input while it's waiting for the results...

//Edit:
User an 1-wire finder:

Looking for 1-Wire devices...
Found '1-Wire' device with address:
0x10, 0x90, 0x51, 0x59, 0x03, 0x08, 0x00, 0xCB
Found '1-Wire' device with address:
0x10, 0x97, 0x1A, 0x59, 0x03, 0x08, 0x00, 0xD7
That's it.

So this are both my sensors. I'm not sure why both of them start with 0x10. Does it declare what kind of device it is? Or how many adresses it has?
So 0x90 and 0x97 might be the first adresses of my sensors?

//Edit2: After reading the datasheet and searching the web, I guess the whole line (8 hex values) is one 64bit address, is it? So the 0x10 might be the beginning of every DS1820?
« Last Edit: January 08, 2019, 05:41:25 AM by Apprentice17 »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Improved/optimized DS18B20/1Wire read
« Reply #25 on: January 08, 2019, 06:17:42 AM »
From the datasheet: https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf

64-BIT Lasered ROM code
Each DS18B20 contains a unique 64–bit code (see Figure 8 ) stored in ROM. The least significant 8 bits of the ROM code contain the DS18B20’s 1-Wire family code: 28h. The next  48  bits  contain  a  unique  serial  number.  The  most  significant 8 bits contain a cyclic redundancy check (CRC) byte  that  is  calculated  from  the  first  56  bits  of  the  ROM  code.  A  detailed  explanation  of  the  CRC  bits  is  provided  in the CRC Generation section. The 64-bit ROM code and associated ROM function control logic allow the DS18B20 to operate as a 1-Wire device using the protocol detailed in the 1-Wire Bus System section.

Not sure why you would be getting 0x10 back for the 1-Wire family code

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Improved/optimized DS18B20/1Wire read
« Reply #26 on: January 08, 2019, 06:29:35 AM »
After doing a bit more digging it seems that the DS18S20's 1-Wire family code is 0x10 while the DS18B20 uses 0x28 as its family code.  Mystery solved.

From https://www.maximintegrated.com/en/app-notes/index.mvp/id/4377 ...
The 8-bit family code in the ROM code is also different for these two devices. The family code for the DS18B20 is 28h; the DS18S20 retains the same family code as the original DS1820, which is 10h.
« Last Edit: January 08, 2019, 06:31:15 AM by ChemE »

Apprentice17

  • NewMember
  • *
  • Posts: 5
Re: Improved/optimized DS18B20/1Wire read
« Reply #27 on: January 08, 2019, 07:35:36 AM »
DS18S20 is what I've ordered. On the case it's labeled DALLAS DS1820. So you seem to prove well ;)
Now I can go on trying to get both sensor values. Maybe TomWS' lib will help me later on...

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Improved/optimized DS18B20/1Wire read
« Reply #28 on: January 08, 2019, 08:03:04 AM »
@TomWS: Sounds great. Where do I find your lib? And do you have an example on how to implement (two or more sensors)?
Attached below.  Note that this is barebones and unsupported.  You're free to use it however you want...
Quote
How can I ensure I always get the same order for the sensors?
Let's say sensor 1 is CPU, sensor 2 is GPU. I need to make sure the IDs don't switch. Didn't get the criterion on how the searchresult is enumerated.
The search protocol will always produce the same results for the same set of sensors as it is based on the bit ordering of the 64 bit serial number.  Once you get a set of results and identify which sensor is 1st, 2nd, etc.  I will not change unless you add another sensor.   However, the serial numbers are constant and, once you know the serial number for a sensor, it may change its position in the table, but will always relate to the same unique sensor.

As ChemE pointed out, the first byte relates to the family or device code of the one wire product.

However, the wrapper supposedly deals with this.  Since I've only used DS18B20s, I can not confirm that it works properly for any other OW type.

Tom
« Last Edit: January 08, 2019, 08:56:05 AM by TomWS »

Apprentice17

  • NewMember
  • *
  • Posts: 5
Re: Improved/optimized DS18B20/1Wire read
« Reply #29 on: January 14, 2019, 03:02:00 AM »
Got ColinR's code converted for using my sensors both:

1.
Read out hex-adresses for sensors using any onewire-scanner-sketch I got my adresses:
0x10, 0x90, 0x51, 0x59, 0x03, 0x08, 0x00, 0xCB
and
0x10, 0x97, 0x1A, 0x59, 0x03, 0x08, 0x00, 0xD7

2.
Converted the strings to decimal values:
0x10 = 16
0x90 = 144
0x51 = 81
0x59 = 89
0x03 = 3
0x08 = 8
0x00 = 0
0xCB = 203


0x10, 0x97, 0x1A, 0x59, 0x03, 0x08, 0x00, 0xD7
0x10 = 16
0x97 = 151
0x1A = 26
0x59 = 89
0x03 = 3
0x08 = 8
0x00 = 0
0xD7 = 215

HEX => DECIMAL
0x10, 0x90, 0x51, 0x59, 0x03, 0x08, 0x00, 0xCB => 16, 144, 81, 89, 3, 8, 0, 203
0x10, 0x97, 0x1A, 0x59, 0x03, 0x08, 0x00, 0xD7 =>16, 151, 26, 89, 3, 8, 0, 215

3.
Now I created the arrays for MY sensors manually:
byte dsaddr[] = {16, 144, 81, 89, 3, 8, 0, 203};
byte dsaddr2[] = {16, 151, 26, 89, 3, 8, 0, 215};

Notice: I filled in the decimal adresses of _my_ sensors (your's might be slightly different...).



4.
Start reading temperatures for both sensors:
dssetresolution(myds,dsaddr,resolution);
dssetresolution(myds,dsaddr2,resolution);


5.
Read temperatures:
lcd.setCursor(0,0);
lcd.print("temp1: ");
lcd.print(dsreadtemp(myds,dsaddr, resolution));

lcd.setCursor(0,1);
lcd.print("temp2: ");
lcd.print(dsreadtemp(myds,dsaddr2, resolution));


@ColinR:
lcd.setCursor(0,3);
lcd.print("thanks 4 your snippet");

@TomWS: Didn't get your code up and running yet, as I just had the idea to convert the adresses manually, Colin's code does the job ;)

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Improved/optimized DS18B20/1Wire read
« Reply #30 on: January 14, 2019, 09:16:16 AM »
For future ref, you could have also done:
Code: [Select]
byte dsaddr[] = {0x10, 0x90, 0x51, 0x59, 0x03, 0x08, 0x00, 0xCB};

Apprentice17

  • NewMember
  • *
  • Posts: 5
Re: Improved/optimized DS18B20/1Wire read
« Reply #31 on: January 15, 2019, 02:28:51 AM »
That's what I tried first, but it didn't work. Couldn't compile it.
Converting the HEX to DEC manually did the trick ;)

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Improved/optimized DS18B20/1Wire read
« Reply #32 on: January 15, 2019, 08:19:05 AM »
That's what I tried first, but it didn't work. Couldn't compile it.
If it didn't compile, you had some other error in your code.  This is a proper 'C' construct.