Skip to Content

← Modbus Flow Meter 32-bit Counter Read and Remote Reset

Bioreactor controlM-DuinoModbus RTUAcquisition

Modbus Flow Meter 32-bit Counter Read and Remote Reset — full example

Read 32-bit flow meter totals over Modbus RTU with an M-Duino PLC and expose a remote counter reset as a Modbus TCP coil. Full Arduino code included.

Complete, runnable program for the M-Duino (flow-meter-reading.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 — 32-bit flow meter reading + remote reset
 *
 * Device:  M-Duino (Industrial Shields, Arduino PLC with Ethernet + RS485)
 * Based on: bioreactor control project, ModbusTCPSlaveRTUMaster.ino
 *
 * Bus: Modbus RTU over RS485, 9600 8N1. Two flow meters at addr 5 and 6
 * that measure the process water entering/leaving the bioreactors.
 *
 * Flow meter RTU registers:
 *   0x100  accumulated liters (2 x 16-bit regs -> 32-bit integer)
 *   0x200  counter reset command (1 = set to zero)
 *
 * The accumulated value is published as an input register on the TCP side,
 * and the reset is exposed as a coil: when the SCADA sets the coil to 1,
 * this sketch issues the 0x200 command over RTU and clears the coil again.
 *
 * Integration: see the "Gateway Modbus TCP <-> RTU" module for the full
 * plant map; here the flow acquisition is isolated.
 */

#include 
#include 
#include 

ModbusRTUMaster master(RS485);
ModbusTCPSlave  modbus;

const uint8_t FLOW[2] = {5, 6};     // RTU addresses of the two flow meters

const uint16_t REG_ACC   = 0x100;     // accumulated liters (2 regs)
const uint16_t REG_RESET = 0x200;     // reset command

// Memory exposed over TCP
uint16_t inputRegs[12];               // 6,7 = liters flow 1 ; 8,9 = liters flow 2
bool     coils[18];                   // 16,17 = reset flow 1 / flow 2

uint32_t tPoll = 0;

void setup() {
  Serial.begin(115200);
  master.begin(9600);
  master.setTimeout(300);

  modbus.addInputRegisters(0, inputRegs, 12);
  modbus.addCoils(0, coils, 18);
  modbus.begin();
}

void loop() {
  modbus.update();   // serve the SCADA over TCP

  if (millis() - tPoll > 500) {
    tPoll = millis();
    for (uint8_t i = 0; i < 2; ++i) {
      // 1. If the SCADA requested a reset (coil 16/17), execute it and clear it
      if (coils[16 + i]) {
        if (resetCounter(FLOW[i]) == 0) {
          coils[16 + i] = false;
          Serial.print("Flow meter counter reset: addr ");
          Serial.println(FLOW[i]);
        }
      }
      // 2. Read the 32-bit accumulated value into input regs (6/7 and 8/9)
      uint32_t liters = readAccumulated(FLOW[i]);
      inputRegs[6 + i * 2]     = (uint16_t)(liters >> 16);
      inputRegs[6 + i * 2 + 1] = (uint16_t)(liters & 0xFFFF);
    }
  }
}

// Reads 2 x 16-bit registers and combines them into liters (32 bits)
uint32_t readAccumulated(uint8_t addr) {
  if (!master.readHoldingRegisters(addr, REG_ACC, 2)) return 0;
  if (master.isWaitingResponse()) {
    ModbusResponse r = master.available();
    if (r && !r.hasError())
      return (uint32_t)((r.getRegister(0) << 16) | r.getRegister(1));
  }
  return 0;
}

// Sets the flow meter counter to zero
uint8_t resetCounter(uint8_t addr) {
  if (!master.writeSingleRegister(addr, REG_RESET, 1)) return 1;
  if (master.isWaitingResponse()) {
    ModbusResponse r = master.available();
    if (r && !r.hasError()) return 0;
  }
  return 1;
}
Download the full project pack — freeThis example + the related ones + bill of materials