← Estación de dos bombas con telemetría LoRaWAN en PLC ESP32
Bombeo de agua (saneamiento)ESP32 PLC 38ARGPIOControl
Estación de dos bombas con telemetría LoRaWAN en PLC ESP32 — ejemplo completo
Monta una estación de dos bombas con alternancia automática, failover y telemetría LoRaWAN en un ESP32 PLC 38AR. Código Arduino completo con tareas FreeRTOS.
Programa completo y ejecutable para el ESP32 PLC 38AR (dual-pump-control-lorawan-38ar.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 — Dual pump control with alternation + LoRaWAN telemetry
*
* Hardware: ESP32 PLC 38AR (Industrial Shields) + LoRa RN2xx3 module
* Based on: water pumping project (sanitation), bombament-2b-38ar.ino
*
* Wiring:
* I0_0 Minimum-level float switch I0_5 Manual selector pump 2
* I0_1 Maximum-level float switch I0_6 Contactor feedback P1
* I0_2 Overlevel float switch I0_7 Contactor feedback P2
* I0_3 Reset push button I0_8 Thermal relay P1 (closed = fault)
* I0_4 Manual selector pump 1 I0_9 Thermal relay P2 (closed = fault)
* I0_11 4-20 mA level sensor
* Q0_0 Pump 1 contactor Q0_1 Pump 2 contactor Q0_2 Alarm pilot light
*
* Logic:
* Alternation: every fill cycle starts the pump opposite to the previous one,
* to equalize running hours. If the active pump fails (thermal relay or
* missing feedback), it automatically switches to the other one and flags
* the error. Manual mode via selector. Telemetry packed into 4 bytes over
* LoRaWAN every 60 s using an independent FreeRTOS task.
*/
#define I_MIN I0_0
#define I_MAX I0_1
#define I_OVERLVL I0_2
#define I_RESET I0_3
#define I_MAN_P1 I0_4
#define I_MAN_P2 I0_5
#define I_CONF_P1 I0_6
#define I_CONF_P2 I0_7
#define I_THERM_P1 I0_8
#define I_THERM_P2 I0_9
#define I_SENSOR I0_11
#define Q_P1 Q0_0
#define Q_P2 Q0_1
#define Q_PILOT Q0_2
enum State { IDLE, PUMPING, ERROR_S };
State state = IDLE;
uint8_t activePump = 1; // alternation: the next one to start
bool errorP1 = false, errorP2 = false;
uint16_t sensorLevel = 0;
const uint32_t T_CONFIRMATION_MS = 2000;
uint32_t tStart = 0;
void startPump(uint8_t p) {
digitalWrite(p == 1 ? Q_P1 : Q_P2, HIGH);
tStart = millis();
}
void stopAll() { digitalWrite(Q_P1, LOW); digitalWrite(Q_P2, LOW); }
// ------------------------------------------------- Control task (100 ms)
void vTaskControl(void *pv) {
for (;;) {
bool minLvl = digitalRead(I_MIN), maxLvl = digitalRead(I_MAX);
sensorLevel = analogRead(I_SENSOR); // 4-20 mA -> 0-1023
switch (state) {
case IDLE:
if (digitalRead(I_MAN_P1)) { startPump(1); state = PUMPING; activePump = 1; }
else if (digitalRead(I_MAN_P2)) { startPump(2); state = PUMPING; activePump = 2; }
else if (minLvl && maxLvl) { // automatic start with alternation
activePump = (activePump == 1) ? 2 : 1;
if ((activePump == 1 && errorP1) || (activePump == 2 && errorP2))
activePump = (activePump == 1) ? 2 : 1; // skip faulty pump
startPump(activePump);
state = PUMPING;
}
break;
case PUMPING: {
if (!minLvl) { stopAll(); state = IDLE; break; } // emptying finished
bool thermal = digitalRead(activePump == 1 ? I_THERM_P1 : I_THERM_P2);
bool conf = digitalRead(activePump == 1 ? I_CONF_P1 : I_CONF_P2);
bool failure = thermal || (!conf && millis() - tStart > T_CONFIRMATION_MS);
if (failure) {
if (activePump == 1) errorP1 = true; else errorP2 = true;
stopAll();
digitalWrite(Q_PILOT, HIGH);
uint8_t other = (activePump == 1) ? 2 : 1;
bool errOther = (other == 1) ? errorP1 : errorP2;
if (!errOther) { activePump = other; startPump(other); } // switch over
else state = ERROR_S; // both KO
}
break;
}
case ERROR_S:
if (digitalRead(I_RESET)) {
errorP1 = errorP2 = false;
digitalWrite(Q_PILOT, LOW);
state = IDLE;
}
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
// ------------------------------------------------- LoRaWAN task (60 s)
// 4-byte frame: inputs (11 bits) + outputs (2) + errors (2) + sensor (10 bits)
void vTaskLora(void *pv) {
for (;;) {
byte msg[4] = {0, 0, 0, 0};
const int pins[] = {I_MIN, I_MAX, I_OVERLVL, I_RESET, I_MAN_P1, I_MAN_P2,
I_CONF_P1, I_CONF_P2, I_THERM_P1, I_THERM_P2};
for (uint8_t i = 0; i < 10; i++)
msg[i / 8] |= digitalRead(pins[i]) << (7 - i % 8);
msg[1] |= digitalRead(Q_P1) << 5;
msg[1] |= digitalRead(Q_P2) << 4;
msg[1] |= errorP1 << 3;
msg[1] |= errorP2 << 2;
msg[2] = (sensorLevel >> 8) & 0x03;
msg[3] = sensorLevel & 0xFF;
lora_send_bytes(msg, 4); // see LoRaWAN module in the catalog
vTaskDelay(60000 / portTICK_PERIOD_MS);
}
}
// Hourly OTAA re-join: if the link drops, it recovers by itself (comms watchdog)
void vTaskRejoin(void *pv) {
for (;;) {
vTaskDelay(3600000 / portTICK_PERIOD_MS);
lora_join_otaa("APP_EUI", "APP_KEY"); // credentials: placeholders
}
}
void setup() {
Serial.begin(115200);
const int ins[] = {I_MIN, I_MAX, I_OVERLVL, I_RESET, I_MAN_P1, I_MAN_P2,
I_CONF_P1, I_CONF_P2, I_THERM_P1, I_THERM_P2};
for (int p : ins) pinMode(p, INPUT);
pinMode(Q_P1, OUTPUT); pinMode(Q_P2, OUTPUT); pinMode(Q_PILOT, OUTPUT);
stopAll();
lora_init(57600); // RN2xx3 via SerialSC1
lora_join_otaa("APP_EUI", "APP_KEY");
xTaskCreate(vTaskControl, "control", 4096, NULL, 3, NULL);
xTaskCreate(vTaskLora, "lora", 4096, NULL, 1, NULL);
xTaskCreate(vTaskRejoin, "rejoin", 4096, NULL, 2, NULL);
}
void loop() { vTaskDelay(portMAX_DELAY); } // everything lives in FreeRTOS tasks
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales