Turbine type Flow meter give incorrect flow

Hi..

I am measuring water flow using LLYGO Module, But flow is not getting accurate and some time it's give random values.

where my flow meter is installed in same line one manual totalizer installed both reading are not matched my flow flow meter gives higher value then other one.

I am using interrupt function for measurement and internal pull-up for sensor pin.

Please read the post on how to get the best from the forum.
You should provide information that is needed to be able to help you. That consists of the code that you are using, a wiring diagram and technical data for the components.

Post your code. Please read the forum guidelines to see how to properly post code and some information on making a good post.

Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

Please post a wiring diagram. Written descriptions are always more ambiguous than a drawing. Hand drawn, photographed and posted is fine. Include all pin names/numbers, components, their part numbers and/or values and power supplies.

Post a link to the data sheet for the flow sensor.

1 Like

Please find the attached code.

// Select your modem:
#define TINY_GSM_MODEM_SIM800 // Modem is SIM800L
#define TINY_GSM_RX_BUFFER 512

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands
#define SerialAT Serial1

// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon

// set GSM PIN, if any
#define GSM_PIN ""

// Your GPRS credentials, if any
const char apn[] = "www"; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org
const char gprsUser[] = "";
const char gprsPass[] = "";

// SIM card PIN (leave empty, if not defined)
const char simPIN[]   = "";

// MQTT details
#define mqttServer    "io.adafruit.com"
#define mqttUserName  "***"  // MQTT username
#define mqttPwd       "***"  // MQTT password
#define clientID      "Node_AT_3" // client id

#define topicFlowrate   mqttUserName    "/feeds/flowrate_AT_3"
#define topicTotalizer  mqttUserName    "/feeds/totalizer_AT_3"

#include <Wire.h>
#include <TinyGsmClient.h>
#include <EEPROM.h>
#include <otadrive_esp.h>
#include <esp_task_wdt.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include <driver/rtc_cntl.h>
#include <Preferences.h>
Preferences pref;

#define WDT_TIMEOUT 300

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif

#include <PubSubClient.h>

TinyGsmClient client(modem);
PubSubClient mqtt(client);

// MuxChannel 1 for OTA
TinyGsmClient gsm_otadrive_client(modem, 1);

// TTGO T-Call pins
#define MODEM_RST            5
#define MODEM_PWKEY          4
#define MODEM_POWER_ON       23
#define MODEM_TX             27
#define MODEM_RX             26
#define I2C_SDA              21
#define I2C_SCL              22

uint32_t lastReconnectAttempt = 0;

// I2C for SIM800 (to keep it running when powered from battery)
TwoWire I2CPower = TwoWire(0);

#define IP5306_ADDR          0x75
#define IP5306_REG_SYS_CTL0  0x00

long lastMsg = 0;
/*******************************************/
//Flow Measurement Global Variables
#define SENSOR  12
long currentMillis = 0;
long previousMillis = 0;
float flowrate, flowrate_1;
unsigned long totalizer;
double tot_2;
volatile byte pulseCount;
byte pulse1Sec = 0;
uint16_t interval=300;
float calibrationFactor=0.2;

long currentMillis_1 = 0;
long previousMillis_1 = 0;

float last_flow_min,avg_flow;

char flowrate_String[8];
char totalizer_String[8];

