DS18B20 Waterproof Probe with Arduino: Aquarium Water Temperature Monitor

Cartoon of a silver stainless-steel DS18B20 waterproof probe hanging into a teal aquarium tank on its black cable with red, yellow and black bare tails, beside a dark-blue OLED module showing abstract white pixel blocks and a white breadboard holding a small blue resistor

A waterproof DS18B20 reads water temperature through a single Arduino pin: red to 5 V, black to GND, yellow to D2, and one 4.7 kΞ© resistor from that yellow line up to 5 V. The resistor is not a refinement: on a 1-Wire bus it is the only thing that can pull the line high, so without it the probe never answers at all β€” the bus scan finds nothing.

What do you need to monitor aquarium water temperature with an Arduino?

The DS18B20 waterproof probe is a stainless-steel capsule roughly 6 mm across on a one-metre black lead that ends in three bare stranded tails β€” red, yellow and black. That one metre is the build’s constraint: it reaches from the water to a dry spot beside the tank, no further. The rest of the list gives it a host, a readout, and that one resistor.

Optional β€” a second probe, and a USB cable if you need one

ItemPriceQty
Temperature Sensor DS18B20 Temperature Measurement Probe WaterProof 18B20Temperature Sensor DS18B20 Temperature Measurement Probe WaterProof 18B20DS18B20RM4.95
Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)ATBC100RM4.90

A second DS18B20 shares the same pin and the same single pull-up resistor, which is what you want for water-in versus water-out, or two tanks on one board. The USB A-to-B cable is the lead the Uno's full-size square socket takes β€” add one if you do not already have that shape in your drawer.

Connector shape decides whether these parts meet. The 0.96-inch white OLED arrives with its four-pin male header soldered on and silkscreened GND, VCC, SCL, SDA β€” ground first, worth reading off the board rather than assuming, since its neighbour is 5 V. That header plugs into the 400-point breadboard, and male-to-male jumpers carry each row to the female sockets on the Uno. The probe has no plug at all: its three tails go straight into breadboard holes.

Why does the DS18B20 need a 4.7 kΞ© pull-up resistor?

The DS18B20’s data pin is open drain: inside the probe, DQ connects to a transistor that can pull the line down to ground and to nothing that can push it up. The Arduino’s pin behaves the same way while it is talking. So on a 1-Wire bus every participant is able to create a logic low and none of them can create a logic high β€” the resistor up to 5 V is the entire mechanism by which the line ever rises. Leave it out and the line simply floats β€” no idle high, no reset pulse, no reply.

The datasheet turns that into a deadline. To send a 1, the master pulls the line low, releases it, and the line must be back at a valid high within 15 Β΅s of the start of the time slot. Resistor and bus capacitance form an RC charging curve, so that deadline is a resistance budget. A one-metre probe lead plus a breadboard row is on the order of 100 pF: 4.7 kΞ© Γ— 100 pF is a 0.47 Β΅s time constant, so the line recovers in about a microsecond β€” fifteen times inside the window. Ten kilohms doubles that to roughly two microseconds, which is exactly why one probe on one metre of cable does work on 10 kΞ©. Add probes and metres and the capacitance climbs; near 1.5 nF the 10 kΞ© curve is only two thirds of the way up when the 15 Β΅s deadline passes and bits drop, while 4.7 kΞ© still arrives with margin.

So the purchase answer is one 4.7 kΞ©, 1/4 W resistor for the entire bus β€” one piece, not one per probe, because the probes hang off the same line in parallel. It has no polarity, so orientation does not matter. Out of a mixed pack, 4.7 kΞ© reads yellow-violet-black-brown on five-band blue metal-film parts (470 Γ— 10) and yellow-violet-red on older four-band carbon ones. Two 10 kΞ© in the same pair of rows come to 5 kΞ©.

Cartoon diagram of an open-drain 1-Wire bus: a navy horizontal line with three switches pulling down to ground beneath two silver probes and a blue board edge, and a single blue resistor pulling up to a red supply rail
Everything on a 1-Wire bus can pull the line down. Only the resistor pulls it back up β€” which is why leaving it out kills the bus rather than degrading it.

How do you wire the DS18B20 to an Arduino Uno?

