Skip to Content

← Water Pump Control with an ESP32 PLC and Float Switches

Water pumping (sanitation)ESP32 PLC 14GPIOControl

Water Pump Control with an ESP32 PLC and Float Switches — full example

Complete Arduino example: automatic water pump control with float level switches, fault detection and a state machine on an industrial ESP32 PLC.

Complete, runnable program for the ESP32 PLC 14 (pump-state-machine-float-switches.ino): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

/*
 * COMPLETE EXAMPLE — Pump control with level float switches (state machine)
 *
 * Hardware: ESP32 PLC 14 (Industrial Shields)
 * Based on: water pumping project (sanitation), bombament-1b-14.ino
 *
 * Wiring:
 *   I0_0  Minimum-level float switch  (NO, closed = water above minimum)
 *   I0_1  Maximum-level float switch  (NO, closed = water above maximum)
 *   I0_2  Overlevel / alarm float switch (NO)
 *   I0_3  Pump contactor auxiliary contact (running feedback)
 *   I0_4  Thermal relay contact       (closed = fault)
 *   I0_5  Error reset push button
 *   Q0_0  Pump contactor command
 *   Q0_1  Alarm pilot light
 *
 * Logic:
 *   OFF -> ON  when minimum AND maximum are active (tank full to the top)
 *   ON  -> OFF when minimum is lost (tank emptied)
 *   While running, contactor feedback and thermal relay are monitored:
 *   any failure latches ERROR (pump stopped + pilot light) until manual reset.
 *
 * Integration: the `data[]` array is ready to be sent over whichever protocol
 * applies — see the MQTT / LoRaWAN / Modbus templates in the catalog.
 */

// --- I/O map (ESP32 PLC 14 pins, industrialshields-arduino library)
#define I_FLOAT_MIN       I0_0
#define I_FLOAT_MAX       I0_1
#define I_FLOAT_OVERFLOW  I0_2
#define I_CONFIRMATION    I0_3
#define I_THERMAL         I0_4
#define I_RESET           I0_5
#define Q_PUMP            Q0_0
#define Q_PILOT           Q0_1

// --- Machine states
enum State { OFF_S, ON_S, ERROR_S };
State state = OFF_S;

// --- Telemetry variables (same order as the frame in the original project)
enum { MIN_LEVEL, MAX_LEVEL, OVERFLOW_LEVEL, RUNNING, FAULT, CONF_FAULT, ERROR_F, DATA_MAX };
bool data[DATA_MAX];

// Contactor feedback takes a while to arrive: allow a margin before declaring failure
const uint32_t T_CONFIRMATION_MS = 2000;
uint32_t t_start = 0;

void setup() {
  Serial.begin(115200);

  pinMode(I_FLOAT_MIN, INPUT);
  pinMode(I_FLOAT_MAX, INPUT);
  pinMode(I_FLOAT_OVERFLOW, INPUT);
  pinMode(I_CONFIRMATION, INPUT);
  pinMode(I_THERMAL, INPUT);
  pinMode(I_RESET, INPUT);
  pinMode(Q_PUMP, OUTPUT);
  pinMode(Q_PILOT, OUTPUT);

  digitalWrite(Q_PUMP, LOW);
  digitalWrite(Q_PILOT, LOW);
}

void loop() {
  // 1. Read inputs
  bool minLvl  = digitalRead(I_FLOAT_MIN);
  bool maxLvl  = digitalRead(I_FLOAT_MAX);
  bool ovf     = digitalRead(I_FLOAT_OVERFLOW);
  bool confirm = digitalRead(I_CONFIRMATION);
  bool thermal = digitalRead(I_THERMAL);
  bool reset   = digitalRead(I_RESET);

  // 2. State machine
  switch (state) {

    case OFF_S:
      if (minLvl && maxLvl) {            // tank full: start
        digitalWrite(Q_PUMP, HIGH);
        t_start = millis();
        state = ON_S;
      }
      break;

    case ON_S:
      if (!minLvl) {                     // tank emptied: stop
        digitalWrite(Q_PUMP, LOW);
        state = OFF_S;
        break;
      }
      if (thermal) {                     // thermal relay tripped
        data[FAULT] = true;
        enterError();
        break;
      }
      // no contactor feedback once the start-up margin has elapsed
      if (!confirm && millis() - t_start > T_CONFIRMATION_MS) {
        data[CONF_FAULT] = true;
        enterError();
      }
      break;

    case ERROR_S:
      if (reset) {                       // manual reset
        data[FAULT] = data[CONF_FAULT] = data[ERROR_F] = false;
        digitalWrite(Q_PILOT, LOW);
        state = OFF_S;
      }
      break;
  }

  // 3. Update telemetry
  data[MIN_LEVEL] = minLvl;
  data[MAX_LEVEL] = maxLvl;
  data[OVERFLOW_LEVEL] = ovf;
  data[RUNNING] = (state == ON_S);

  // 4. Periodic serial dump (replace with MQTT/LoRaWAN/Modbus sending)
  static uint32_t t_log = 0;
  if (millis() - t_log > 5000) {
    t_log = millis();
    Serial.printf("state=%d min=%d max=%d ovf=%d run=%d fault=%d conf=%d err=%d\n",
                  state, minLvl, maxLvl, ovf,
                  data[RUNNING], data[FAULT], data[CONF_FAULT], data[ERROR_F]);
  }

  delay(100);   // 100 ms control cycle, same as the original project
}

void enterError() {
  digitalWrite(Q_PUMP, LOW);
  digitalWrite(Q_PILOT, HIGH);
  data[ERROR_F] = true;
  state = ERROR_S;
}
Download the full project pack — freeThis example + the related ones + bill of materials