1-Wire, DS18B20... How do I search the bus?

I had the same looping problem with the search function. The looping seems to depend on the individual addresses for some reason, not on the number of sensors. Anyway, I rewrote the search algorithm.

I had my new version working successfully with up to 12 sensors, and I haven't seen it loop yet. This was with Arduino 11. For the record, I was using a 4K7 resistor.

Here is my code for the search routine:

//
// Perform a search. If this function returns a '1' then it has
// enumerated the next device and you may retrieve the ROM from the
// OneWire::address variable. If there are no devices, no further
// devices, or something horrible happens in the middle of the
// enumeration then a 0 is returned.  If a new device is found then
// its address is copied to newAddr.  Use OneWire::reset_search() to
// start over.
//
// searchJunction is the branch on the current address at which the
//    current search is to continue -
//    prior to this point the path follows the previous address
//    after this point, at each junction, 0 is selected
// lastJunction is the last unexplored junction on the present path.
//    the next search will continue from here
// 
uint8_t OneWire::search(uint8_t *newAddr)
{
  uint8_t i = 0;
  char lastJunction = -1;

  if ( searchExhausted) return 0;
  searchExhausted = 1; // the search is exhausted unless we find another junction

  if ( !reset()) return 0;
  write( 0xf0, 0);    // send search ROM

  for( i = 0; i < 64; i++) {
    uint8_t a = read_bit( );
    uint8_t nota = read_bit( );
    uint8_t ibyte = i/8;
    uint8_t ibit = 1<<(i&7);

    if ( a && nota) return 0;  // I don't think this should happen, this means nothing responded, but maybe if
    // something vanishes during the search it will come up.
    if ( !a && !nota) {                    // if at a junction, then
      if ( i < searchJunction) {           // if before search junction
        if ( address[ ibyte]&ibit)  a = 1; // follow previous address
        else a = 0;
      }
      else { 
        if ( i == searchJunction) a = 1;// at the search junction take the new branch.
        else a = 0;                     // past the search junction, on a new branch so select 0
      }
      if (!a) {             // if 0 is chosen
        lastJunction = i;   // set last junction
        searchExhausted = 0; // continue the search
      }
    }

    if (a) address[ibyte] |= ibit; // set address bit
    else address [ibyte] &= ~ibit;
    write_bit( a);
  } // end of address bit loop
  searchJunction = lastJunction;                    // set searchJunction for the next call to Search

  for ( i = 0; i < 8; i++) newAddr[i] = address[i]; // copy found address into output address
  return 1;  
}
#endif

And I put this in the header after Tom Pollard's note to clarify the version:

Modified to work with larger numbers of devices - avoids loop.
Tested in Arduino 11 alpha with 12 sensors.
26 Sept 2008 -- Robin James

If you are not sure how to update your library, do it this way:
first make a copy of your existing library, and put it in a safe place - i.e. not in "libraries". This is insurance!
Then in OneWire.cpp, delete the search routine and replace it with the one above. Also add the new header comment.
Filnally, delete the file OneWire.o. This will be regenerated when you restart Arduino.

I would have posted the full files here, but the size limitation does not permit this. A would have updated the library too, but I'm a newbie, and I'm clueless about how to go about it.

A couple of final comments:
As others have said, it is best to hard code the addresses instead of using the search routine every time. This is not as difficult as you may think.
Secondly, it is better and more robust to use more than one bus. This way, if something goes wrong you don't lose all of your sensors at once.
Would someone care to post some examples?

Couple of thoughts - Crunch, are you sure that you are rebuilding the OneWire object every time, and not just reusing the previous object file? Make sure you delete the OneWire.o file before clicking verify.

Also, I tried using a 47K resistor on mine, it wouldn't work at all like that. I've found that 1K is about right.

And finally, I found that periodic interrupts play havoc with the search function. If you are using a periodic ISR, try executing a noInterrupts() call just before calling the search to turn off interrupts, and then execute an interrupts() call afterwards to turn them back on.

