← SHEET 02 · ASSEMBLIES EL-003

PID Temperature Controller

ELCONTROL
LIVE DRAWING — HOVER OR DRAG TO CRANK · BUILT FROM THE REAL PLANT PARAMETERS
PART NOEL-003
MATL / SYSTEMARDUINO NANO · NTC 100K · SSR
TOOLSArduino IDE · Wokwi · C++

Same control problem as the Arduino PID motor rig and the RL-control series — closed-loop feedback with saturation and noise to manage — but swapped from position to temperature, and written as embedded firmware instead of a Python/Gymnasium sim. No PID library: the controller, the anti-windup, and the derivative filtering are all hand-rolled so the gain structure is fully inspectable, the same discipline as the motor rig’s discrete PID.

OVERVIEW & MOTIVATION

A bench temperature loop: hold a setpoint against a slow, saturating, noisy plant. Temperature control differs from the motor rig’s position loop in three ways that shape the firmware — the actuator (SSR) can’t be finely modulated so it needs a time-proportional (slow-PWM) drive instead of true PWM; the sensor is a nonlinear NTC thermistor needing a Steinhart-Hart-style linearization; and getting it wrong is a fire risk, not just an overshoot, so the cutoff logic is as much a design requirement as the control loop itself. The target plant is a small resistive heater plate (hotplate / preheat-bed class), driven from an Arduino Nano, with a rotary encoder for setpoint entry and an OLED for live status.

COMPONENTS & BOM

REFCOMPONENTSPECROLE
U1Arduino NanoATmega328, 5 V logicruns the PID loop, drives the SSR window
RT1NTC thermistor100 kΩ @ 25 °C, beta ≈ 3950temperature sensing element
R1Reference resistor100 kΩ, 1%forms the sense divider with RT1
K1Solid-state relay (SSR)zero-cross, rated above heater currentswitches heater power from a logic-level input
DISP1SSD1306 OLED128×64, I2C, addr 0x3Cshows setpoint / actual / output % / fault
ENC1Rotary encoderKY-040 module (CLK/DT/SW)setpoint entry + step-size toggle + fault-reset
LED1Fault indicator LED5 mm + 220 Ω series Rlit while a safety cutoff is latched
Heater elementresistive, matches SSR/supply ratingthe actual plant (real build only)

WIRING

WIRING — PID TEMPERATURE CONTROLLER (EL-003) NTC 100K DIVIDER SSD1306 OLED ARDUINO NANO SSR / RELAY (SIM) ROTARY ENCODER FAULT LED · D6 · 220R OUTA0 VCC5V GNDGND IND3 VCC5V GNDGND
<text x="412" y="53">SDA</text><text x="365" y="153">A4</text>
<text x="412" y="65">SCL</text><text x="365" y="165">A5</text>
<text x="412" y="77">VCC</text><text x="365" y="177">5V</text>
<text x="412" y="89">GND</text><text x="365" y="189">GND</text>

<text x="412" y="249">CLK</text><text x="365" y="201">D2</text>
<text x="412" y="260">DT</text><text x="365" y="213">D4</text>
<text x="412" y="271">SW</text><text x="365" y="225">D5</text>
<text x="412" y="282">VCC</text><text x="365" y="237">5V</text>
<text x="412" y="293">GND</text><text x="365" y="249">GND</text>

<text x="326" y="292">D6</text>
<text x="112" y="296">COM/NO</text>
⚠ MAINS — 230V/120V HEATER SIDE QUALIFIED WIRING ONLY — NOT SIMULATED SIGNAL = BLUE · POWER = MAROON
MCU PINNETPERIPHERAL PIN
A0THERM_SENSENTC divider OUT
A4I2C_SDAOLED SDA
A5I2C_SCLOLED SCL
D2ENC_CLKEncoder CLK (INT0)
D3SSR_DRIVESSR / relay IN
D4ENC_DTEncoder DT
D5ENC_SWEncoder push button
D6FAULT_LEDFault LED (220 Ω to GND)
5V+5VNTC VCC, OLED VCC, encoder VCC, SSR/relay VCC
GNDGNDcommon return

CONTROL DESIGN

Three deliberate choices, none from a library:

