I have a project to be made i.e. water Billing System using RFID(Mfrc522) on Arduino NANO. There is one difficulty that I haven't been able to overcome. I hope someone could help me out. Project flow:

  1. water or any liquid flows through the sensor and quantity of water gets recorded
  2. now according to quantity of water flown there is price decided
  3. this price needs to be deducted by swiping rfid card issue:
  4. i haven't been able to control interrupts
  5. my program calculates the water flown required:
  6. edit code such that it as soon as water flows through it, it calculates and ask user to swipe card and as user swipes a amount deductes and process continues.

if not clear please ask and help me out

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9          // Configurable, see typical pin layout above
#define SS_PIN          10         // Configurable, see typical pin layout above
MFRC522 mfrc522(SS_PIN, RST_PIN);
String tagUID = "10 90 70 38";  // String to store UID of tag. Change it with your tag's UID
int bal =1000;

byte statusLed    = 13;
byte sensorInterrupt = 0;  // 0 = digital pin 2
byte sensorPin       = 2;

// The hall-effect flow sensor outputs approximately 4.5 pulses per second per
// litre/minute of flow.
float calibrationFactor = 4.5;

volatile byte pulseCount;  

float flowRate;
unsigned int flowMilliLitres;
unsigned long totalMilliLitres;

unsigned long oldTime;

void setup()

  // Initialize a serial connection for reporting values to the host
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0;
  totalMilliLitres  = 0;
  oldTime           = 0;
  // The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
  // Configured to trigger on a FALLING state change (transition from HIGH
  // state to LOW state)
  attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  Serial.begin(9600);    // Initialize serial communications with the PC
  while (!Serial);
  SPI.begin();      // Init SPI bus
  mfrc522.PCD_Init();   // Init MFRC522

 * Main program loop
void loop()

   if((millis() - oldTime) > 2000)    // Only process counters once per second
    // Disable the interrupt while calculating flow rate and sending the value to
    // the host

    // Because this loop may not complete in exactly 1 second intervals we calculate
    // the number of milliseconds that have passed since the last execution and use
    // that to scale the output. We also apply the calibrationFactor to scale the output
    // based on the number of pulses per second per units of measure (litres/minute in
    // this case) coming from the sensor.
    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;

    // Note the time this processing pass was executed. Note that because we've
    // disabled interrupts the millis() function won't actually be incrementing right
    // at this point, but it will still return the value it was set to just before
    // interrupts went away.
    oldTime = millis();

    // Divide the flow rate in litres/minute by 60 to determine how many litres have
    // passed through the sensor in this 1 second interval, then multiply by 1000 to
    // convert to millilitres.
    flowMilliLitres = (flowRate / 60) * 1000;

    // Add the millilitres passed in this second to the cumulative total
    totalMilliLitres += flowMilliLitres;

    unsigned int frac;

    // Print the flow rate for this second in litres / minute
    Serial.print("Flow rate: ");
    Serial.print(int(flowRate));  // Print the integer part of the variable
    Serial.print("\t");       // Print tab space

    // Print the cumulative total of litres flowed since starting
    Serial.print("Output Liquid Quantity: ");        
    Serial.print("\t");       // Print tab space

    // Reset the pulse counter so we can start incrementing again
    pulseCount = 0;

    // Enable the interrupt again now that we've finished sending output
    attachInterrupt(sensorInterrupt, pulseCounter, FALLING);

Insterrupt Service Routine
void pulseCounter()
  // Increment the pulse counter

void rfid()
   // put your main code here, to run repeatedly:

  Serial.println("In Rfid Loop");
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
      // Select one of the cards
      if ( ! mfrc522.PICC_ReadCardSerial()) {
  //Reading from the card
      String tag = "";
      for (byte j = 0; j < mfrc522.uid.size; j++)
        tag.concat(String(mfrc522.uid.uidByte[j] < 0x10 ? " 0" : " "));
        tag.concat(String(mfrc522.uid.uidByte[j], HEX));
       if (tag.substring(1) == tagUID)
          Serial.println("YOUR BALANCE");

          // Reset the pulse counter so we can start incrementing again
          //pulseCount = 0;

          // Enable the interrupt again now that we've finished sending output
          //attachInterrupt(sensorInterrupt, pulseCounter, FALLING);

        Serial.println("WRONG CARD ENTERED");

  • 1
    And what is your actual problem with the interrupts? "haven't been able to control interrupts" is not a good problem description. Was did you expect your code to do and what did it actually do instead?
    – chrisl
    May 19, 2020 at 13:26
  • You have the interrupts detached for most of the time. If you want to use the interrupt you need to have it attached. Instead of detaching for all of loop, only detach for a very short time, make a copy of the pulseCount variable, and then turn interrupts back on. Then use the copy you made in the calculations.
    – Delta_G
    May 19, 2020 at 13:29

1 Answer 1


You are disabling interrupts for way too long. Interrupts are meant for handling time-critical tasks that cannot wait for a full loop iteration. If you ever disable them, it should be for a time as short as possible. In your case, you only need to disable interrupts while accessing the pulseCount variable, as this variable is shared between the ISR and the regular code. This can be done in a very short "critical section" as follows:

uint8_t pulseCountCopy = pulseCount;
pulseCount = 0;

In the rest of the loop you then only access pulseCountCopy. Note that you don't really need to reset pulseCount to zero. Since it is incremented using the rules of modular arithmetic, you can just compute the differences between consecutive readings. You will get the correct result even when it rolls over. This makes the critical section even shorter:

static byte oldPulseCount;
uint8_t pulseCountCopy = pulseCount;
uint8_t pulseIncrement = pulseCountCopy - oldPulseCount;
oldPulseCount = pulseCountCopy;

And then note that, since reading a single byte is an atomic operation (a single machine instruction), you do not even need to disable interrupts al all! However, if the flow can ever be larger than 0.47 L/s, then 8 bits will not be enough for pulseCount. You will have to use a 16-bit variable (uint16_t) and keep the critical section.

As a side note, you are calling millis() several times per loop iteration. This is bad practice, as there is no guarantee that you will always get the same value, so you end up with your timing calculations being somewhat sloppy. You should rather do something like

uint32_t now = millis();

at the start of loop(), and use the same value for the whole iteration.

Another side note: you are differentiating the pulse count in order to get a flow rate, then integrating the flow rate to get a total volume. This is completely useless. The pulse count is already a measure of the total volume. Basic arithmetics shows you that the calibration factor is

4.5 (pulses/s) / (L/min) = 270 pulses/L

Your calculations will be less sloppy (having less accumulated error) if you just divide the total pulse count by 270 to get the total volume in liters.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.