After reading the advice above and seeing that OTHER people have been able to make this concept work, I figured it HAD to be something I was doing wrong rather than a coding error in the library. (I couldnt see how I would be the first person to find the error)

I got frustrated with this whole problem this morning and did the following:

  1. Re-Downloaded the latest Arduino engine
  2. Re-Downloaded the OneWire library
  3. Went to the original source example for the onewire library and used that
  4. Ripped out all the hook up wires on the proto-board and replaced them
  5. Replaced the resister. Double checked it to be a 4.7k resister.

NOW I have 8 DS18S20's on the bus and they are reading properly. So I dont know which of the above made it work but it just does now.

I am going to work out how to get more room on my prototyping board and get more sensors onto the bus. Hopefully it reads all 15 of them. Then I can start playing with the actual production sensors.
I appreciate the time you guys have done on this!.

I'm sure everyone has already thought of this, but it's worth double checking to make sure the Vdd pin of each chip is attached to ground if you are trying to run the DS18S20 parts in the parasitic power mode. As you're also no doubt aware, you can't run very many in that mode unless you pay careful attention to the data sheet specs and allow enough time for the chips to recharge after a lengthy session of running off their internal supply.

I have created a new version of OneWire which fixes long-standing bugs in the I/O code. Robin James's search function was merged, and I made several small optimizations, mostly to eliminate unnecessary RAM usage. After a few emails with the authors I could contact, I'm calling this version 2.0, to distinguish it from the many other buggy copies online.

http://www.pjrc.com/teensy/td_libs_OneWire.html

If you've used any prior versions of OneWire, even with Robin James's search function merged, you almost certainly experienced occasional CRC errors or devices not responding to the search, no matter how good your wiring and pullup resistor are. Version 2.0 fixes these long-standing problems.

Can you update the playground, please?

Can you update the playground, please?

Done.

Awesome! Thanks!

Paul,

Using the latest version of your 1wire library, I'm still experiencing strange behaviour. Having up to 4 sensors of type DS18B20 in one bus (non-parasite mode), everything works as expected.

Adding the 5th sensor of the same type, the "search" function either cannot see (enumerate) any of the sensor, or only some of them. I changed the test bed several times, to eliminate possibly faulty wirings, and yes - depending upon the current test environment the number of sensors seen by "search" varies. But in a specific test bed (wires remaining at the same place etc., simply playing around with the sensors itself) the behaviour of search is reproducable.

I tried some permutations of the sensors I have at hand (all of the same type DS18B20), but the overall behaviour remains the same. 1-4 sensors are fine, adding the 5th led to the erratic behaviour.

Any ideas? Or someone else out there with similar issues?

Thanks

Joe

What's the physical wiring of your sensors look like? There are some app-notes from Maxim/Dallas that discuss overall network topology and the limitations you'll run into with wire length, as well as some mitigation strategies. I don't have the app-note location handy, however. I ran into similar problems as you when wiring sensors up in my house and ended up having to run two separate networks. That may be the case for you, as well...

All sensors are still on a breadboard, but thanks for the hint. I had a quick look at the app note you were refering to (#148) at
Mixed-signal and digital signal processing ICs | Analog Devices)
but nothing for short range networks like the one I have on my breadboard.

It's no big deal for the application I have in mind: sensors will be ~2m away from the bus master, and I can even spend more arduino pins, to have more than one 1wire bus, and reduce the number of DS18B20 on one bus.

But I'd like to understand, why :wink:

Joe

