Safe solenoid valve sequencing on an ESP32 PLC with a state machine
One function owns the outputs
setMotion() function is the only place in the entire firmware where the general, load and unload valve outputs are written. Buttons, BLE commands and safety timeouts all request a state instead of touching pins directly, and every state writes all five outputs in one block. This makes the valve truth table reviewable at a glance and turns "why did both valves open at once?" from a debugging nightmare into an impossible question.Physical buttons and remote control coexist
[M]1*, [M]2* and [M]0* frames sent from a smartphone app. Because both input paths converge on the same state machine, remote and local control can never disagree about what the valves are doing — and the physical stop button always works, even with the app connected.Timeouts as a safety net
A snippet from the implementation
Straight from the example as deployed on the ESP32 PLC 38R — copy it freely:
void setup() {
Serial.begin(115200);
pinMode(BTN_LOAD, INPUT);
pinMode(BTN_UNLOAD, INPUT);
pinMode(BTN_STOP, INPUT);
pinMode(OUTPUT_EVG, OUTPUT);
pinMode(OUTPUT_EVD, OUTPUT);
pinMode(OUTPUT_EVC, OUTPUT);
pinMode(LED_LOAD, OUTPUT);
pinMode(LED_UNLOAD, OUTPUT);
setMotion(MOTION_NONE); // always start in safe idle
Serial.println("Solenoid valve control ready (ESP32 PLC 38R)");
}The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.
Frequently asked questions
Can the ESP32 PLC 38R drive solenoid valves directly?
Yes, its relay outputs switch typical 12/24 V DC hydraulic solenoid coils directly. Check the coil current against the relay rating and add flyback suppression for inductive loads if not already fitted.
Why use a state machine instead of toggling outputs directly?
Because every output combination becomes explicit and reviewable. With direct toggling, two code paths can disagree and leave conflicting valves open; with a state machine, illegal combinations are unreachable by construction.
How do I add a new movement, like a slow-speed mode?
Add a new enum value and one case in setMotion() defining its exact valve combination. No other code changes — buttons or BLE commands just request the new state.