The DS18B20’s tails are fine stranded wire, tinned and splayed open at the ends, while a breadboard hole is built for a solid pin. Roll each tail tight between finger and thumb before it goes near the board β€” one stray strand bridging into the neighbouring row puts 5 V onto the data line. If you own an iron, a dab of solder on the twist is the sturdier fix.

Wire Goes to Why
Probe RED Uno 5V VDD β€” normal power, so the chip never has to steal current from the data line
Probe BLACK Uno GND Ground reference for the bus
Probe YELLOW Uno D2 DQ, the 1-Wire data line β€” any digital pin except 0 and 1 works if you change it in code, since those two carry the Serial Monitor
4.7 kΞ© resistor Yellow row β†’ 5V The pull-up. One for the whole bus, whatever the probe count
OLED GND Uno GND First pin on the header β€” check the silkscreen before plugging in
OLED VCC Uno 5V Display supply, sharing the probe’s rail
OLED SCL Uno A5 IΒ²C clock β€” fixed pins on an Uno, not free choice
OLED SDA Uno A4 IΒ²C data

Feed the board first: one jumper from the Uno’s 5V socket to the breadboard’s red stripe, one from GND to the blue stripe. Three rows of that table want 5 V and the Uno has one socket β€” keep everything on the same half of the board as those two jumpers. One column of five breadboard holes is then the whole bus: the yellow tail, one leg of the 4.7 kΞ© resistor and the jumper to D2 all land there, and the resistor’s other leg reaches the 5 V rail.

Only the steel capsule and its cable are sealed β€” the Uno, breadboard and display are not, so keep them on a dry shelf above the water line and drop the cable in over the rim. Hang the capsule in open water away from the heater’s outflow: parked against a heater it reads the heater, not the tank. Stainless also stains in saltwater β€” inspect the capsule when you clean a marine tank. Everything here is 5 V DC; an aquarium heater is a mains appliance, and nothing on this breadboard should be wired to switch one.

Cartoon comparison of a yellow stranded wire end β€” frayed silver strands touching a neighbouring breadboard hole marked with an orange cross, versus the same wire twisted into a neat point seated in one hole with a teal tick
Left: splayed strands reaching into the next row, which is a short between 5 V and data. Right: the same tail rolled tight, seated in one hole.

Which library does DS18B20 Arduino code need β€” OneWire or DallasTemperature?

The DS18B20 needs two libraries doing different jobs. OneWire by Paul Stoffregen bit-bangs the microsecond timing the bus demands; DallasTemperature by Miles Burton sits on top and turns that into readable commands. Install both from Library Manager, plus the Adafruit SSD1306 and GFX pair our DHT11 air-temperature monitor also uses.

/*
  DS18B20 waterproof probe -> 0.96" OLED water temperature monitor
  Arduino Uno + OneWire + DallasTemperature + Adafruit SSD1306

  ONE 4.7k resistor between the yellow DATA wire and 5V. The whole bus
  needs exactly one, no matter how many probes hang off the pin.
*/
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const uint8_t ONE_WIRE_PIN = 2;   // yellow probe wire lands here
const uint8_t MAX_PROBES   = 2;   // raise this if you add more probes

Adafruit_SSD1306 oled(128, 64, &Wire, -1);
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);

DeviceAddress probe[MAX_PROBES];
uint8_t found = 0;

void printRom(const uint8_t *addr) {
  for (uint8_t i = 0; i < 8; i++) {
    if (addr[i] < 0x10) Serial.print('0');
    Serial.print(addr[i], HEX);
  }
}

void scanBus() {
  sensors.begin();                 // runs the 1-Wire search from scratch
  uint8_t onBus = sensors.getDeviceCount();
  found = (onBus < MAX_PROBES) ? onBus : MAX_PROBES;

  Serial.print(F("Probes on the bus: "));
  Serial.println(onBus);

  for (uint8_t i = 0; i < found; i++) {
    if (sensors.getAddress(probe[i], i)) {
      Serial.print(F("  index "));
      Serial.print(i);
      Serial.print(F("  ROM "));
      printRom(probe[i]);          // note this down, label that cable
      Serial.println();
      sensors.setResolution(probe[i], 12);   // 0.0625 C steps, 750 ms conversion
    }
  }
}