One other person (who's local to me) reported a problem. I'm going to meet with him on Monday and try to learn more. It might be related.

If you could get the 64 bit ID numbers from your 5 chips and post them here, it would really help.

I really do want to get to the bottom of this. To be realistic, right now I'm working on finishing up a highly urgent project, so it might be a week or two until I can really focus on OneWire again. But I definitely will. Hopefully before then I can find a way to reproduce the problem here.

Paul,

Your dedication is much appreciated! (I've got a job myself during weekdays...)

:28:49:DA:3C:02:00:00:0B:
:28:05:C7:3C:02:00:00:93:
:28:53:E4:3C:02:00:00:B9:
:28:FF:D8:3C:02:00:00:3D:
:28:1B:E8:3C:02:00:00:4C:

Let me know how I can support you any further.

Joe

guys

Let me start by saying hats off to you on the work you have done. I am obviously new, so this question may be answered elsewhere... I have two DS18b20's working it reports the addresses. But thinking of this as a comercial application... How do I tell the system which sensore is located where? I have a memory chip setup, so once I figure it out I can store the address... But how do I make it so a user can set it up and insure the temp I am reading is located where I think it is...

I think I found a bug on read_bit(void) function of current library.

This library is works good on most of devices but my device doesn't happy with this library.

It doesn't submit correct values on ReadROM command properly. Searched a lot and find something:

According to Maxim-IC,
Read Time Slot (Read Zero):
Defines A to BR time as 9 micro second, than A to CR time as 18 us.
So, we needed to wait at least 18uS after submitting 0 for reading the line value.
But current Arduino code is slightly different, Sample time is (3+9) 11uS after submitting 0 that generates the bug on my device.

Current OneWire Lib 2.0:

uint8_t OneWire::read_bit(void){
          uint8_t mask=bitmask;
      volatile uint8_t *reg asm("r30") = baseReg;
      uint8_t r;

      cli();
      DIRECT_MODE_OUTPUT(reg, mask);
      DIRECT_WRITE_LOW(reg, mask);
      delayMicroseconds(3);
      DIRECT_MODE_INPUT(reg, mask);      // let pin float, pull up will raise
      delayMicroseconds(9);
      r = DIRECT_READ(reg, mask);
      sei();
      delayMicroseconds(53);
      return r;
        }

Why do we needed to sample this bit at 11us? Spec allows us up to 30uS.

Changing it via

uint8_t OneWire::read_bit(void){
uint8_t mask=bitmask;
        volatile uint8_t *reg asm("r30") = baseReg;
        uint8_t r;
        
        cli();
        DIRECT_MODE_OUTPUT(reg, mask);
        DIRECT_WRITE_LOW(reg, mask);
        delayMicroseconds(9);//Increased to make it compatible to Spec
        DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise
        delayMicroseconds(15);//This increased too to iterates sampling time to 21uS after zero pulse. More compatibility.
        r = DIRECT_READ(reg, mask);
        sei();
        delayMicroseconds(41); //decreased 12uS(6uS+6uS).  So no speed degrade with this code.
        return r;
        }

Makes the job well. I don't know exactly if this bug might be corrupting search algorithm too at beginning pages.

Regards,
Erdem U. Altinyurt

The DS18B20 is probably the most common 1-wire device. Here is the datasheet:

http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf

Please look at Figure 16 "Recommended Master Read 1 Timing" on page 17 of 22. The diagram shows the line is supposed to be sampled shortly before 15 us after the initial low pulse began. The timings in the current code are 3 us low pulse, plus 9 us. That's 12 us, which allows the small amount of extra overhead from code execution to be as much as 3 us (if the CPU is running at 8 MHz or slower).

The page you linked suggests 18 us from the initial pulse until sampling (or "A to CR"). That's 3 us beyond the maximum recommended by the DS18B20. Also, your code below generates a 9 us pulse plus a 15 us delay, which is a total of 24 us from "A to CR", or 6 us more than the page you linked, and 9 us more than suggested for the DS18B20. That doesn't seem like such a good approach....

I am open to increasing the timings, but since the DS18B20 and similar chips are so very common, I really think it's a not good to increase the total "A to CR" more than 14 us (it's currently 12), since it's supposed to be less than 15 for those incredibly common parts.

Does it work on your device if you use 4 us pulse and 10 us delay before reading?

Can you capture the actual signal on your device with a digital scope?

Thanks for quick response. Yes you have right. I don't have exact 1 wire specification pdf. So searched over internet and found that it's recommended to use 18uS. Don't looked the spec of DS18B20. Read Valid Time(RVT) is 15uS for this chips. But they also don't recognize first 1uS I think. :wink:
Since I have Deumilanove, got 16Mhz cpu. So that 3 us overhead not count for my setup.

I am really happy with maxims 18uS spec. Since sample time is valid up to 30uS depending on wiki, I thought it will be more compatible by adding 6us to second delay. I can still read DS18B20 in this configuration but yes, second delay could corrupt some low quality DS chips.

I played with delays and found that Also 15uS (3+12) is working ok but anything lower than 15us generates corrupted read. Also checked my (4) all DS18B20's output is valid readable up to 26uS in both Powered or Parasite Powered configurations in reality. I guess 11uS margin (15uS - 26uS) is little much for the safety of reading this chips.
Sample time is required to > 15uS at Wikipedia. Wiki says device needed to hold bit 30uS also. It's not compatible with DS18B20's spec sheet since VRT is 15us. I think there is small anarchy over specs of OneWire protocol. 18uS better for proper protocol that in my mind or for Maxims :). 15uS (3+12) will not crash any code than I believe. But it's RVT Max and could be ~+1uS higher than DS18B20's RVT. I think RVT + 2~3uS will be in safety line.


