A temperature alarm
A TK01 XL LED that lights above 28 °C. Switch it off at the same line and it flickers every time the reading wobbles across; switch it off one degree lower and it changes exactly once each way. That gap is hysteresis.
One line is not enough
The obvious alarm is one comparison: LED on if the temperature is above 28 °C, off if it is below. It works, until the temperature settles close to 28.
Run it with 28 °C, the same line. A fingertip on the thermistor pushes the reading up towards skin temperature, and it levels off not far above the line. Then the last few tenths of a degree of wobble carry it back and forth across 28, and the LED follows every crossing. The trace is drawn to show the behaviour, not recorded, but any real sensor near a threshold does the same.
On an LED it is a flicker. On a relay it is chatter, and on a buzzer a stutter.
Two lines
Now pick 27 °C, one degree lower. The LED turns on when the reading reaches 28 °C, and turns off only when it falls below 27. Between the two lines it stays as it was. The wobble is smaller than the gap, so it never reaches the other line, and the LED changes exactly twice.
That gap is called hysteresis, and the sketch holds it in two lines:
if (!alarmOn && c >= ALARM_C) alarmOn = true;
if (alarmOn && c < ALARM_C - HYST_C) alarmOn = false;alarmOn remembers which side of the gap the alarm is on. Each line can only
change it in one direction.
Wiring the LED
The TK01 goes in beside the thermistor: its GND to GND, its SIGNAL to the pin the XL LED book uses, D9 on an Uno, GPIO 4 on an ESP32, GP15 on a Pico. On an ESP32-S3 the XL LED book's GPIO 4 is already the thermistor here, so the LED moves to GPIO 5.
The sketch waits 250 ms between readings with delay, which is fine for an
alarm that only has one job. If it has to do other things too, blinking
without stopping is the
way to take the delay out.
The code
The averaged reading, plus a TK01 XL LED that turns on at ALARM_C and turns off only below ALARM_C minus HYST_C. The LED's state is remembered in alarmOn so each line only fires when it has to.
/*
NTC Thermistor - a temperature alarm TK12 / /p/tk12
Wiring, the TK12. Count from the square pad, parts up, header at
the bottom:
GND -> GND
VCC -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
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
The TK01 XL LED, counted the same way:
GND -> GND
NC -> nothing (both of its NC pins)
SIGNAL -> D9 on an Uno, GPIO 4 on an ESP32, GPIO 5 on an
ESP32-S3, GP15 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. Arduino Uno
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
#include <math.h>
// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int SENSOR_PIN = A0;
// The TK01's SIGNAL. Uno: 9. ESP32: 4. ESP32-S3: 5. Pico: 15.
const int LED_PIN = 9;
const float ALARM_C = 28.0; // on at or above this
const float HYST_C = 1.0; // off only below ALARM_C - HYST_C
const int SAMPLES = 32;
const float R_FIXED = 10000.0; // the 10 kOhm from VCC to SIGNAL
const float R25 = 10000.0; // the thermistor at 25 C: the "103"
const float B = 3950.0; // its B value: the "3950"
const float T0 = 298.15; // 25 C in kelvin
const float VCC_MV = 3300.0; // ESP32 only: your 3V3 pin, measured
bool alarmOn = false;
float thermistorOhms() {
float sum = 0;
#if defined(ARDUINO_ARCH_ESP32)
for (int i = 0; i < SAMPLES; i++) sum += analogReadMilliVolts(SENSOR_PIN);
float mv = sum / SAMPLES;
if (mv <= 0 || mv >= VCC_MV) return NAN;
return R_FIXED * mv / (VCC_MV - mv);
#else
for (int i = 0; i < SAMPLES; i++) sum += analogRead(SENSOR_PIN);
float n = sum / SAMPLES;
if (n <= 0 || n >= 1023) return NAN;
return R_FIXED * n / (1023.0 - n);
#endif
}
float celsius(float ohms) {
float invT = 1.0 / T0 + log(ohms / R25) / B;
return 1.0 / invT - 273.15;
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
float r = thermistorOhms();
if (isnan(r)) {
Serial.println("SIGNAL at 0: check VCC. At the top: check GND.");
delay(500);
return;
}
float c = celsius(r);
if (!alarmOn && c >= ALARM_C) alarmOn = true;
if (alarmOn && c < ALARM_C - HYST_C) alarmOn = false;
digitalWrite(LED_PIN, alarmOn ? HIGH : LOW);
Serial.print(c, 2);
Serial.println(alarmOn ? " C ALARM" : " C");
delay(250);
}Set HYST_C to 0 to see what the figure shows without it: near 28 °C the LED flickers. LED_PIN is the TK01 XL LED book's own pin, except on the ESP32-S3, where GPIO 4 is the thermistor here, so the LED moves to GPIO 5.
The same alarm in MicroPython: the averaged reading, and a TK01 that turns on at ALARM_C and off only below ALARM_C minus HYST_C.
"""
NTC Thermistor - a temperature alarm, MicroPython TK12 / /p/tk12
Wiring, the TK12. Count from the square pad, parts up, header at
the bottom:
GND -> GND
VCC -> 3V3 (SIGNAL is a fraction of VCC)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
The TK01 XL LED, counted the same way:
GND -> GND
NC -> nothing (both of its NC pins)
SIGNAL -> GPIO 4 on an ESP32, GPIO 5 on an ESP32-S3,
GP15 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, math, sys and time are built in.
"""
import math
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
SENSOR_PIN = 4
# The TK01's SIGNAL. ESP32: 4. ESP32-S3: 5. Pico: 15.
LED_PIN = 5
ALARM_C = 28.0 # on at or above this
HYST_C = 1.0 # off only below ALARM_C - HYST_C
SAMPLES = 32
R_FIXED = 10000 # the 10 kOhm from VCC to SIGNAL
R25 = 10000 # the thermistor at 25 C: the "103"
B = 3950 # its B value: the "3950"
T0 = 298.15 # 25 C in kelvin
VCC = 3.3 # ESP32 only: your 3V3 pin, measured
adc = ADC(Pin(SENSOR_PIN))
ESP = sys.platform == "esp32" # ESP32 and ESP32-S3
if ESP:
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
led = Pin(LED_PIN, Pin.OUT)
def thermistor_ohms():
total = 0
for _ in range(SAMPLES):
if ESP:
total += adc.read_uv() / 1000000 / VCC
else:
total += adc.read_u16() / 65535
ratio = total / SAMPLES
if ratio <= 0 or ratio >= 1:
return None
return R_FIXED * ratio / (1 - ratio)
def celsius(ohms):
return 1 / (1 / T0 + math.log(ohms / R25) / B) - 273.15
alarm_on = False
while True:
r = thermistor_ohms()
if r is None:
print("SIGNAL at 0: check VCC. At the top: check GND.")
time.sleep_ms(500)
continue
c = celsius(r)
if not alarm_on and c >= ALARM_C:
alarm_on = True
if alarm_on and c < ALARM_C - HYST_C:
alarm_on = False
led.value(1 if alarm_on else 0)
print("%.2f C%s" % (c, " ALARM" if alarm_on else ""))
time.sleep_ms(250)Set HYST_C to 0 to watch it flicker near the line. LED_PIN is GPIO 5 on the ESP32-S3 because GPIO 4 is the thermistor. Stop it with Ctrl-C; the LED stays as it was, so switch it off in the shell with led.value(0).
When it does not work
Hold a fingertip on the thermistor. A fingertip is warmer than most rooms, and the reading climbs past 28 °C within several seconds. If your room is already above 28 °C, raise ALARM_C a few degrees until the LED starts off.
Wider than the flicker, narrower than you care about. With 32 readings averaged the flicker is a small fraction of a degree, so one degree is ample. A heater or a fan that should not switch every few seconds wants several degrees.
Check the TK01 alone first: its SIGNAL on LED_PIN, its GND on GND, and a sketch that just sets LED_PIN HIGH. Then open the serial monitor and see what temperature the sketch thinks it is. If the reading is right and below 28, warm it more or lower ALARM_C.
Through a relay block or a transistor, yes, and there hysteresis matters more: a relay that chatters wears out its contacts. Never power a motor or a heater from the pin itself.
Edit this page — content/books/ntc-thermistor/a-temperature-alarm.mdx
Questions about this product
See what other owners have asked, and read their solutions.
NTC Thermistor
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.