/*******************************************/
void int_GSM_modem() {

  // Set GSM module baud rate and UART pins
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(6000);
  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();
  // modem.init();

  if (!modem.waitForNetwork()) {
    //Serial.println(" fail");
    while (true);
  }

  int csq = modem.getSignalQuality();
  SerialMon.print("Modem Signal Strength: ");
  SerialMon.println(csq);
  
  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem Info: ");
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN if needed
  if ( GSM_PIN && modem.getSimStatus() != 3 ) {
    modem.simUnlock(GSM_PIN);
  }

  SerialMon.print("Connecting to APN: ");
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    ESP.restart();
  }
  else {
    SerialMon.println(" OK");
  }

  if (modem.isGprsConnected()) {
    SerialMon.println("GPRS connected");
  }

  // MQTT Broker setup
  mqtt.setServer(mqttServer, 1883);
  mqtt.setCallback(mqttCallback);
}
void DATA_PUBLISH() {

  if (!mqtt.connected()) {
        SerialMon.println("=== MQTT NOT CONNECTED ===");
        // Reconnect every 10 seconds
        uint32_t t = millis();
        if (t - lastReconnectAttempt > 10000L) {
            lastReconnectAttempt = t;
            if (mqttConnect()) {
                lastReconnectAttempt = 0;
            }
        }
        delay(100);
        return;
    }
    
  long now = millis();
  if (now - lastMsg > (1000 * interval)) {
    lastMsg = now;
    // Convert the value to a char array
    dtostrf(avg_flow, 1, 2, flowrate_String);
    Serial.print("flowrate: ");
    Serial.println(flowrate_String);
    mqtt.publish(topicFlowrate, flowrate_String);

    // Convert the value to a char array

    dtostrf(totalizer, 1, 2, totalizer_String);
    Serial.print("Totalizer: ");
    Serial.println(totalizer_String);
    mqtt.publish(topicTotalizer, totalizer_String);
  }
}
void NW_Check() {
  // Make sure we're still registered on the network
  if (!modem.isNetworkConnected()) {
    SerialMon.println("Network disconnected");
    if (!modem.waitForNetwork(180000L, true)) {
      SerialMon.println(" fail");
      delay(10000);
      return;
    }
    if (modem.isNetworkConnected()) {
      SerialMon.println("Network re-connected");
    }


#if TINY_GSM_USE_GPRS
    // and make sure GPRS/EPS is still connected
    if (!modem.isGprsConnected()) {
      SerialMon.println("GPRS disconnected!");
      SerialMon.print(F("Connecting to "));
      SerialMon.print(apn);
      if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
        SerialMon.println(" fail");
        delay(10000);
        return;
      }
      if (modem.isGprsConnected()) {
        SerialMon.println("GPRS reconnected");
      }
    }
#endif
  }
}
bool setPowerBoostKeepOn(int en) {
  I2CPower.beginTransmission(IP5306_ADDR);
  I2CPower.write(IP5306_REG_SYS_CTL0);
  if (en) {
    I2CPower.write(0x37); // Set bit1: 1 enable 0 disable boost keep on
  } else {
    I2CPower.write(0x35); // 0x37 is default reg value
  }
  return I2CPower.endTransmission() == 0;
}
void mqttCallback(char* topic, byte* message, unsigned int len) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < len; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic esp/output1, you check if the message is either "true" or "false".
  // Changes the output state according to the message
}
boolean mqttConnect() {
  SerialMon.print("Connecting to ");
  SerialMon.print(mqttServer);

  // Connect to MQTT Broker without username and password
  //boolean status = mqtt.connect("GsmClientN");

  // Or, if you want to authenticate MQTT:
  boolean status = mqtt.connect(clientID, mqttUserName, mqttPwd);

  if (status == false) {
    SerialMon.println(" fail");
    ESP.restart();
    return false;
  }
  SerialMon.println(" success");

  return mqtt.connected();
}
void IRAM_ATTR pulseCounter() {
  pulseCount++;
}
void update_prgs(size_t i, size_t total) {
  SerialMon.printf("upgrade %d/%d   %d%%\n", i, total, ((i * 100) / total));
  esp_task_wdt_reset();
}
void setup() {
  // Set console baud rate
  SerialMon.begin(115200);
  delay(10);
  
  pref.begin("Last_tot", 0);
  pref.begin("interval_val", 2);
  pref.begin("cal_fac", 0);
  //init_bod();
  // Start I2C communication
  I2CPower.begin(I2C_SDA, I2C_SCL, 400000);

  // Keep power when running from battery
  bool isOk = setPowerBoostKeepOn(1);
  SerialMon.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL"));

  pinMode(SENSOR, INPUT_PULLUP);
  // Set modem reset, enable, power pins
  pinMode(MODEM_PWKEY, OUTPUT);
  pinMode(MODEM_RST, OUTPUT);
  pinMode(MODEM_POWER_ON, OUTPUT);
  digitalWrite(MODEM_PWKEY, LOW);
  digitalWrite(MODEM_RST, HIGH);
  digitalWrite(MODEM_POWER_ON, HIGH);

  pinMode(13, OUTPUT);

  SPIFFS.begin(true);
  OTADRIVE.setInfo("***", "2.1.1");
  OTADRIVE.onUpdateFirmwareProgress(update_prgs);
  
  SerialMon.println("Wait...");
  int_GSM_modem();

  tot_2 = pref.getDouble("Last_tot", 0);
  //interval = pref.getUInt("interval_val", 300);
  //calibrationFactor = pref.getFloat("cal_fac", 0.2);
  
  //tot_2 = 0;//Reset Totalizer
  
  Serial.print("cal_f->");
  Serial.println(calibrationFactor);
  Serial.print("interval_val->");
  Serial.println(interval);

  pulseCount = 0;
  flowrate = 0.0;
  previousMillis = 0;
  attachInterrupt(digitalPinToInterrupt(SENSOR), pulseCounter, FALLING);
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1);

  esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts
  esp_task_wdt_add(NULL);
  //mqttConnect();
}
void loop() {
  digitalWrite(13, HIGH);
  NW_Check();
  if (OTADRIVE.timeTick(3600))
  {
      OTADRIVE.updateFirmware(gsm_otadrive_client);
      Serial.print("*");
  }
  DATA_PUBLISH();
  mqtt.loop();
  Flow_measure();
  esp_task_wdt_reset();
  digitalWrite(13, LOW);
}
void Flow_measure() {
  long currentTime = 0;
  long currentTime_1 = 0;
  float F = 0;

  currentMillis_1 = currentMillis = millis();
  currentTime = currentMillis - previousMillis;

  currentTime_1 = currentMillis_1 - previousMillis_1;

  if (currentTime_1 >= 5113)
  {
    pref.putDouble("Last_tot", tot_2);
    previousMillis_1 = millis();
  }

  if (currentTime > 1000)
  {
    pulse1Sec = pulseCount;
    pulseCount = 0;

    flowrate = ((1000.0 / (millis() - previousMillis)) * pulse1Sec) / calibrationFactor;
    previousMillis = millis();

    avg_flow = ((9*avg_flow)+flowrate)/10.0;
    flowrate_1 = avg_flow;//LPM

    tot_2 = (tot_2 + (flowrate_1/60000.0)); //Totalizing in Kilo Liter
    totalizer = (unsigned long)tot_2;
    Serial.print("Q:");
    Serial.print(float(avg_flow));  // Print the integer part of the variable
    Serial.print("LM");
    Serial.print("-");
    Serial.print("Totalizer: ");
    Serial.print(tot_2,3);
    Serial.print("-");
    Serial.print(float(totalizer));  // Print the integer part of the variable
    Serial.println("L");
  }
}