Also chip cannot detect 1.st uS since voltage is started to dropping but not grounded, so line is in set state. And line will not be in set state after RVT immediately, due both voltage start rising + safety margins of the chips. It's clearly seen on old DS1820's spec (without B) Figure 18 (Line start rising up after ~17.th uS). Spec sheet says sample time required < 15uS also but It will work properly in even 17.uS sample time depending graph. Slow CPU's will iterate to 18uS but read operation will be in successful since DS18B20's grounds line for >25us in reality.
Also new spec sheets defines RVT 15uS but there is 3uS gap between of line starting to drop down to zero and RVT start. (I counted the pixels ;D )

So 12uS is too low and overkill for the 1Wire protocol, 14uS is matches for DS18B20's but better to move 15us for more compatibility with the protocol. I think it's sweet spot. I advice to using 16uS. It will be look more compatible with 1Wire specs. Thats all works with DS18B20's.

I don't have scope. But will try to make one from my arduino one day... :wink:

Regards,
Erdem

What value of pullup resistor are you using?

Perhaps you could try using a lower value resistor? Or if you have a long cable, try using a lower capacitance cable.

I guess 11uS margin (15uS - 26uS) is little much for the safety of reading this chips.

Did you test at the temperature extremes, or only at room temperature? The wide timing margin is needed to accommodate changes in the timing inside the 1-wire chips, which lack accurate clocks and can change substantially over temperature. They can also change from batch to batch.

Ignoring the the 15 us maximum would not be a good practice. While it worked with the 4 devices you tested, at whatever temperature you tested, going beyond the spec is just asking for trouble.

My guess is your pullup resistor is a bit weak. Using a stronger (lower value) resistor will likely pull the line up faster, enabling you to use the DS18B20 timing.

I used 4.7k standard pull-up for 1Wire device bus. I don't tested under chilled temperatures. But 26uS gives correct reads >127 C degrees with DS18B20's without single bit of error with both powered & parasite power connections... I don't wait different results under -55 C degrees. It might change it's characteristic on different batches +- 1 or 2 uS. But there is ~10uS margin surely will protects us. So +1uS will not arise trouble.

At my situation, I think it's required to use higher value of resistor since I wanted to longer "0" pulses, not shorter one, that slow downs line pull-up. I can turn around this problem on my local setup via using altered OneWire library code or electronically by using new resister. But I just wanted to make Arduino's OneWire library more compatible with OneWire spec. At least, please think about adjusting reading time to 14uS that matches with DS18B20 master sample time.
Regards