Anti-windup by conditional integration. The integral only accumulates while the unclamped output isn’t already saturated in the same direction the error is pushing it — cheaper than back-calculation and sufficient for a slow thermal plant with a hard 0–100% output range.

double unclamped = pTerm + integralTerm + dTerm;
bool satHigh = unclamped > 100.0, satLow = unclamped < 0.0;
bool wouldGrowSaturation = (satHigh && error > 0.0) || (satLow && error < 0.0);
if (!wouldGrowSaturation) integralTerm += Ki * error * dt;

Derivative-on-measurement, not on error — the D term reacts to the plant’s rate of change, not to a setpoint step, so turning the knob doesn’t spike the output:

double dMeasurement = (measurement - prevMeasurement) / dt;
double dTerm = -Kd * dMeasurement;   // note: -Kd, since d(error)/dt = -d(measurement)/dt

Time-proportional output. An SSR can’t be PWM’d at kHz like a MOSFET gate without wearing out the contactor equivalent inside it, so the 0–100% PID output is converted into an on-time within a fixed 1 s window instead — the classic slow-PWM pattern for SSR-driven heaters:

unsigned long onTimeMs = (unsigned long)(outputPct / 100.0 * WINDOW_MS);
bool ssrOn = (now - windowStartMs) < onTimeMs;

SIMULATION

Wokwi project: Arduino Nano + wokwi-ntc-temperature-sensor + board-ssd1306 + wokwi-ky-040 + wokwi-relay-module (SSR stand-in) + a fault LED. Load sketch.ino and diagram.json from sources/electronics/pid-hotplate/ into a new Wokwi Arduino Nano project (add Adafruit SSD1306 and Adafruit GFX Library via the library manager), then run:

  1. Click the NTC part and drag its temperature slider — this is the plant disturbance the loop reacts to; the firmware reads it through the same divider math a real 100k NTC would use.
  2. Turn the encoder to move the setpoint; short-press toggles the step size (1 °C / 5 °C); long-press (≥1.5 s) clears a latched fault once the reading is sane again.
  3. Watch the OLED’s SP / PV / OUT % line and the relay LED to see the time-proportional window open and close as the loop converges.
  4. Drag the slider past TEMP_MAX_C (150 °C) to trip the overtemp cutoff, or unplug the NTC wire to see the sensor-fault cutoff latch the fault LED (D6).

Substitutions, noted honestly: the relay module stands in for an SSR (no SSR part exists in Wokwi’s library) — its IN pin is the same logic-level control signal a real SSR would take. Wokwi’s NTC module is a fixed 10 kΩ/10 kΩ divider internally; the firmware’s constants are set so the ratio still matches a real 100 kΩ/100 kΩ build (see the README for the derivation). There is no thermal link from the relay’s switched side back to the NTC in the simulator — closing the relay doesn’t heat the simulated sensor, only the temperature slider does.

STATUS

Design and simulation stage. The firmware, the Wokwi circuit, and the safety-cutoff logic are written and run in-simulator; no bench hardware has been built and no gains have been tuned against a real thermal plant. Kp/Ki/Kd in sketch.ino are reasonable starting points for a small resistive plate, not measured. Ahead: source the SSR and NTC, build the mains-isolated heater side, mount the sensor in thermal contact with the plate, and re-tune from a real step response.

USE CASES & APPLICATIONS

Time-proportional SSR-driven PID is the standard pattern for slow, saturating thermal loops: reflow/preheat plates, 3D-printer heated beds, lab incubators, and small industrial oven/kiln zones all use the same three-term structure with anti-windup and a slow-PWM output stage — the mains-side details differ, the control math doesn’t.

FILES

  • sources/electronics/pid-hotplate/sketch.ino — complete firmware; pinout and safety notes in the header comment.
  • sources/electronics/pid-hotplate/diagram.json — Wokwi circuit, importable directly.
  • sources/electronics/pid-hotplate/README.md — Wokwi run steps, sim-vs-real substitution table, and real-build notes (mains safety warning up front).

← BACK TO ASSEMBLIES

NAME ODILBEK MARIMOV
DWG NO. PF-2026
SHEET 01 / 07
DISCIPLINE ROBOTICS / MECHATRONICS
SCALE 1:1
REV A
THIRD-ANGLE PROJECTION
DATE 2026-07-11
UNITS mm