void setup() {
  Serial.begin(9600);

  // Most 0.96" modules answer on 0x3C; a few are strapped to 0x3D.
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED not answering - check SDA/SCL and 5V"));
    for (;;) { }
  }
  oled.setTextColor(SSD1306_WHITE);

  scanBus();

  // "YES" here means a probe has VDD tied to GND and is stealing power
  // from the data line - the usual reason readings stick at 85.00.
  Serial.print(F("Parasite power: "));
  Serial.println(sensors.isParasitePowerMode() ? F("YES") : F("no"));
}

void loop() {
  if (found == 0) {                // a missing pull-up looks like THIS, not like -127
    oled.clearDisplay();
    oled.setTextSize(1);
    oled.setCursor(0, 0);
    oled.print(F("NO PROBE FOUND"));
    oled.setCursor(0, 16);
    oled.print(F("CHECK 4.7k -> 5V"));
    oled.setCursor(0, 28);
    oled.print(F("AND YELLOW ON D2"));
    oled.display();
    scanBus();                     // keeps looking, so fixing a wire recovers it
    delay(1000);
    return;
  }

  sensors.requestTemperatures();   // blocks until the probe says it is done (750 ms max)

  oled.clearDisplay();
  oled.setTextSize(1);
  oled.setCursor(0, 0);
  oled.print(F("WATER TEMP"));

  for (uint8_t i = 0; i < found; i++) {
    float c = sensors.getTempC(probe[i]);

    oled.setTextSize(2);
    oled.setCursor(0, 18 + i * 24);

    if (c <= DEVICE_DISCONNECTED_C) {        // -127: nobody answered
      oled.print(F("--.- C"));
      Serial.println(F("-127 : no reply - check the 4.7k and the wiring"));
    } else {
      oled.print(c, 1);
      oled.print(F(" C"));
      Serial.print(i);
      Serial.print(F(" : "));
      Serial.println(c, 2);
      if (c > 84.9 && c < 85.1) {             // power-on scratchpad default
        Serial.println(F("   85.00 : read before conversion, or starved supply"));
      }
    }
  }

  oled.display();
  delay(1000);
}

On an Uno it compiles to 19,620 bytes of flash β€” 60% of the 32 KB β€” with 595 bytes of static RAM; the display library then allocates a 1,024-byte frame buffer when begin() runs, so about 1.6 KB of the Uno’s 2 KB is committed before your own variables. That is why every fixed string here is wrapped in F(): unwrapped, each would be copied into what is left instead of staying in flash. requestTemperatures() blocks until the probe reports its conversion finished β€” the DallasTemperature library polls the bus rather than sleeping a fixed interval, so the wait is capped by the datasheet’s 750 ms maximum at 12 bits rather than fixed at it; 9 bits drops that ceiling to 93.75 ms and coarsens the steps to 0.5 Β°C.

That reading has a second job if you already run our TDS water quality meter. Conductivity climbs about 2% per degree Celsius, so that build assumes 25 Β°C and a cooler tank reads slightly low. A DS18B20 on its own pin hands that sketch a real number instead β€” same Uno, one more probe, and the assumption becomes a measurement.

How do several DS18B20 probes share one Arduino pin?

Every DS18B20 leaves the factory with a 64-bit ROM code lasered into it: an 8-bit family code (28h for this part), a 48-bit serial number unique to that die, and an 8-bit CRC over the other seven bytes. No two probes answer to the same number, so the master can address one at a time on a shared wire. It finds them by walking the bus bit by bit β€” where two probes disagree, it takes one branch, finishes that address, then returns for the other β€” which is what getAddress() reads out in the sketch above.

In practice: both yellow tails into the same breadboard column, both reds to the red rail, both blacks to the blue rail, and the single 4.7 kΞ© resistor stays where it is β€” a second one only halves the pull-up resistance for no benefit. Run the sketch with Serial Monitor open at 9600 baud and each ROM code prints once at startup. To learn which is which, cup one probe in warm water, watch which index moves, and wrap a tape flag round that cable.

Several probes landing on one rail is easier watched than described:

What do βˆ’127 and 85.00 mean on a DS18B20?

