Using the updated Arduino 1-Wire library code presented here, you can eliminate the need for an external pull-up resistor for typical small networks of DS18B20 temperature sensors. This should also work with any AVR processor and other types of 1-Wire devices as well. You can download the updated 1-Wire library here…
The mythical “required” external pull-up
If you’ve ever used the ubiquitous (and amazingly useful!) DS18B20 family of 1-Wire temperature sensors, you’ve almost certainly used a 4.7K ohm pull-up resistor as well. Every one of the seemingly endless Arduino DS18B20 tutorials on the web starts with some version of the line “You will not be able to do anything with this senor until you go out and procure yourself a 4.7K ohm resistor”. AdaFruit is even generous enough to include one of these resistors with every DS18B20-based temperature sensor they sell (be it bare, waterproof, or hi-temp) because they know you are going to need it.
I am here to tell you that everything is about to change. If you were banking on your stockpiles of 4.7K ohm resistors to be the one reliable store of value in these uncertain times, you need to rethink your long-term asset preservation strategy because the decade-long run of stable demand for this part is about to plummet. Yes – it is now possible to connect DS18B20 sensors without any external pull-up resistor at all!
Outrageous claims demand outrageous proof, so let’s start with a brief demo that proves beyond a shadow of a doubt that this is not just a cockamamie theory, but cold hard fact…
Understanding the pull-up
In the time-tested tradition, the 4.7K ohm pull-up resistor is always connected between the power and the bus and holds the bus high unless either the master or one of the salves is actively pulling it down. This is handy because multiple devices can pull the bus low at the same time and nothing bad happens. There is a neat protocol that everyone follows that lets them carry on orderly communication on this shared wire.
It turns out that the processor in the Arduino has built-in internal pull-ups on the I/O pins that can be enabled via software. These pull-ups can be very handily because they let you, say, connect a switch between a pin and ground. The pull-up will hold the pin at high unless the switch connects it to ground. These internal pull-ups are specified in the Data Sheet as 20K-50K ohms…
While this is a lot more resistance than the 4.7K ohms specified for the 1-Wire pull-ups, we can still make it work for many cases. There real limiting factor here is time – the time it takes for the bus to come back up to a high level after one of the devices is finished pulling it to low.
Without going into too much detail, here is what it looks like when a 1-Wire master reads a bit from a slave (also from the DS18B20 data sheet)…
The master will pull the bus low to tell the slave it is ready to read a bit, and then it will let go of the bus to see what happens. If the slave wants to send a 0, it will signal so by pulling the bus low. If the master sees that the bus is STILL low even after it has let go of it, then it knows that the slave is sending a 0 bit. If the slave wants to send a 1 bit, then it does nothing and just lets the bus get pulled back up to high when the master lets go. The master looks at the bus after it has let go, and if it has returned to high then it knows that the slave sent a 1. Cool, right?
In the case of the slave sending a 0 bit, the pull-up has no effect during the actual bit transmission. We go from the state of the master actively pulling low, to the state of both the master and the slave actively pulling low, to the state of just the slave pulling low. The bus is actively low throughout the entire bit and would be so even with no pull-up at all.
In the case of the 1 bit, we need the pull-up to pull the bus high after the master lets go. This is the moment that the red arrow is pointing to above. The stripped area underneath the arrow is time period where the bus is being pulled from low to high by the pull-up. This does not happen instantly because the pull-up resistor, the devices on the bus, and the wires connecting them all form an RC circuit. The bigger the resistance and the capacitance, the longer it will take for the line to get back up to a high level.
The spec says that in the case of a 0 bit, the slave will pull the bus low within 1us of the master pulling it low, and will hold the bus low for at least 15us after. To be able to tell if the slave was sending a 1 or 0 bit, the master looks at the bus during this time to see if it stayed low (the slave was pulling it low to send a 0 bit), or it returned to high (the slave sent a 1 bit by not doing anything and just letting the bus get pulled back up to high after the master let go).
These protocol constraints limit the size of the pull-up resistor and the capacitance of the system. If it takes the bus longer than 15us to return to high after the beginning of this sequence, then the master will see the bus as low and falsely think that the slave was sending a 0 bit. Since the spec also says that the master must hold the bus low for at least 1us to start the whole process, so we are left with 14us for the pull-up to pull the bus up from low to high.
The yellow trace at the bottom is a trigger I setup to to show when things happen. The trigger goes high when the master lets go of the bus, and then goes low when the master reads the bus to see if a “0” or “1” bit is present. The blue trace is the bus and you can see a nice steep RC curve where the bus rises after the master lets go. It just makes it back up to 5 volts by the time the master reads it.
So, can the internal 50K Ohm (worst case) internal pull-up do the job? Well, it all depends on the resistance and capacitance of the devices on the bus and the wires connecting them. It turns out that this is also a limiting factor when using an external 4.7K pull-up, it is just that the smaller pull-up resistor will give you more headroom for higher resistance and capacitance in the network.
There are very helpful documents that give heuristics to determine how many devices, how much wire, and what configurations will work on normal 1-wire networks with external resistors. All these same hints apply to networks driven by internal 50K Ohm pull-ups, you just get less of everything.
For everything to work, our pull-up must be able to pull the bus back up to a “1” within 14us. But what is the minim voltage for the master to see a “1” bit?
Since we are using a digital input pin of our AVR to sense the bus, and since we are running with a supply voltage (Vcc) of 5 volts, the datasheet gives the answer here…
We need at least about 2.6 volts on the I/O pin for it to be seen as a “1”.
Can it work?
It turns out that this is just a classic RC circuit that looks like this….
The larger C1 is, the longer it will take to charge though the 50K ohm pull-up resistor. We need to find the largest value of C1 that will get the bus up to 2.6 volts in less than 14us.
We can use the formula..
Vc = V * (1- e^(-t / R*C))
…to figure out that we can just make it under the wire if C1 is 400pF. Any larger and the bus might be lower than our 2.6 volt threshold at the end of the 14us (you can check my work here).
400pF is not a lot of capacitance. Can it possibly work?
There are 3 main sources of capacitance in 1-wire networks – the nodes (our DS18B20’s), the wires, and the input pin on the master.
The data sheet for the DS18B20 tells us that the data pin has a maximum capacitance of 25pF…
A typical capacitance for twisted pair wire is on the order of 1pF per inch.
The capacitance of an input pin of a microcontroller like our AVR is typically on the order of 20pF.
Because all of these capacitances are in parallel, we can just add them together to find the total capacitance.
So, as long as stay within the parameters of this formula…
( sensor_count * 25pF ) + ( total_length_of_wire * 1pF/inch ) + 20 pF < 400pF
…then it might just work!
But does it actually work in real life?
It really works. 1 meter of wire with 4 sensors works. 10 meters of wire with 1 sensor works. 3 meters of wire with 2 sensors works. Try it.
My hunch is that the internal pull-up will be good enough for the vast majority of people using the DS18B20 sensors. The new library is looks the same as the old one to the programmer and will work with or without an external pull-up, so you can use it for all your projects and hopefully save the resistor most of the time.
If you need dozens of sensors dangling from dozens of meters of wires to monitor the core of your breeder reactor for partial melt, then you can splurge the extra $0.02 for a potentially superfluous external pull-up without hurting my feelings.
Note that in cases where the internal pull-up is not strong enough, you will know it because you will not be able to communicate with the sensors, at which point you can simply add an external resistor and it automatically start working (assuming that the external pull-up is strong enough for your network).
It turns out to be almost trivially easy to enable the internal pull-up in on an AVR digital I/O pin in software. It is also very easy to add the required code to the existing 1-wire library. You can see the changes needed to the original 1-Wire code here…
The changes are slight – 2 lines added and 2 removed.
The basic idea is to turn on the internal pull-ups anytime the bus is not being actively driven (either high or low) by the master. This even saves a bit of power (about 10 trillion electron-volts per reset pulse!) in the case when the master is actively driving the bus low because it is no longer fighting against the external pull-up.
The resulting new code is backwards compatible with the old code, and will work with or without external pull-up resistors.
While I had the code open, I also added a new function that lets you tell the difference between a shorted bus and a bus that just has no slaves on it. The function is called busFail() and could be useful in debugging failed networks.
You can find the latest Version of the library here…
- Download, install, and run version 1.6.4 or higher of the Arduino IDE.
- Download the DallasTempurature library as a ZIP and install it by choosing “Sketch->Include Library->Add .ZIP Library…”
- Download the OneWireNoResistor library as a ZIP and install it by choosing “Sketch->Include Library->Add .ZIP Library…”
- Open the example program by choosing “File->Examples->OneWire No Resistor->NoPullupTester”.
- Connect a temperature sensor(s) to your Arduino as shown above.
Q: How can there be enough current going though the 50K Ohm internal pull-up to power parasite devices on the bus?
A: The DS18B20 chips are spec-ed to pull 5uA when idle (IDQ) . This means a voltage drop of only 0.25 volts per slave device. Since we are starting at 5 volts, and the DS18B20 chip is spec-ed to run down to 3 volts (VDD), then we should wost case be able to get up to 8 salves on the bus before the voltage drop across the internal pull-up is the limiting factor.
Q: How about the 1.5mA per slave needed during a temperature conversion or EEPROM write?
A: Despite the dire admonishment on the wiki, the 1-Wire library actively drives the bus high during these power hungry events if there are devices are running on parasite power. This means that the pull-up resistors (internal or external) are irrelevant in these cases. A digital I/O pin is spec-ed to be able to drive 20mA, so you should be able to do a simultaneous temperature conversion with up to a dozen parasite-powered sensors on the bus. In practice, I’ve done even more and it worked fine so the sensors are probably drawing less than the spec-ed 1.5mA maximum current.
Q: Help! I need to add more slaves/wire to my network and I am just not willing to fork over my hard earned money to buy that external resistor unless it is 100% necessary!
A: We still have some tricks up our sleeves to get even more headroom out of our networks. Our limiting factor above was that the “1” bit did not have time to rise all the way up the level where it could be register as a “1” on the digital input pin (about 2.6 volts). Even though there is not enough voltage for a digital I/O pin to see it as a “1” we can clearly see it is rising just by looking at it. If we were to switch over to an analog input pin, well then we could detect rises as small as micro-volts! This would give us much, much more headroom on both internally and externally pulled-up networks. And, it just turns out that the way our AVR chip samples analog pins works out perfectly for this task. There is way too much info to shove in this FAQ answer- but maybe I’ll write a follow-up article with working code if there is enough interest.
Q: Wait, I understand everything about the internal pull-up, but how did you get the the sensor to work directly plugged into the Arduino header?
A: Easy – I just added the following lines of code to the setup() function…
// This lines just make it so you can plug a DS18B20 directly into // digitial pins 8-10. digitalWrite( 8 , LOW ); pinMode( 8 , OUTPUT ); digitalWrite( 10 , LOW ); pinMode( 10 , OUTPUT );
These two pins are now driven low and effectively ground, so both the Vcc and the ground of the sensor are now connected to ground even if you plug it in upside down (remember that Vcc can go to ground and not power because we are using parasitic power off the data pin in the middle).
Q: Why the frick don’t you just buy a frickin’ $0.02 resistor you frickin’ cheapskate!
A: I actually have boxes of 4.7K resistors sitting on my shelf. That is not what this is about. I ended up working on this because I was writing instructions for a DS18B20 temperature logger and got stuck on the part about hooking up the pull-up. I looked around the web, and everyone said the external resistor was “required” with no explanation.I bet there are lots and lots of people who have excitedly sat down to build a project with a brand new DS18B20 sensor that they picked up on the way home, only to loose all their momentum when they found out that they need an extra part that they didn’t have. Hopefully now future generations of DS18B20 builders will avoid this frustration.
I am also obsessed with eliminating hardware with clever software wherever possible. Hardware is mater and is therefore you have to manufacture, move, and dispose of each and every hardware part used. None of these these problems apply to software – write it once and and it can be efficiently used infinitely many times in infinitely many places by infinitely many people. And when you dispose of an old piece of code, you have more room than when you started rather than less. We will never run out of room for code landfills. I’d guess that clever software could eliminate about 20% if the matter in most technological objects. Think about a world where every phone, computer, and car was 20% less stuff. Think about the effort required to manufacture all that needless mass, and to transport it around the world, and then to dump it. I am happy to trade some of my time to get rid of the need for a widely used $0.02 resistor!