← Lectura de sensor de nivel 4-20 mA con un PLC ESP32
Bombeo de agua (saneamiento)ESP32 PLC 38ARGPIOAdquisición
Lectura de sensor de nivel 4-20 mA con un PLC ESP32 — ejemplo completo
Lee una sonda de nivel 4-20 mA en un PLC ESP32: escalado a cm, filtrado por media móvil, detección de cable roto y control de bomba con histéresis en Arduino.
Programa completo y ejecutable para el ESP32 PLC 38AR (level-sensor-4-20ma.ino): incluye cabecera de conexionado, requisitos y notas de integración.
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales
Vista de solo lectura.
/*
* COMPLETE EXAMPLE — Reading a 4-20 mA analog level sensor
*
* Hardware: ESP32 PLC 38AR (Industrial Shields)
* Based on: water pumping project (sanitation), bombament-2b-38ar.ino
*
* Wiring:
* I0_11 4-20 mA hydrostatic level sensor (2-wire, powered at 24 Vdc;
* the PLC analog input converts the current loop to 0-1023)
* I0_0 Overlevel float switch (NO) — backup independent of the sensor
* Q0_0 Drain pump contactor
* Q0_1 Alarm pilot light
*
* Logic:
* 1. Raw 0-1023 reading of I0_11 (4 mA ~ 0%, 20 mA ~ 100% of the scale).
* 2. Moving-average filter (8 samples) to remove current-loop ripple.
* 3. Broken-wire detection: below ~3.5 mA the loop is open (sensor
* disconnected or cut cable) — alarm and safe mode.
* 4. Conversion to centimeters of water column and hysteresis control:
* the pump starts above LEVEL_START and stops below LEVEL_STOP.
* The overlevel float switch acts as a hardwired backup.
*
* Integration: `level_cm` and `filtered_sensor` (0-1023) are ready to go into
* the 4-byte LoRaWAN frame — see ejemplos/bombeo-agua/lorawan-telemetry-bitpacking.ino
*/
// --- I/O map (ESP32 PLC 38AR pins, industrialshields-arduino library)
#define I_LEVEL_SENSOR I0_11
#define I_OVERLEVEL I0_0
#define Q_PUMP Q0_0
#define Q_PILOT Q0_1
// --- Sensor scale: transmitter from 0 to 250 cm of water column
const float SCALE_CM = 250.0; // level at 20 mA
const uint16_t RAW_4MA = 205; // approx. reading at 4 mA (1023 * 4/20)
const uint16_t RAW_20MA = 1023; // reading at 20 mA
const uint16_t RAW_BROKEN_WIRE = 180; // < ~3.5 mA: open loop
// --- Hysteresis control setpoints (in cm)
const float LEVEL_START = 180.0; // start the drain pump
const float LEVEL_STOP = 60.0; // stop the pump
// --- Moving-average filter
const uint8_t N_SAMPLES = 8;
uint16_t samples[N_SAMPLES];
uint8_t sample_index = 0;
uint16_t filtered_sensor = 0; // 0-1023, ready for telemetry
float level_cm = 0.0;
bool broken_wire = false;
bool pump = false;
// ------------------------------------------------- Reading + filtering
uint16_t read_filtered_sensor() {
samples[sample_index] = analogRead(I_LEVEL_SENSOR); // I0_11, 4-20mA -> 0-1023
sample_index = (sample_index + 1) % N_SAMPLES;
uint32_t sum = 0;
for (uint8_t i = 0; i < N_SAMPLES; i++) sum += samples[i];
return sum / N_SAMPLES;
}
// ------------------------------------------------- Scaling to physical units
float raw_to_cm(uint16_t raw) {
if (raw <= RAW_4MA) return 0.0; // below 4 mA there is no valid reading
return (raw - RAW_4MA) * SCALE_CM / (RAW_20MA - RAW_4MA);
}
void setup() {
Serial.begin(115200);
pinMode(I_LEVEL_SENSOR, INPUT);
pinMode(I_OVERLEVEL, INPUT);
pinMode(Q_PUMP, OUTPUT);
pinMode(Q_PILOT, OUTPUT);
digitalWrite(Q_PUMP, LOW);
digitalWrite(Q_PILOT, LOW);
// Preload the filter so it does not start with a zero average
uint16_t first = analogRead(I_LEVEL_SENSOR);
for (uint8_t i = 0; i < N_SAMPLES; i++) samples[i] = first;
}
void loop() {
filtered_sensor = read_filtered_sensor();
// --- Loop diagnostics: broken wire / disconnected sensor
broken_wire = (filtered_sensor < RAW_BROKEN_WIRE);
if (broken_wire) {
// Safe mode: with no reliable reading, the pump only obeys the overlevel
// float switch (hardwired backup independent of the sensor).
pump = digitalRead(I_OVERLEVEL);
digitalWrite(Q_PILOT, HIGH); // warn the operator
} else {
level_cm = raw_to_cm(filtered_sensor);
// Hysteresis control: two separate setpoints keep the pump from
// cycling in and out continuously around a single threshold.
if (level_cm > LEVEL_START) pump = true;
else if (level_cm < LEVEL_STOP) pump = false;
digitalWrite(Q_PILOT, digitalRead(I_OVERLEVEL)); // overlevel = alarm
}
digitalWrite(Q_PUMP, pump);
// Trace every second: raw, cm and state — useful to calibrate the scale on site
static uint32_t t_trace = 0;
if (millis() - t_trace >= 1000) {
t_trace = millis();
Serial.printf("raw=%u level=%.1f cm pump=%d broken_wire=%d\n",
filtered_sensor, level_cm, pump, broken_wire);
}
delay(100);
}
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales