Skip to Content

← Production Pulse Counter on an ESP32 PLC with Interrupts

Textile monitoring (weaving)ESP32 PLCGPIOAcquisition

Production Pulse Counter on an ESP32 PLC with Interrupts — full example

Count production pulses on an ESP32 PLC using hardware interrupts: debouncing, atomic reads, RPM estimation and MQTT reporting for machine monitoring.

Complete, runnable program for the ESP32 PLC (production-pulse-counter.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 — Interrupt-driven production pulse counter
 *
 * Hardware: ESP32 PLC (Industrial Shields, any model with WiFi)
 * Based on: textile monitoring project, modul_funciones.h
 *
 * Wiring:
 *   I0_0   Production pulse sensor (proximity switch/encoder on the
 *          cylinder of the Terrot knitting machine: 1 pulse = 1 revolution)
 *
 * Architecture:
 *   1. Hardware interrupt on I0_0 -> volatile counter, no pulses lost
 *      even when the loop is busy (SD, MQTT, web...)
 *   2. Debounce via minimum time between pulses (electrical noise filter)
 *   3. Every send cycle (20 s) the counter is read, approximate RPM is
 *      computed and published over MQTT; then the counter is reset
 *   4. Shift total kept in a second variable that is only reset
 *      by command
 *
 * Works together with other catalog examples:
 *   - mqtt-events-sd-buffering.ino (snapshot every 20 s)
 *   - sd-daily-file-datalogging.ino (backup)
 *   - mqtt-remote-commands.ino (reset of the shift total)
 */

#include 
#include 

const char *WIFI_SSID = "WIFI_SSID";
const char *WIFI_PASS = "WIFI_PASS";
const char *MQTT_HOST = "BROKER_IP";
const int   MQTT_PORT = 1883;
const int   PLC_ID    = 1;
const char *TOPIC_DATA = "planta/plc1/data";

const uint32_t SEND_PERIOD_MS = 20000;    // one snapshot every 20 s
const uint32_t MIN_PULSE_GAP_MS = 30;     // debounce: max ~33 pulses/s

WiFiClient net;
PubSubClient mqtt(net);

// --- Counter state (volatile: touched from the ISR) ---------------------
volatile uint32_t cyclePulses   = 0;   // reset on every send
volatile uint32_t shiftPulses   = 0;   // running total, reset by command
volatile uint32_t lastPulseTime = 0;

uint32_t tSend = 0, tWifi = 0;

// ISR: as small as possible, no Serial and no String
void IRAM_ATTR onPulse() {
  uint32_t now = millis();
  if (now - lastPulseTime < MIN_PULSE_GAP_MS) return;  // bounce/noise
  lastPulseTime = now;
  cyclePulses++;
  shiftPulses++;
}

// --- Non-blocking connectivity (see the WiFi reconnection sheet) --------
void checkWiFi() {
  if (WiFi.status() == WL_CONNECTED) return;
  if (millis() - tWifi < 10000) return;
  tWifi = millis();
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
}

void checkMQTT() {
  if (WiFi.status() != WL_CONNECTED || mqtt.connected()) return;
  mqtt.connect(("plc" + String(PLC_ID)).c_str());
}

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

  pinMode(I0_0, INPUT);
  attachInterrupt(I0_0, onPulse, FALLING);   // falling edge of the sensor

  checkWiFi();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
}

void loop() {
  checkWiFi();
  checkMQTT();
  mqtt.loop();

  if (millis() - tSend >= SEND_PERIOD_MS) {
    uint32_t period = millis() - tSend;
    tSend = millis();

    // Atomic read and reset of the cycle counter
    noInterrupts();
    uint32_t n      = cyclePulses;
    uint32_t total  = shiftPulses;
    cyclePulses = 0;
    interrupts();

    // Approximate RPM: cycle pulses extrapolated to 1 minute
    float rpm = (n * 60000.0f) / period;

    String json = "{\"id\":" + String(PLC_ID) +
                  ",\"pulses\":" + String(n) +
                  ",\"shift_total\":" + String(total) +
                  ",\"rpm\":" + String(rpm, 1) + "}";

    if (mqtt.connected()) mqtt.publish(TOPIC_DATA, json.c_str());
    Serial.println(json);   // in production, additionally: saveToSD(json)
  }

  delay(10);   // the counter keeps running in the ISR even while the loop sleeps
}
Download the full project pack — freeThis example + the related ones + bill of materials