Six sketches, zero libraries to install — all six compile for arduino:avr:uno with arduino-cli; the five relay sketches enforce a 180-second minimum off-time (MIN_OFF_MS, a const you can change) so a bare hysteresis loop cannot chatter your relay or compressor, and the PWM sketch latches off at MAX_SAFE_C instead.
arduino:avr:uno
The plain two-point thermostat done properly: a 10k NTC thermistor, one relay, a real dead-band, an anti-short-cycle timer and a latch that shuts the load off when the probe fails.
This is the file most people are actually looking for when they search for thermostat code, plus the two things a tutorial version leaves out. First, a minimum off-time (MIN_OFF_MS, 180 s) and a minimum on-time (MIN_ON_MS, 60 s): a bare hysteresis loop with a noisy probe can switch a relay repeatedly within a minute, which is exactly what kills a compressor. Second, a sensor-fault latch: an open or shorted thermistor reads at the very top or bottom of the 10-bit ADC range, which a naive sketch happily interprets as 'freezing' and heats forever — here three consecutive out-of-range readings lock the relay off until power is cycled. Wiring: 5V through the NTC to the A0 junction, then a 10k 1% resistor to GND; D8 to the relay module input; D13 is the on-board LED showing the heat call. Change SETPOINT_C, HYSTERESIS_C and the NTC_* constants for your part; set RELAY_ACTIVE_LOW to false if your relay board switches on a HIGH input. No delay() in the control path — the sampling and the timers use millis(), so the switch timers stay honest even if you add printing.
| Pin | Mode | Connect to |
|---|---|---|
A0 | INPUT | NTC divider midpoint (5V - NTC - A0 - 10k - GND) |
8 | OUTPUT | Relay module IN (heat call) |
13 | OUTPUT | On-board LED, lit while heating |
| Name | Value | Unit |
|---|---|---|
SETPOINT_C | 21.0 | deg C |
HYSTERESIS_C | 0.8 | deg C, half dead-band |
SERIES_RESISTOR | 10000.0 | ohm |
NTC_NOMINAL_R | 10000.0 | ohm at NTC_NOMINAL_C |
NTC_NOMINAL_C | 25.0 | deg C |
NTC_BETA | 3950.0 | K, datasheet B25/50 |
ADC_FAULT_LOW | 15 | raw ADC |
ADC_FAULT_HIGH | 1008 | raw ADC |
FAULT_SAMPLES | 3 | consecutive readings |
ADC_SAMPLES | 8 | readings averaged |
MIN_OFF_MS | 180000 | ms, anti-short-cycle |
MIN_ON_MS | 60000 | ms |
SAMPLE_MS | 1000 | ms |
RELAY_ACTIVE_LOW | 1 | 1 = relay switches on a LOW input |
SERIAL_BAUD | 9600 | baud |
A thermostat you can adjust without a computer: LM35 probe, a 10k potentiometer as the setpoint dial, and a dial filter so a noisy wiper cannot shift the target every second.
The same relay control as sketch 1, but the setpoint comes from a knob so the finished box needs no laptop. The device a tutorial omits here is the dial filter: a raw analogRead of a cheap potentiometer jitters by several counts, and if you map that straight onto degrees the target wanders and the relay follows it. This file only accepts a new dial position after it has moved more than POT_DEADBAND_COUNTS AND held still for POT_STABLE_MS (800 ms) — a debounce for an analog input. The setpoint is also clamped between SETPOINT_MIN_C and SETPOINT_MAX_C so the knob cannot ask for a temperature your build is not safe at, and a reading outside SENSOR_MIN_VALID_C..SENSOR_MAX_VALID_C latches the relay off. Wiring: LM35 +Vs to 5V, GND to GND, OUT to A1; potentiometer ends to 5V and GND with the wiper to A2; D7 to the relay input. Change SETPOINT_MIN_C / SETPOINT_MAX_C to set the range of your dial.
| Pin | Mode | Connect to |
|---|---|---|
A1 | INPUT | LM35 OUT (10 mV per degree C) |
A2 | INPUT | Setpoint potentiometer wiper (10k, ends to 5V and GND) |
7 | OUTPUT | Relay module IN (heat call) |
13 | OUTPUT | On-board LED, lit while heating |
| Name | Value | Unit |
|---|---|---|
ADC_REF_MV | 5000.0 | mV, supply used as ADC reference |
LM35_MV_PER_C | 10.0 | mV per deg C |
SETPOINT_MIN_C | 5.0 | deg C, dial low end |
SETPOINT_MAX_C | 30.0 | deg C, dial high end |
HYSTERESIS_C | 0.5 | deg C, half dead-band |
SENSOR_MIN_VALID_C | -2.0 | deg C |
SENSOR_MAX_VALID_C | 90.0 | deg C |
POT_DEADBAND_COUNTS | 8 | raw ADC |
POT_STABLE_MS | 800 | ms the dial must hold still |
MIN_OFF_MS | 180000 | ms, anti-short-cycle |
MIN_ON_MS | 60000 | ms |
SAMPLE_MS | 500 | ms |
ADC_SAMPLES | 16 | readings averaged |
FAULT_SAMPLES | 4 | consecutive readings |
RELAY_ACTIVE_LOW | 1 | 1 = relay switches on a LOW input |
SERIAL_BAUD | 9600 | baud |
Proportional-plus-integral control of a resistive heater through a logic-level MOSFET, with anti-windup and a hard over-temperature latch — for an incubator, a reflow bed or a heated enclosure where on/off would overshoot.
A relay is wrong for a small resistive heater: on/off control overshoots and the mass oscillates. This sketch drives pin D9 with analogWrite into a logic-level N-MOSFET and closes a PI loop every CONTROL_MS (500 ms). Two devices a tutorial version does not have. (1) Anti-windup by conditional integration: the integral term is only updated while the output is inside the usable band, so a cold start does not pile up an enormous integral that keeps the heater at full power long past the setpoint. (2) A hard over-temperature latch: at or above MAX_SAFE_C, or if the sensor reads outside SENSOR_MIN_VALID_C..SENSOR_MAX_VALID_C, the duty goes to zero permanently, the fault LED blinks and only a power cycle clears it — a controller that has lost its probe must not be allowed to argue. PWM_MAX also caps the duty below 255 so a heater sized with margin cannot be driven to full power. Note on frequency: D9 runs at about 490 Hz, which is fine for a resistive load through a MOSFET and is NOT suitable for a mechanical relay — do not put a relay on this pin. Start with KP and KI as shipped, then raise KP until you see a small oscillation and back off.
| Pin | Mode | Connect to |
|---|---|---|
A0 | INPUT | LM35 OUT (10 mV per degree C) |
9 | OUTPUT | PWM to the gate resistor of a logic-level N-MOSFET |
12 | OUTPUT | Fault LED (blinks when latched off) |
| Name | Value | Unit |
|---|---|---|
SETPOINT_C | 55.0 | deg C |
MAX_SAFE_C | 70.0 | deg C, hard latch |
KP | 12.0 | PWM counts per deg C |
KI | 0.35 | PWM counts per deg C second |
INTEGRAL_LIMIT | 300.0 | deg C second |
PWM_MAX | 200 | PWM counts of 255 |
ADC_REF_MV | 5000.0 | mV |
LM35_MV_PER_C | 10.0 | mV per deg C |
SENSOR_MIN_VALID_C | -2.0 | deg C |
SENSOR_MAX_VALID_C | 120.0 | deg C |
CONTROL_MS | 500 | ms per control step |
BLINK_MS | 250 | ms fault LED half period |
ADC_SAMPLES | 16 | readings averaged |
SERIAL_BAUD | 9600 | baud |
Day, night and away setpoints on one relay: a millis-based minute clock, a night window that crosses midnight correctly, and a debounced away button.
Three targets on one thermostat. The clock is a minute counter driven by millis() and seeded with BOOT_MINUTE_OF_DAY, so you power the box on at a known time and it knows the hour without an RTC — the header says plainly that a ceramic resonator drifts, so this is for a heater, not for a watch; if you need calendar accuracy, add an RTC. The devices a tutorial omits: the minute counter advances in a while loop with unsigned subtraction, so it stays correct across the millis() rollover at about 49.7 days AND recovers the right number of minutes if the loop was ever delayed; the night window is evaluated with a test that handles a range crossing midnight (22:00 to 06:00) instead of the naive comparison that silently never fires; and the away button on D2 uses INPUT_PULLUP with a DEBOUNCE_MS filter so one press is one toggle, not five. Minimum on and off times apply to every mode change, so a setback transition cannot short-cycle the load. Change NIGHT_START_MIN / NIGHT_END_MIN (minutes past midnight) and the three setpoints.
| Pin | Mode | Connect to |
|---|---|---|
A0 | INPUT | NTC divider midpoint (5V - NTC - A0 - 10k - GND) |
2 | INPUT_PULLUP | Away button to GND (no external resistor) |
8 | OUTPUT | Relay module IN (heat call) |
13 | OUTPUT | On-board LED, lit while heating |
| Name | Value | Unit |
|---|---|---|
DAY_SETPOINT_C | 21.0 | deg C |
NIGHT_SETPOINT_C | 17.5 | deg C |
AWAY_SETPOINT_C | 12.0 | deg C |
HYSTERESIS_C | 0.6 | deg C, half dead-band |
NIGHT_START_MIN | 1320 | minutes past midnight (22:00) |
NIGHT_END_MIN | 360 | minutes past midnight (06:00) |
BOOT_MINUTE_OF_DAY | 420 | minutes past midnight at power on (07:00) |
MINUTES_PER_DAY | 1440 | minutes |
MS_PER_MINUTE | 60000 | ms |
DEBOUNCE_MS | 40 | ms button filter |
MIN_OFF_MS | 180000 | ms, anti-short-cycle |
MIN_ON_MS | 60000 | ms |
SAMPLE_MS | 1000 | ms |
SERIES_RESISTOR | 10000.0 | ohm |
NTC_NOMINAL_R | 10000.0 | ohm at NTC_NOMINAL_C |
NTC_NOMINAL_C | 25.0 | deg C |
NTC_BETA | 3950.0 | K |
ADC_FAULT_LOW | 15 | raw ADC |
ADC_FAULT_HIGH | 1008 | raw ADC |
ADC_SAMPLES | 8 | readings averaged |
SERIAL_BAUD | 9600 | baud |
A thermostat that notices when heating does nothing: if the temperature has not risen by MIN_RISE_C after a full STALL_WINDOW_MS of calling for heat, it locks the load off and raises an alarm — plus a rolling 24-hour runtime cap.
The failure a plain thermostat cannot see is the one where the relay clicks, the contactor never closes, or the door is open — the room never warms, so the sketch calls for heat forever. This file measures the rise: when the heat call starts it records the temperature, and every STALL_WINDOW_MS (10 minutes) it checks whether the reading gained at least MIN_RISE_C. If it did, the reference resets and heating continues. If it did not, the relay is locked off, D12 blinks and the Serial Monitor prints STALL LOCKOUT. The second device is a rolling runtime cap: total heating time is accumulated against a 24-hour window (DAY_MS) and once it exceeds DAILY_CAP_MS (12 hours) the relay is held off until the window rolls, so a sensor that drifted low cannot run a heater around the clock unnoticed. Both counters use unsigned millis() arithmetic and a while loop on the window boundary, so they survive the rollover. Calibrate MIN_RISE_C by watching one real heating cycle in the Serial Monitor and using the rise your enclosure actually achieves in ten minutes.
| Pin | Mode | Connect to |
|---|---|---|
A0 | INPUT | NTC divider midpoint (5V - NTC - A0 - 10k - GND) |
8 | OUTPUT | Relay module IN (heat call) |
12 | OUTPUT | Alarm LED (blinks on stall lockout) |
13 | OUTPUT | On-board LED, lit while heating |
| Name | Value | Unit |
|---|---|---|
SETPOINT_C | 21.0 | deg C |
HYSTERESIS_C | 0.8 | deg C, half dead-band |
STALL_WINDOW_MS | 600000 | ms (10 minutes) of heating per check |
MIN_RISE_C | 0.5 | deg C expected per window |
DAILY_CAP_MS | 43200000 | ms (12 hours) of heating per day |
DAY_MS | 86400000 | ms rolling window |
MIN_OFF_MS | 180000 | ms, anti-short-cycle |
MIN_ON_MS | 60000 | ms |
SAMPLE_MS | 1000 | ms |
BLINK_MS | 250 | ms alarm LED half period |
SERIES_RESISTOR | 10000.0 | ohm |
NTC_NOMINAL_R | 10000.0 | ohm at NTC_NOMINAL_C |
NTC_NOMINAL_C | 25.0 | deg C |
NTC_BETA | 3950.0 | K |
ADC_FAULT_LOW | 15 | raw ADC |
ADC_FAULT_HIGH | 1008 | raw ADC |
ADC_SAMPLES | 8 | readings averaged |
SERIAL_BAUD | 9600 | baud |
Change the setpoint and dead-band over USB without recompiling — with range validation on every command and an override that expires by itself.
Tuning by editing a constant and re-uploading wastes an afternoon. This sketch reads line commands from the Serial Monitor at 9600 baud (set the line ending to Newline): SET 21.5 changes the base setpoint, HYS 0.6 changes the dead-band, OVR 24 applies a temporary override, OFF releases it, and ? prints the status. The devices a tutorial omits: every value is range-checked against its MIN/MAX const and rejected with an ERR line rather than silently accepted — a fat-fingered SET 210 cannot become the target — and the override is not permanent. It expires after OVERRIDE_TIMEOUT_MS (1 hour) and the thermostat returns to its base setpoint by itself, so a remote 'boost' left running by accident cannot heat a room all week. The serial reader is non-blocking: characters are collected into a fixed buffer and only a completed line is parsed, an over-long line is discarded with an error, and the control loop keeps running while you type. The setpoint lives in RAM, so a power cut returns to SETPOINT_DEFAULT_C — the header says so plainly rather than pretending otherwise.
| Pin | Mode | Connect to |
|---|---|---|
A0 | INPUT | NTC divider midpoint (5V - NTC - A0 - 10k - GND) |
8 | OUTPUT | Relay module IN (heat call) |
13 | OUTPUT | On-board LED, lit while heating |
| Name | Value | Unit |
|---|---|---|
SETPOINT_DEFAULT_C | 21.0 | deg C at power on |
SETPOINT_MIN_C | 5.0 | deg C, accepted range low |
SETPOINT_MAX_C | 30.0 | deg C, accepted range high |
HYSTERESIS_DEFAULT_C | 0.8 | deg C |
HYST_MIN_C | 0.2 | deg C |
HYST_MAX_C | 3.0 | deg C |
OVERRIDE_TIMEOUT_MS | 3600000 | ms (1 hour) before override expires |
CMD_BUF_LEN | 24 | characters per command line |
MIN_OFF_MS | 180000 | ms, anti-short-cycle |
MIN_ON_MS | 60000 | ms |
SAMPLE_MS | 1000 | ms |
SERIES_RESISTOR | 10000.0 | ohm |
NTC_NOMINAL_R | 10000.0 | ohm at NTC_NOMINAL_C |
NTC_NOMINAL_C | 25.0 | deg C |
NTC_BETA | 3950.0 | K |
ADC_FAULT_LOW | 15 | raw ADC |
ADC_FAULT_HIGH | 1008 | raw ADC |
ADC_SAMPLES | 8 | readings averaged |
SERIAL_BAUD | 9600 | baud |
This page is the working piece. The full pack has everything below.
검로드 1리스팅 · 5개국어 상세 · ★소유 · 전체 깊이 · 오프라인 · 기계가 먹는 파일 · 가격=⛔상수 금지 — ★측정된 경쟁 밴드(L4)
Send it to me