The DS18B20 has two readings that are messages rather than temperatures β€” and one message that never becomes a reading at all.

Reading What it actually is Where to look
Probes on the bus: 0 The search finished and nothing answered β€” no device ever pulled the line low to announce itself The pull-up first: with no resistor the line cannot idle high, so the reset pulse gets no reply. Then the yellow tail’s column, and the pin number the sketch declares
βˆ’127.00 DEVICE_DISCONNECTED_C β€” the master sent a reset pulse and no device answered A probe that answered at startup and has since stopped: a tail worked loose, a strand making intermittent contact, or a cable pinched under the tank lid
85.00 The power-on default of the temperature register, still untouched A reading taken before the conversion finished, or a supply that sags during it
Plausible but frozen A real value that never updates requestTemperatures() outside the loop, so the same scratchpad is read forever

That +85 Β°C is the power-on reset value of the temperature register itself, so a chip powered but not yet finished converting hands it to any read. It also points at a starved supply, which brings in parasite power: a DS18B20 can run with VDD tied to ground, drawing what it needs from the data line while the line idles high. Our probe has three wires, so give VDD the red one and 5 V: normal power, the mode used here. Parasite mode instead needs the master to clamp the line hard high for the whole conversion, because a 4.7 kΞ© resistor cannot supply the ~1.5 mA the chip draws while converting. The sketch prints Parasite power: YES if it sees that state, which means the red wire is not doing its job.

Common mistakes we see from real customers

Buying the probe and forgetting the resistor. A customer messaged us: “For the resistor: do you have a 4.7k or 10k 1/4W resistor available? I need about 10 pcs for my DS18B20 pull-up.” He had the value right and the count generous: one bus needs one resistor. The probe ships as probe only β€” no breakout board, no resistor.

Assuming a starter pack covers it. Another asked whether a set included “220 Ohm, 1K Ohm, 4.7K Ohm and 10K Ohm”. Read what you already own by the bands above rather than by hope, and if you are buying rather than digging, buy the value itself β€” one 4.7 kΞ© 1/4 W part settles the whole bus.

Expecting the display to light on its own. A blank OLED beside a working probe is almost always the IΒ²C address: most 0.96-inch modules answer on 0x3C, a few on 0x3D, and File β†’ Examples β†’ Wire β†’ i2c_scanner prints whichever address is really on A4/A5, so you change that one number in oled.begin() knowing the answer rather than guessing β€” the same habit our LCD1602 IΒ²C guide teaches.

FAQ

Do I really need a resistor for the DS18B20?

Yes β€” one 4.7 kΞ© 1/4 W resistor between the data wire and 5 V, for the whole bus. Probe and Arduino can only pull the line low, so the resistor is the only component able to raise it; without it the bus never idles high and the scan finds no probe at all.

Why does my DS18B20 read βˆ’127?

βˆ’127 is the library’s DEVICE_DISCONNECTED_C sentinel: the reset pulse got no answer. If the probe was never found, the sketch reports zero devices at startup and the fault is the missing pull-up, the resistor in the wrong column, or the wrong pin. A true βˆ’127 means it answered once and stopped β€” a tail worked loose or a strand barely touching.

Can I use a 10 kΞ© resistor instead of 4.7 kΞ©?

For a single probe on the standard one-metre cable, yes: the line still reaches a valid high in roughly two microseconds against a 15 Β΅s deadline. That margin shrinks as probes and cable add capacitance, so 4.7 kΞ© is the value to keep. Two 10 kΞ© in parallel give 5 kΞ©.

How many DS18B20 sensors can I connect to one Arduino pin?

Enough for any tank setup. Each carries a unique 64-bit ROM address, so they share one pin and one pull-up. The limit is electrical, not a number in the library: total cable capacitance eventually blunts the rising edge.

Is the DS18B20 accurate enough for an aquarium?

Comfortably. It is specified to Β±0.5 Β°C from βˆ’10 Β°C to +85 Β°C and resolves 0.0625 Β°C steps at 12 bits, so a failing heater shows up long before livestock notice.

Last updated August 2026. Stuck? Chat with us on WhatsApp.

Leave a Reply

Your email address will not be published. Required fields are marked *