Run a sampled PID loop against a simulated plant
Observe a 100 ms PID controller drive an LED PWM while a first-order plant model responds in firmware to setpoint and disturbance changes.

Gateway Gus: I want you to keep the simulated-plant boundary visible and explain every output change from setpoint, measurement, and stored controller state.
Predict the reading, then compare it with the measurement.
Wokwi ESP32
Third party ToolOpen the ESP32 editor, paste diagram.json, then paste sketch.ino.
Open Wokwi to paste in the files (new tab)Get the files
Use both prepared files. This is a paste-in setup; saving a project requires a Wokwi account.
sketch.ino
- Use the launch button above to open the ESP32 editor in Wokwi.
- Select the editor’s diagram.json tab and replace all its text with the supplied diagram.json.
- Select the sketch.ino tab, replace all its text with the supplied sketch.ino, then click Start Simulation.
Steps
Step 1
- Do
- Paste the supplied diagram and sketch into Wokwi; inspect the potentiometer, disturbance button, and LED PWM output.
- You will see
- Power: setpoint VCC connects to ESP32 3.3 V; GND returns to ground (diagram.json line 12). Setpoint signal connects to ESP32 GPIO34 (diagram.json line 12). Disturbance button connects ESP32 GPIO13 to ground when pressed (diagram.json line 13). Output LED anode connects to ESP32 GPIO2; its cathode returns through r1 to ground (diagram.json line 14). The output LED’s series resistor r1 is 220 Ω (diagram.json line 8).
- Why it matters
- GPIO34 supplies the requested setpoint, GPIO13 injects the later disturbance, and GPIO2 exposes controller output through the LED. The resistor limits LED current.

Step 1 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- In Wokwi's sketch editor, inspect the 100 ms schedule, anti-windup guard, and first-order plant update in the sketch.
- You will see
- Set sample interval and plant time constant:
const float SAMPLE_S=0.1f, PLANT_TAU_S=1.2f;(sketch.ino line 4). Set the PID gains:const float KP=2.0f, KI=0.9f, KD=0.08f;(sketch.ino line 5). Detect saturation in the error direction:bool pushingFurther=(output>=100.0f && error>0) || (output<=0.0f && error<0);(sketch.ino line 33). Update the integral only when allowed:if (!pushingFurther) integral=candidate;(sketch.ino line 34). Describe the software plant equation:// Software-in-the-loop plant: y[k+1] = y[k] + dt/tau * (u-y[k]).(sketch.ino line 37). Advance the simulated plant:measured += (SAMPLE_S/PLANT_TAU_S)*(output-measured);(sketch.ino line 38). - Why it matters
- The sample period and plant time constant define the later response. The saturation guard prevents the integral from growing farther into a limit, while the first-order update advances the simulated measured value.

Step 2 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- In the Simulation toolbar, start the run and read the Serial Monitor heading.
- You will see
- Input: run the supplied PID sketch in Wokwi. Observed: "PID LOOP ESP32 READY". Observed: "MODEL=first_order_in_firmware sample_ms=100 tau_s=1.2 output=LED_PWM". Expected: the model boundary and sample period are stated at startup.
- Why it matters
- The plant is modeled by firmware in this run. The declared schedule is 100 ms. The declared time constant is 1.2 s. The LED PWM is an output indicator, not a physical plant measurement.

Step 3 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- In the Serial Monitor panel, follow the first controller sample rows.
- You will see
- Input: leave the supplied potentiometer setting unchanged. Observed: "setpoint=68.4 measured=52.4 output=62.3 saturated=NO model=SIMULATED". Observed later: "setpoint=68.4 measured=55.8 output=61.9 saturated=NO model=SIMULATED". Expected: setpoint, measurement, command and saturation in each row.
- Why it matters
- The setpoint remains 68.4 in both rows. The modeled measurement rises from 52.4 to 55.8. The output changes from 62.3 to 61.9. Both rows declare their simulated-model status.

Step 4 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- In the Serial Monitor panel, let the modeled measurement approach the potentiometer setpoint.
- You will see
- Input: leave the setpoint at 68.4. Observed: "setpoint=68.4 measured=63.5 output=65.4 saturated=NO model=SIMULATED". Observed later: "setpoint=68.4 measured=64.3 output=65.9 saturated=NO model=SIMULATED". Expected: measured rises toward the unchanged setpoint.
- Why it matters
- The modeled measurement rose from 63.5 to 64.3. The setpoint remained 68.4. The command changed slightly as the error narrowed. The rows explicitly label the plant as simulated.

Step 5 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- On the Wokwi circuit canvas, press Disturb plant once.
- You will see
- Input: press Disturb plant once on the Wokwi circuit canvas. Observed before: "setpoint=68.4 measured=59.8 output=63.2 saturated=NO model=SIMULATED". Observed: "DISTURBANCE applied=-25.0 to simulated plant". Observed after: "setpoint=68.4 measured=40.2 output=100.0 saturated=YES model=SIMULATED".
- Why it matters
- The disturbance lowered the modeled measurement from 59.8 to 40.2. The setpoint stayed at 68.4. The controller drove the output to its 100.0 limit. The monitor explicitly marks the plant as simulated.

Step 6 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab) Step 7
- Do
- In Wokwi's Serial Monitor, continue for at least twelve samples after the disturbance.
- You will see
- Input: continue for twelve or more 100 ms samples after the disturbance. Observed: "setpoint=68.4 measured=58.0 output=75.7 saturated=NO model=SIMULATED". Observed later: "setpoint=68.4 measured=62.6 output=69.2 saturated=NO model=SIMULATED". Expected: measured moves back toward the unchanged 68.4 setpoint.
- Why it matters
- The modeled measurement rose from 58.0 to 62.6. The output fell from 75.7 to 69.2 as error narrowed. Both rows report no saturation. These serial samples show recovery, not a physical plant measurement.

Step 7 · Wokwi ESP32; numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
A fan must hold cabinet temperature near a target while heat load changes when processors wake and sleep. Which control shape is the best first loop choice?
Return to the chapter’s knowledge checkA refrigerated cabinet runs a fixed fan schedule, but door openings now vary by hour. The cloud service can suggest setpoints, yet network delays are sometimes several seconds. Which design record best fits the risk?
Return to the chapter’s knowledge check