Wet or dry
A detector that says wet once when water arrives and dry once when it has gone. VCC comes from a pin and is switched on for 500 ms every 10 s. It calls the board wet above 30 % of full scale and dry again only below 10 %, because one line on a rippling analog reading is crossed several times.
Wiring, one wire moved
The same three wires as the first reading, except VCC: it goes to a pin the sketch drives instead of to 3V3 or 5V. D7 on an Uno, GPIO 25 on an ESP32, GPIO 5 on an ESP32-S3, GP15 on a Pico. None is a strapping pin, and each drives HIGH at the board's logic voltage.
Two thresholds, not one
A detector turns a reading into a yes or a no. The obvious way is one line: above it, wet. But an analog reading ripples, and a board drying slowly passes that line not once but several times on the way down.
Press Play with one line and the sketch reports a single breath as wet, dry, wet, dry. With two, wet above 30 % and dry only below 10 %, it reports it once each way. Between the two lines the sketch keeps whatever it last said. The gap is called hysteresis.
The figure's breath is a model, with a ripple added; it is the shape that matters, not the numbers.
The sketch
loop() runs once every 10 s. It switches POWER_PIN HIGH, waits
SETTLE_MS, 500 ms, for the 100 nF, reads, and switches the pin LOW again.
Then it compares the reading with WET_AT and DRY_AT and prints wet or
dry only when the state changes.
FULL is 4095 on an ESP32 and 1023 on anything else, so WET_AT and
DRY_AT come out right on both. They are starting points: a single drop of
condensate reads well under 30 %, and the sketch will not call it wet. Run
the first sketch, see where your board sits dry, fogged and splashed, and
set the two thresholds between them.
What you should see
Dry, a line every 10 s, reading 0 or close to it. Hold the loops over a
steaming mug for ten seconds or so and the next reading jumps: the sketch
prints it and then wet. Take it away, let it dry, and a reading or two
later it prints dry.
The code
Every 10 s: POWER_PIN HIGH, wait 500 ms, analogRead, POWER_PIN LOW. It prints each reading, and prints wet or dry only when the state changes. Change STEAM_PIN and POWER_PIN to your wiring.
/*
Steam Sensor - wet or dry TK65 / /p/tk65
Wiring. The same as the first reading, except VCC, which comes
from a pin so the loops only carry current while being read.
Count from the square pad on the TinkerBlock board, parts up,
header at the bottom:
GND -> GND
VCC -> POWER_PIN: D7 on an Uno, GPIO 25 on an ESP32,
GPIO 5 on an ESP32-S3, GP15 on a Raspberry Pi Pico.
It drives HIGH at your board's logic voltage.
NC -> nothing (unconnected on the board)
SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32,
GPIO 4 on an ESP32-S3, GP26 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int STEAM_PIN = 4;
// The pin that powers VCC.
// Uno: 7. ESP32: 25. ESP32-S3: 5. Pico: 15.
const int POWER_PIN = 5;
// Full scale: 4095 on an ESP32 or S3, 1023 on an Uno or a Pico.
#if defined(ARDUINO_ARCH_ESP32)
const int FULL = 4095;
#else
const int FULL = 1023;
#endif
// Wet above 30 % of full scale, dry again only below 10 %.
const int WET_AT = FULL * 30 / 100;
const int DRY_AT = FULL * 10 / 100;
// The 1 MΩ and the 100 nF take 0.1 s per time constant;
// five of them is 500 ms, within 1 % of the final value.
const unsigned long SETTLE_MS = 500;
const unsigned long PERIOD_MS = 10000; // one reading every 10 s
bool wet = false;
void setup() {
Serial.begin(115200);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, LOW); // loops off between reads
}
void loop() {
digitalWrite(POWER_PIN, HIGH);
delay(SETTLE_MS);
int reading = analogRead(STEAM_PIN);
digitalWrite(POWER_PIN, LOW);
Serial.print("reading ");
Serial.println(reading);
if (!wet && reading > WET_AT) {
wet = true;
Serial.println("wet");
} else if (wet && reading < DRY_AT) {
wet = false;
Serial.println("dry");
}
delay(PERIOD_MS - SETTLE_MS);
}The thresholds are shares of full scale, so the same sketch works on a 10-bit Uno and a 12-bit ESP32: 307 and 102 counts on an Uno, 1229 and 410 on an ESP32. They are starting points, not calibration; set them from the readings your own board prints. It compiles for an ESP32-S3 and an Uno.
View on GitHub · blocks/tk65-steam-sensor/arduino/steam_wet_or_dry/steam_wet_or_dry.ino @ v1.5The same detector in MicroPython, for an ESP32, an ESP32-S3 or a Pico. The thresholds are shares of read_u16's 65535.
"""
Steam Sensor - wet or dry, MicroPython TK65 / /p/tk65
Wiring. The same as the first reading, except VCC, which comes
from a pin so the loops only carry current while being read.
Count from the square pad on the TinkerBlock board, parts up,
header at the bottom:
GND -> GND
VCC -> POWER_PIN: GPIO 25 on an ESP32, GPIO 5 on an
ESP32-S3, GP15 on a Raspberry Pi Pico
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine, sys and time are built in.
"""
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
STEAM_PIN = 4
# The GPIO number that powers VCC. ESP32: 25. ESP32-S3: 5. Pico: 15.
POWER_PIN = 5
WET_AT = 65535 * 30 // 100 # wet above 30 % of full scale
DRY_AT = 65535 * 10 // 100 # dry again only below 10 %
SETTLE_MS = 500 # five of the 0.1 s time constant
PERIOD_MS = 10000 # one reading every 10 s
adc = ADC(Pin(STEAM_PIN))
if sys.platform == "esp32": # ESP32 and ESP32-S3
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
power = Pin(POWER_PIN, Pin.OUT, value=0) # loops off between reads
wet = False
while True:
power.on()
time.sleep_ms(SETTLE_MS)
reading = adc.read_u16()
power.off()
print("reading", reading)
if not wet and reading > WET_AT:
wet = True
print("wet")
elif wet and reading < DRY_AT:
wet = False
print("dry")
time.sleep_ms(PERIOD_MS - SETTLE_MS)power.on() and power.off() switch VCC. Stop it with Ctrl-C; the pin stays in whatever state it was left in until the board resets.
View on GitHub · blocks/tk65-steam-sensor/micropython/steam_wet_or_dry.py @ v1.5When it does not work
It only looks every 10 s, and a breath clears in a few seconds, so a breath between two readings is missed. Hold the loops in steam, or lay a wet fingertip on them, for longer than 10 s. Or shorten PERIOD_MS while testing.
The reading is sitting between the two thresholds and crossing both, or the thresholds are too close together. Print the readings, see where wet and dry actually sit on your board, and move WET_AT and DRY_AT apart.
Something still bridges a gap: a trapped drop or a film of grime that conducts when damp. Watch the printed reading. If it stays above DRY_AT, clean the loops with isopropyl alcohol and let them dry.
The pin is not giving the 100 nF time to fill. SETTLE_MS must be about 500. If it is, check the VCC wire is on the pin POWER_PIN names and that pinMode sets it as an OUTPUT.
Edit this page — content/books/steam-sensor/wet-or-dry.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Steam Sensor
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.