Did you get the flow sensor to work by itself before including it into the larger code?

1 Like

Before including big code it's wok same as now, In big code while i am giving pulse using pulse generator it's work fine.

I’ve not looked at your code but …

An internal pull-up may not be sufficient , if the lead length is long .

You also need to know if the pulse output is a hall type , in which case a circuit to suit is needed .

Turbine meters need to be operated within their flow range or can be very inaccurate. Also if mounted close to bends , vales etc the flow can be quite turbulent and cause over reading .

Hi, @jetpace
Welcome to the forum.

Is your meter installed down stream of the totalisator?
How much distance is there between the two units?

They might be too close together and you need the water coming out of the totalisator to get back to laminar flow before encountering your meter.

Can you please post some images of your project so we can see your component layout?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

Okay, I will add pull-up because my flow meter lead is long about 1-mtr. It's hall type sensor.

10K resistor for PULL_UP is it okay ?

Yes , but check the output type of the meter .
10k ok fora Reed switch type output , but you turbine could well be different - post up a spec for it ( or a link )

That is a good starting value. If it improves but is still not good, go to lower values.

Sorry just read you have Hall effect sensor - that might need a specific pull up value or “bias “ power to work correctly. Need the spec .

Agree ..Going down to lower resistor values is fine , providing that’s ok with the meter spec !

Wirth doing error analysis from the spec of both meters to see if really they are ok .
If the totaliser is for billing , it has to under read , so you are not overcharged .
Running a cheap turbine at 1/10 it’s max flow will probably be 10% out .

Please find link, I am using that one.

Hi,
That meter has a decent sized bore, are you sure that it is full of water and not have bubbles in it?

What is the actual water flow?

Tom... :smiley: :+1: :coffee: :australia:

I don't know actual flow because we are measuring totalizing aver 12 hours. It's come from water tank directly there is a know any motor, It's come from gravity.

For My sensor which resistor is best, How to know ?

I've picked out just the code which is relevant to the pulse counter (below).
Your primary data is the pulseCount but you are continually resetting it after folding it into a flow rate calculation. You should really never lose your primary data like that.
I'd suggest making the pulseCount bigger, say uint32_t, and derive the volume and flow rate periodically directly from that, maybe something like this:

Pseudocoude:

every x seconds {
    pulses = pulseCount - oldPulseCount ;
    oldPulseCount = pulseCount ;
    print pulseCount (total pulses since start) ; 
    convert pulseCount to liters and print total liters since start.
    convert pulses to litres and print liters delivered in the last x seconds (flow rate).
}

.

code fom OP (relevant to flow calculation)

. . .
//Flow Measurement Global Variables
#define SENSOR  12
volatile byte pulseCount;
. . .


setup(){ 
   . . . 
   pinMode(SENSOR, INPUT_PULLUP);
   attachInterrupt(digitalPinToInterrupt(SENSOR), pulseCounter, FALLING);
   . . .
}



void loop() {
  . . .
  Flow_measure();
  . . .
}

void IRAM_ATTR pulseCounter() {
  pulseCount++;
}


void Flow_measure() {
  long currentTime = 0;
  long currentTime_1 = 0;
  float F = 0;

  currentMillis_1 = currentMillis = millis();
  currentTime = currentMillis - previousMillis;

  currentTime_1 = currentMillis_1 - previousMillis_1;

  if (currentTime_1 >= 5113)
  {
    pref.putDouble("Last_tot", tot_2);
    previousMillis_1 = millis();
  }

  if (currentTime > 1000)
  {
    pulse1Sec = pulseCount;
    pulseCount = 0;

    flowrate = ((1000.0 / (millis() - previousMillis)) * pulse1Sec) / calibrationFactor;
    previousMillis = millis();

    avg_flow = ((9*avg_flow)+flowrate)/10.0;
    flowrate_1 = avg_flow;//LPM

    tot_2 = (tot_2 + (flowrate_1/60000.0)); //Totalizing in Kilo Liter
    totalizer = (unsigned long)tot_2;
    Serial.print("Q:");
    Serial.print(float(avg_flow));  // Print the integer part of the variable
    Serial.print("LM");
    Serial.print("-");
    Serial.print("Totalizer: ");
    Serial.print(tot_2,3);
    Serial.print("-");
    Serial.print(float(totalizer));  // Print the integer part of the variable
    Serial.println("L");
  }
}

I’d try the 10k , documentation isn’t great

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.