Delta-T monitor on an Arduino Uno
Two DS18B20s on one wire read supply and return air: the drop across the coil is judged only while the cooling call runs and has settled, and a drain-pan float cuts the call through a relay and raises an alarm (a low-voltage teaching model). The whole build, an Arduino Uno and 19 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Advanced About 55 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $40. On Mokxi they're free.
What you'll learn
- Sensors
- School
- Choosing and reading a resistor
- How an LED lights and why it needs a resistor
- Difficulty: Advanced
- Time: About 55 minutes
- Board: Arduino Uno R3
- Parts: 21
How it works
Two DS18B20s on one wire read supply and return air: the drop across the coil is judged only while the cooling call runs and has settled, and a drain-pan float cuts the call through a relay and raises an alarm (a low-voltage teaching model).
Arduino Uno R3 runs the sketch below. The circuit around it: Temperature sensor, DS18B20, Float switch, ZP4510, Relay module, Slide switch, Resistor, 10k Ω, Resistor, 220 Ω. pinMode sets PIN_FLOAT as an input. digitalRead reads whether PIN_RUNNING is HIGH or LOW. Serial.begin opens the serial port at 115200 baud. millis reads the time since power-up, so the sketch can time things without stopping.
Parts list
21 parts, plus the jumper wires. They retail for about $40; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Full-size breadboard
- 2 × Temperature sensor, DS18B20
- 1 × Float switch, ZP4510
- 1 × Relay moduleIn Halloween Prop Kit.
- 1 × Slide switchIn Logic Lab Kit, Spaceship Panel Kit.
- 2 × Resistor, 10k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 6 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, whiteIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 2 × LED, yellowIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × LED, greenIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × LED, blueIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × LED, redIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Resistor, 220 Ω (6) pin 1 | Arduino Uno R3 pin 9 |
| Resistor, 220 Ω (5) pin 1 | Arduino Uno R3 pin 8 |
| Resistor, 220 Ω (4) pin 1 | Arduino Uno R3 pin 7 |
| Resistor, 220 Ω (3) pin 1 | Arduino Uno R3 pin 6 |
| Relay module pin NC | Arduino Uno R3 pin 4 |
| Resistor, 10k Ω (2) pin 2 | Arduino Uno R3 pin 4 |
| Resistor, 220 Ω (1) pin 1 | Arduino Uno R3 pin 4 |
| Float switch, ZP4510 pin 2 | Arduino Uno R3 pin 3 |
| Relay module pin IN | Arduino Uno R3 pin 3 |
| Resistor, 10k Ω (1) pin 2 | Arduino Uno R3 pin 3 |
| Temperature sensor, DS18B20 (1) pin DQ | Arduino Uno R3 pin 2 |
| Temperature sensor, DS18B20 (2) pin DQ | Arduino Uno R3 pin 2 |
| Temperature sensor, DS18B20 (1) pin VDD | Arduino Uno R3 pin 5V |
| Temperature sensor, DS18B20 (2) pin VDD | Arduino Uno R3 pin 5V |
| Float switch, ZP4510 pin 1 | Arduino Uno R3 pin 5V |
| Relay module pin VCC | Arduino Uno R3 pin 5V |
| Slide switch pin 3 | Arduino Uno R3 pin 5V |
| Temperature sensor, DS18B20 (1) pin GND | Arduino Uno R3 pin GND |
| Temperature sensor, DS18B20 (2) pin GND | Arduino Uno R3 pin GND |
| Relay module pin GND | Arduino Uno R3 pin GND |
| Resistor, 10k Ω (1) pin 1 | Arduino Uno R3 pin GND |
| Resistor, 10k Ω (2) pin 1 | Arduino Uno R3 pin GND |
| LED, white pin C | Arduino Uno R3 pin GND |
| LED, yellow (1) pin C | Arduino Uno R3 pin GND |
| LED, green pin C | Arduino Uno R3 pin GND |
| LED, blue pin C | Arduino Uno R3 pin GND |
| LED, yellow (2) pin C | Arduino Uno R3 pin GND |
| LED, red pin C | Arduino Uno R3 pin GND |
| Resistor, 220 Ω (2) pin 1 | Relay module pin NO |
| Slide switch pin 2 | Relay module pin COM |
| LED, white pin A | Resistor, 220 Ω (1) pin 2 |
| LED, yellow (1) pin A | Resistor, 220 Ω (2) pin 2 |
| LED, green pin A | Resistor, 220 Ω (3) pin 2 |
| LED, blue pin A | Resistor, 220 Ω (4) pin 2 |
| LED, yellow (2) pin A | Resistor, 220 Ω (5) pin 2 |
| LED, red pin A | Resistor, 220 Ω (6) pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Delta-T monitor: how much cooler is the air leaving an air conditioner's
// coil than the air coming back to it, and is the condensate drain overflowing?
//
// THIS IS A TEACHING MODEL. The call, the relay and the contactor LED run at
// 5 V; a real system switches 24 VAC between R and Y, and a real condensate
// float switch is wired straight into that 24 V line. Never connect this to a
// real air conditioner or a building.
//
// pin 2 both DS18B20 thermometers (one-wire bus, one 4.7 kohm pull-up)
// pin 3 float sense: HIGH when the float is up (water high)
// pin 4 running: HIGH when the contactor is energized
// pin 6 OK green LED delta-T inside 14 to 22 F
// pin 7 LOW blue LED delta-T under 14 F
// pin 8 HIGH yellow LED delta-T over 22 F
// pin 9 ALARM red LED condensate overflow, latched
//
// DELTA-T
// Delta-T is the return air temperature minus the supply air temperature,
// in degrees F. Across a cooling coil about 20 F is typical, and 14 to 22 F
// is a common rule of thumb for "healthy". It is a hint, not a diagnosis:
// it moves with the humidity and with the outdoor temperature.
// Under 14 F low refrigerant, too much airflow, or a sensor in the
// wrong place (not in the airstream, or too far from the coil).
// Over 22 F too little airflow: a dirty filter, a closed register, a
// weak blower or a dirty coil.
// The two sensors are found by their ROM codes (printed at start-up). Paste
// your own pair into SUPPLY_ROM and RETURN_ROM; they are labelled in the
// print-out the way you would label the probes with tape.
//
// WHEN IT JUDGES
// Only while the contactor is running (pin 4 high for 0.5 s), and only after
// the SETTLE time, so that the coil and the ducts have reached their steady
// temperatures. Real coils want a few minutes, so SETTLE is 10 minutes real,
// scaled by 20 here to 30 s. Idle or settling, the verdict LEDs stay dark.
// Min, max and average delta-T cover the judged samples of the current run
// and start again with each new run.
//
// CONDENSATE FLOAT SWITCH
// A float in the drain pan closes when the water rises. In the hardware path
// it drives a relay whose normally closed contact is in series with the Y
// call, so rising water opens the call to the contactor with no help from
// the Uno, the way a real float switch opens the R and Y side. The same
// float signal also goes to pin 3, and the sketch raises the alarm.
// Latch rule: the alarm sets after the float has been up for 0.5 s and stays
// set until the float has been down for CLEAR time (3 minutes real, 9 s
// here). The relay is not latched: it lets the call back the moment the
// float drops, as a real float switch does. So the alarm reminds you that
// water got high, while the call restarts and settles again by itself.
#include <OneWire.h>
#include <DallasTemperature.h>
const int PIN_BUS = 2;
const int PIN_FLOAT = 3;
const int PIN_RUNNING = 4;
const int PIN_OK = 6;
const int PIN_LOW = 7;
const int PIN_HIGH = 8;
const int PIN_ALARM = 9;
const unsigned long SCALE = 20;
const unsigned long SETTLE_MS = 600000UL / SCALE;
const unsigned long CLEAR_MS = 180000UL / SCALE;
const unsigned long DEBOUNCE_MS = 500;
// Tenths of a degree F.
const int LOW_BELOW = 140;
const int HIGH_ABOVE = 220;
// Replace these with the codes your own sensors print at start-up.
DeviceAddress SUPPLY_ROM = {0x28, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29};
DeviceAddress RETURN_ROM = {0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70};
OneWire oneWire(PIN_BUS);
DallasTemperature sensors(&oneWire);
enum Verdict { NONE, LOW_DT, OK_DT, HIGH_DT };
const char *const VERDICT_NAME[] = {"none", "LOW", "OK", "HIGH"};
bool converting = false;
unsigned long requestedAt = 0;
bool sensorsOk = false;
int supplyF = 0, returnF = 0, deltaF = 0; // tenths
bool running = false;
unsigned long runningSince = 0;
bool runRaw = false;
unsigned long runRawAt = 0;
bool settled = false;
Verdict verdict = NONE;
int dtMin = 0, dtMax = 0;
long dtSum = 0;
unsigned int dtCount = 0;
bool alarm = false;
unsigned int trips = 0;
bool floatRaw = false;
unsigned long floatRawAt = 0;
unsigned long floatLowSince = 0;
unsigned long lastStatus = 0;
void printTenths(int t) {
if (t < 0) {
Serial.print('-');
t = -t;
}
Serial.print(t / 10);
Serial.print('.');
Serial.print(t % 10);
}
void printRom(const uint8_t *a) {
for (int i = 0; i < 8; i++) {
if (a[i] < 16) Serial.print('0');
Serial.print(a[i], HEX);
}
}
int toTenthsF(long raw) {
// raw is 1/128 C; F = C * 1.8 + 32, in tenths
return (int)((raw * 18 + (raw < 0 ? -64 : 64)) / 128) + 320;
}
void readTemperatures() {
if (!converting) {
sensors.requestTemperatures();
requestedAt = millis();
converting = true;
return;
}
if (millis() - requestedAt < 800) return;
converting = false;
long s = sensors.getTemp(SUPPLY_ROM);
long r = sensors.getTemp(RETURN_ROM);
bool good = s != DEVICE_DISCONNECTED_RAW && r != DEVICE_DISCONNECTED_RAW;
if (!good && sensorsOk) Serial.println("FAULT: a thermometer is not answering, no verdicts");
sensorsOk = good;
if (!good) return;
supplyF = toTenthsF(s);
returnF = toTenthsF(r);
deltaF = returnF - supplyF;
// A new sample counts toward the statistics only while judging.
if (running && settled) {
if (dtCount == 0 || deltaF < dtMin) dtMin = deltaF;
if (dtCount == 0 || deltaF > dtMax) dtMax = deltaF;
dtSum += deltaF;
dtCount++;
}
}
void printStats() {
if (dtCount == 0) {
Serial.println("no judged samples");
return;
}
Serial.print("min ");
printTenths(dtMin);
Serial.print("F max ");
printTenths(dtMax);
Serial.print("F avg ");
printTenths((int)(dtSum / (long)dtCount));
Serial.print("F over ");
Serial.print(dtCount);
Serial.println(" samples");
}
void updateRunning() {
bool raw = digitalRead(PIN_RUNNING) == HIGH;
if (raw != runRaw) {
runRaw = raw;
runRawAt = millis();
}
if (raw != running && millis() - runRawAt >= DEBOUNCE_MS) {
running = raw;
settled = false;
verdict = NONE;
if (running) {
runningSince = millis();
dtCount = 0;
Serial.println("RUN start: settling before judging");
} else {
Serial.print("RUN stop: ");
printStats();
}
}
if (running && !settled && millis() - runningSince >= SETTLE_MS) {
settled = true;
Serial.println("SETTLED: judging delta-T");
}
}
void judge() {
Verdict next = NONE;
if (running && settled && sensorsOk) {
next = deltaF < LOW_BELOW ? LOW_DT : (deltaF > HIGH_ABOVE ? HIGH_DT : OK_DT);
}
if (next != verdict) {
verdict = next;
if (verdict != NONE) {
Serial.print("VERDICT ");
Serial.print(VERDICT_NAME[verdict]);
Serial.print(" dT ");
printTenths(deltaF);
Serial.print("F");
if (verdict == LOW_DT) Serial.print(": check refrigerant, airflow and sensor placement");
if (verdict == HIGH_DT) Serial.print(": check filter, registers, blower and coil");
Serial.println();
}
}
digitalWrite(PIN_OK, verdict == OK_DT);
digitalWrite(PIN_LOW, verdict == LOW_DT);
digitalWrite(PIN_HIGH, verdict == HIGH_DT);
}
void updateFloat() {
unsigned long now = millis();
bool raw = digitalRead(PIN_FLOAT) == HIGH;
if (raw != floatRaw) {
floatRaw = raw;
floatRawAt = now;
}
if (raw) floatLowSince = now;
if (!alarm && raw && now - floatRawAt >= DEBOUNCE_MS) {
alarm = true;
trips++;
Serial.print("ALARM: condensate overflow, the float relay has cut the Y call (trip ");
Serial.print(trips);
Serial.println(")");
}
if (alarm && !raw && now - floatLowSince >= CLEAR_MS) {
alarm = false;
Serial.println("ALARM cleared: the float has been down long enough");
}
digitalWrite(PIN_ALARM, alarm);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_FLOAT, INPUT);
pinMode(PIN_RUNNING, INPUT);
pinMode(PIN_OK, OUTPUT);
pinMode(PIN_LOW, OUTPUT);
pinMode(PIN_HIGH, OUTPUT);
pinMode(PIN_ALARM, OUTPUT);
Serial.println("Mokxi Arduino Uno: delta-T monitor (teaching model, 20x faster timers)");
sensors.begin();
sensors.setWaitForConversion(false);
int found = sensors.getDeviceCount();
Serial.print(found);
Serial.println(" thermometers on the bus");
DeviceAddress a;
for (int i = 0; i < found; i++) {
if (!sensors.getAddress(a, i)) continue;
Serial.print("ROM ");
printRom(a);
if (memcmp(a, SUPPLY_ROM, 8) == 0) Serial.print(" SUPPLY");
if (memcmp(a, RETURN_ROM, 8) == 0) Serial.print(" RETURN");
Serial.println();
}
}
void loop() {
readTemperatures();
updateRunning();
updateFloat();
judge();
if (millis() - lastStatus >= 1000) {
lastStatus = millis();
Serial.print("S ");
printTenths(supplyF);
Serial.print("F R ");
printTenths(returnF);
Serial.print("F dT ");
printTenths(deltaF);
Serial.print("F ");
if (!running) Serial.print("idle");
else if (!settled) Serial.print("settling");
else Serial.print(VERDICT_NAME[verdict]);
if (alarm) Serial.print(" ALARM");
if (dtCount > 0) {
Serial.print(" min ");
printTenths(dtMin);
Serial.print(" max ");
printTenths(dtMax);
Serial.print(" avg ");
printTenths((int)(dtSum / (long)dtCount));
}
Serial.println();
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Runs once when the board starts. It gets the pins and libraries ready.
void setup() {
Serial.begin(115200);
pinMode(PIN_FLOAT, INPUT);
pinMode(PIN_RUNNING, INPUT);
pinMode(PIN_OK, OUTPUT);
pinMode(PIN_LOW, OUTPUT);
pinMode(PIN_HIGH, OUTPUT);
pinMode(PIN_ALARM, OUTPUT);
Serial.println("Mokxi Arduino Uno: delta-T monitor (teaching model, 20x faster timers)");
sensors.begin();
sensors.setWaitForConversion(false);
int found = sensors.getDeviceCount();
Serial.print(found);
Serial.println(" thermometers on the bus");
DeviceAddress a;
for (int i = 0; i < found; i++) {
if (!sensors.getAddress(a, i)) continue;
Serial.print("ROM ");
printRom(a);
if (memcmp(a, SUPPLY_ROM, 8) == 0) Serial.print(" SUPPLY");
if (memcmp(a, RETURN_ROM, 8) == 0) Serial.print(" RETURN");
Serial.println();
}
}loop sketch.ino
Runs over and over for as long as the board has power. This is where the project does its work.
void loop() {
readTemperatures();
updateRunning();
updateFloat();
judge();
if (millis() - lastStatus >= 1000) {
lastStatus = millis();
Serial.print("S ");
printTenths(supplyF);
Serial.print("F R ");
printTenths(returnF);
Serial.print("F dT ");
printTenths(deltaF);
Serial.print("F ");
if (!running) Serial.print("idle");
else if (!settled) Serial.print("settling");
else Serial.print(VERDICT_NAME[verdict]);
if (alarm) Serial.print(" ALARM");
if (dtCount > 0) {
Serial.print(" min ");
printTenths(dtMin);
Serial.print(" max ");
printTenths(dtMax);
Serial.print(" avg ");
printTenths((int)(dtSum / (long)dtCount));
}
Serial.println();
// ... 2 more lines in the editorTroubleshooting
The usual mistakes with this circuit, and the fix for each.
- The sensor reads -127 C or no device is found. Temperature sensor
Put a 4.7 k resistor between the data line and VCC. Check the pinout, since swapping VCC and GND can overheat the sensor. Then confirm the data pin matches your code. - It always shows 85 C. Temperature sensor
85 C is the power on value, meaning the conversion has not finished or the supply is weak. Wait for the conversion (up to 750 ms at 12 bit) before reading. - The switch acts opposite to what you expected: closed when dry, open when wet. Float switch
The float can be mounted either way up. Turn the float over on the stem to change whether the reed closes or opens when the water rises. Test with a meter. - The input reads random values or the pump chatters at the water level. Float switch
Wire it between the pin and ground with INPUT_PULLUP. Waves make the reed flicker, so add a delay or a short debounce time and keep the float in calm water. - The relay does not click, or the board resets when it switches. Relay module
A relay coil needs more current than a pin can give. Use a module with a transistor driver, and power VCC from 5 V. Share ground between the module and the Arduino. - The relay works backwards: on when you expect off. Relay module
Many modules are active LOW: the input must go LOW to turn the relay on. Set the pin HIGH at startup so the load does not switch on during boot. - The input reads random values in one position. Slide switch
A switch only connects or disconnects. Add a pull-up or pull-down resistor, or use INPUT_PULLUP, so the pin has a defined level when the switch is open. - The load never turns on when the switch is flipped. Slide switch
Use the center (common) leg and one outer leg. The unused outer leg goes nowhere. Check that the switch is in series with the load.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.