Repository navigation
Set up tank temperature monitors #2525
Description
Tank temperatures: ESP32 + DS18B20 build guide
Status: parts on hand, nothing built yet. The firmware below is an untested draft for a pilot. Dashboard integration (collector + data/tanks.json) comes after the hubs work on the bench.
What we have
| Item | Qty | Use |
|---|---|---|
| ESP32 dev board (ESP32-DevKitC or Adafruit HUZZAH32) | 3 | 2 hubs + 1 spare |
| DS18B20 waterproof probe, 316 stainless, 3 m | 12 | 10 tanks + 2 spares |
| ESP32 screw-terminal breakout | 2 | One per hub, so nothing needs soldering |
| 4.7 kΩ resistors | pack | One pull-up per hub |
| Wago 221 lever connectors | pack | Join the probe wires |
| 3-conductor 22 AWG shielded cable, 15 m | 1 | Short leads and jumpers; extensions if a tank is > 3 m away |
| 5 V 2 A USB supply + USB cable | 2 + 3 | Power (the third cable is for flashing the spare) |
Each hub is one ESP32 with 5 probes on a single 1-Wire data line. Every DS18B20 has a unique 64-bit ID, so one pin reads all 5.
Layout
tank 1 tank 2 tank 3 tank 4 tank 5
│ │ │ │ │ probe cables, 3 m each
└────────┴────┬───┴────────┴────────┘
│ Wago banks: all RED together, all BLACK together, all YELLOW together
│ short 3-conductor lead
┌───────┴────────┐
│ ESP32 hub "a" │ reads probes every 60 s, serves JSON on WiFi
└───────┬────────┘
│ USB → 5 V supply (mounted high, away from splash)
▼
collector on the UW network polls http://<hub>/ → seneye-style branch → dashboard
Hub "b" is identical for tanks 6–10.
1. Check the probe wire colors
Most probes use red = power (VDD), black = ground (GND), yellow = data (DQ). Some use white or blue for data, or swap black and yellow. Check the seller's listing or the bag label before wiring. Getting power and ground backwards makes the probe hot and usually kills it.
2. Bench test with ONE probe
Do this at a desk before cutting or bundling anything.
- Push the ESP32 into the screw-terminal breakout.
- Wire one probe to the breakout:
| Probe wire | Breakout terminal |
|---|---|
| Red (VDD) | 3V3 (not 5V / VIN) |
| Black (GND) | GND |
| Yellow (DATA) | GPIO 4: labeled 4, IO4 or G4 on the DevKitC, and A5 on the HUZZAH32. Check your board's pinout card. |
- Put the 4.7 kΩ resistor between 3V3 and GPIO 4. Clamp one leg in each terminal, alongside the probe wires.
- Flash the firmware (section 4). The Serial Monitor should show one probe with a sensible room temperature.
3. Wire a full hub (5 probes)
Each color needs 6 connections: 5 probes plus 1 lead to the board. A Wago 221 holds at most 5, so for each color join two connectors with a short jumper. A 5-port plus a 3-port gives exactly 6 free slots; two 5-ports give 8.
- Cut about 30 cm of the 3-conductor cable to use as the lead. Its three conductors run from the Wago banks to the breakout (3V3, GND, GPIO 4). Cut jumpers from the same spool.
- Strip the probe wires about 11 mm. Probe conductors are often thinner than the 24 AWG minimum the Wago 221 is rated for, so fold the bare end back on itself to double it before inserting. Tug-test every wire.
- Bank 1: all 5 reds + the lead conductor going to 3V3.
Bank 2: all 5 blacks + the lead conductor going to GND.
Bank 3: all 5 yellows + the lead conductor going to GPIO 4. - Keep the single 4.7 kΩ pull-up at the breakout (3V3 ↔ GPIO 4). Use one pull-up per hub, not one per probe.
- If the cable has a bare drain wire or foil shield, connect it to GND at the hub end only.
- Extensions: to reach a tank more than 3 m away, splice in the 3-conductor cable with Wagos (color for color). Keep each hub's total wire under about 20 m.
If probes drop out or read null intermittently, the bus is struggling with the cable length. Add a second 4.7 kΩ in parallel with the first, which makes about 2.35 kΩ.
4. Firmware
One-time setup (Arduino IDE 2.x):
- Boards Manager → install esp32 by Espressif Systems.
- Library Manager → install OneWire (Paul Stoffregen) and DallasTemperature (Miles Burton).
- Board: ESP32 Dev Module (DevKitC) or Adafruit ESP32 Feather (HUZZAH32).
- If no serial port appears, the USB cable is probably charge-only. Try another cable. Some DevKitC boards also need the CP210x or CH340 USB driver.
Edit the four settings at the top, then upload. Set HUB_NAME to shore-temps-a on the first board and shore-temps-b on the second.
// shore_temps.ino — SHORE tank temperature hub (ESP32 + DS18B20 probes on one 1-Wire bus)
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <OneWire.h>
#include <DallasTemperature.h>
const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";
const char* HUB_NAME = "shore-temps-a"; // "shore-temps-b" on the second hub
const int ONE_WIRE_PIN = 4; // GPIO 4 (A5 on HUZZAH32)
const unsigned long READ_EVERY_MS = 60UL * 1000; // read probes once a minute
const unsigned long WIFI_GIVEUP_MS = 10UL * 60 * 1000; // reboot if WiFi is down this long
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);
WebServer server(80);
String latestJson = "{}";
unsigned long lastRead = 0, wifiLostAt = 0;
String idToString(const DeviceAddress a) {
char buf[17];
for (int i = 0; i < 8; i++) sprintf(buf + 2 * i, "%02X", a[i]);
return String(buf);
}
void readProbes() {
sensors.begin(); // re-scan so a replugged probe reappears
sensors.requestTemperatures(); // ~750 ms at 12-bit resolution
String out = "{\"hub\":\"" + String(HUB_NAME) + "\",\"uptime_s\":" + String(millis() / 1000) +
",\"probes\":[";
DeviceAddress a;
int n = sensors.getDeviceCount(), written = 0;
for (int i = 0; i < n; i++) {
if (!sensors.getAddress(a, i)) continue;
float c = sensors.getTempC(a);
bool bad = (c == DEVICE_DISCONNECTED_C) || (c == 85.0); // 85.0 = power-on value, not a reading
if (written++) out += ",";
out += "{\"id\":\"" + idToString(a) + "\",\"temp_c\":" + (bad ? String("null") : String(c, 2)) + "}";
}
out += "]}";
latestJson = out;
Serial.println(latestJson);
}
void setup() {
Serial.begin(115200);
delay(500);
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
Serial.println("MAC address: " + WiFi.macAddress()); // UW IT may ask for this
WiFi.begin(WIFI_SSID, WIFI_PASS);
for (int i = 0; i < 60 && WiFi.status() != WL_CONNECTED; i++) delay(500);
Serial.println("IP address: " + WiFi.localIP().toString());
MDNS.begin(HUB_NAME); // http://shore-temps-a.local/ on the same subnet
sensors.setResolution(12);
server.on("/", []() { server.send(200, "application/json", latestJson); });
server.begin();
readProbes();
lastRead = millis();
}
void loop() {
server.handleClient();
if (millis() - lastRead >= READ_EVERY_MS) { readProbes(); lastRead = millis(); }
if (WiFi.status() != WL_CONNECTED) {
if (!wifiLostAt) wifiLostAt = millis();
if (millis() - wifiLostAt > WIFI_GIVEUP_MS) ESP.restart();
} else {
wifiLostAt = 0;
}
}Open the Serial Monitor at 115200 baud. You should see the MAC address, the IP address, and then a line like this every minute:
{"hub":"shore-temps-a","uptime_s":61,"probes":[{"id":"28FF4A1B6E180301","temp_c":20.81}, ...]}Opening http://<IP address>/ in a browser on the same network returns the same JSON.
5. Network
The ESP32 only works on 2.4 GHz WiFi and can't easily join eduroam (WPA2-Enterprise). Ask UW IT for a device network in the tank room, such as UW MPSK. They may want each board's MAC address, which is printed at boot. Also ask for a DHCP reservation (fixed IP) for each hub, so the collector always finds it.
6. Identify and check each probe
- With all 5 probes on a hub, hold one probe in your hand. Within about a minute, its ID is the one whose reading climbs.
- Label both ends of that cable (tank name) and record it in the table below.
- Ice-bath check: put all probes in crushed ice topped up with water and stir. After 5 minutes each should read 0.0 ± 0.5 °C. Record any steady offset. A probe that is off by more than 0.5 °C becomes a spare.
| Hub | Probe ID | Tank | Ice-bath reading (°C) |
|---|---|---|---|
| a | |||
| a | |||
| a | |||
| a | |||
| a | |||
| b | |||
| b | |||
| b | |||
| b | |||
| b |
The ID → tank mapping and offsets will live in the repo, not in the firmware, so moving a probe never means reflashing.
7. Install in the tank room
- Keep the electronics dry. Mount each hub and its USB supply above splash height, in a closed plastic box if possible. Use a GFCI-protected outlet.
- Give every probe cable a drip loop, a low U-bend before it rises to the hub, so water runs off instead of following the cable up.
- Put each probe mid-depth, away from heater, chiller and inflow outlets, and clip or zip-tie it so it can't drift.
- Wipe the probes during routine tank cleaning. Fouling slows their response.
8. Next: dashboard integration (not built yet)
The hubs sit on a private UW network that GitHub can't reach, so this mirrors the Seneye setup: a collector inside the network polls each hub's JSON every 15 min, applies the ID → tank map and offsets, keeps a 14-day series, and publishes it for the site build to fill data/tanks.json.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| No probes found | Data wire not on GPIO 4, pull-up missing, or red and black swapped |
Probe reads null |
Loose Wago connection (redo the fold-back and tug test) or bus too long (add the second pull-up) |
| Reads exactly 85.0 | Brief power glitch. The firmware already reports it as null. If it repeats, check the 3V3 connection. |
| Probe hot to the touch | Power and ground reversed. Unplug immediately. |
IP address shows 0.0.0.0 |
Wrong SSID or password, a 5 GHz-only network, or the device isn't registered with UW IT |
Activity