Water Meter Project - Noob trouble (probably)

Hello

First of all this project was copied from:

I wanted to make a simple water meter to control the water consumsion in my vacation home, mostly to alert me of any leaks and such. so i found the project above and this being my first project and all, i thought that it could be a nice entry level project. How wrong was i !?

First it was the error “ISR not in IRAM”

I went and found that the solution was a symple line: ICACHE_RAM_ATTR before the (attachInterrupt)

Im mentioning that because i dont know if, first helps some one and second is this that is ruaning my project.

I probably am doing something wrong in my network config, but i can not get the project to comunicate with the ThingSpeak platform. im just asking some help to figure it out.

If the code is ok can someone help me please, to finish my first project please, inlight me in what im doing wrong.

#include <Arduino.h>
#include <EEPROM.h>
#define USE_SERIAL Serial
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

// Variable init
const int  buttonPin = D2; // variable for D2 pin
int contagem = 0;   // variable to store the “rise ups” from the flowmeter pulses
int litros = 0;
char thingspeak_string[200]; //string used to send info to the server ThingSpeak
char litros_string[10] = "0";
int addr = 0; //endereço eeprom

//SSID and PASSWORD for the AP (swap the XXXXX for real ssid and password )
const char* ssid = "xxxxxx";
const char* password = "xxxxxx";

//HTTP client init
HTTPClient http;

//Webserver init
WiFiServer server(80);

//Interrupt function, so that the counting of pulse “rise ups” dont interfere with the rest of the code  (attachInterrupt)
ICACHE_RAM_ATTR void pin_ISR()
{
  contagem++;
}

void setup() {
  // Serial Comunication init
  Serial.begin(115200);
  delay(10);

  // EEPROM access init
  EEPROM.begin(1);
  litros = EEPROM.read(addr);


  // Initialization of the variable “buttonPin” as INPUT (D2 pin)
  pinMode(buttonPin, INPUT);

  // Wifi connection init
  Serial.println();
  Serial.print("A iniciar ligação...");
  Serial.println();
  WiFi.begin(ssid, password);

  //Waiting for the connection to be established
  Serial.print("Waiting for the connection...");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(2000);
    Serial.print(".");

    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println();
      Serial.printf("Connect to the SSID: %s", ssid);
    }
  }

  /***********************/
  // Starting Webserver
  server.begin();
  Serial.println();
  Serial.println();
  Serial.println("Server started");

  // Print the IP address
  Serial.print("Use this URL to connect: ");
  Serial.print("http://");
  Serial.print(WiFi.localIP());
  Serial.println("/");
  Serial.println();
  Serial.print("A iniciar contagem dos litros...");

  // Attach an interrupt to the ISR vector
  attachInterrupt(digitalPinToInterrupt(buttonPin), pin_ISR, RISING);

  Serial.println();
  Serial.print("Waiting for client....");
  Serial.println();
}


void loop() {

  // Verify if a clients is connected to the server
  WiFiClient client = server.available();

  // Reply from the local http server, and constrution of the page “on the fly”
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: keep-alive");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.println("<head><meta http-equiv=\"refresh\" content=\"10\" >");
  client.println("<script type='text/javascript'>");
  client.println("function loadDoc() {");
  client.println("var xhttp = new XMLHttpRequest();");
  client.println("xhttp.onreadystatechange = function() {");
  client.println("if (xhttp.readyState == 4 && xhttp.status == 200) {");
  client.printf("document.getElementById('id_litros').innerHTML = %d", litros);
  client.println("}");
  client.println("};");
  client.println("xhttp.open('GET', '', true);");
  client.println("xhttp.send();");
  client.println("}");
  client.println("</script></head>");
  client.println("<body onload='setInterval(loadDoc, 5000);'>");
  client.println("

");
  client.printf("<div id='id_litros'>Est&atilde;o contados %d litros!</div>", litros);
  client.println("<iframe width=\"450\" height=\"260\" style=\"border: 1px solid #cccccc;\" src=\"https://thingspeak.com/channels/120470/charts/1?bgcolor=%23ffffff&color=%23d62020&dynamic=true&results=60&title=Contagem+de+Litros&type=line\"></iframe>");
  client.println("</body>");
  client.println("</html>");
  client.stop();

  delay(1);

  // If the counting of transitions (Low to High, “rise ups”) it's higher than 440, count one litre more. Then do the rest of the functions (update to EEPROM variable, loca  webserver and ThingSpeak)
  //pulse per litre +/- 450 "www.hobbytronics.co.uk/yf-s201-water-flow-meter"

  if (contagem > 440 )
  {
    litros++;
    Serial.println();
    Serial.print("Litros: ");
    Serial.print(litros);

    //Write the new litres value to the EEPROM and put “contagem” variable to zero
    EEPROM.write(addr, litros);
    EEPROM.commit();
    contagem = 0;

    //The value of litres is sent to the ThingSpeak server. It is needed to have an account in ThingSpeak server before using this funcionality. You will have to copy the link given (something like this:  https://api.thingspeak.com/update.json?api_key=XXX&field1=XXX), example below.
    dtostrf(litros, 4, 2, litros_string);
    sprintf(thingspeak_string, "https://api.thingspeak.com/channels/XXXXXXXX/feeds.json?api_key=XXXXXXXXXXXXXXXXXX&results=0", litros_string);
    //String sent to ThingSpeak server.
    http.begin(thingspeak_string);

    //Send HTTP Header
    int httpCode = http.GET();

    // httpCode_code will be a negative number if there is an error
    if (httpCode > 0) {
      // file found at server
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        Serial.print(" ");
        Serial.println(payload);
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();

  }//stop counting

  delay(500);
}

Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html .
Then look down to item #7 about how to post your code.
It will be formatted in a scrolling window that makes it easier to read.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks… Tom… :slight_smile:

Thanks and sorry TomGeorge

This is the circuit, it is a simple circuit, i realy think that the code is the problem.
I dont know if it was simpler to write a new one.
I just want the flowmeter to work as a warning basically. if water flows true it i want to get some kind of alert (push notification, Email, etc)

And once again thanks for the help

contagem is supposed to be 'volatile'

I have one of those sensors but haven't really put it through any real testing as yet. I'm wondering if that will even register if there is a small leak. A small leak of a couple of drops per minute over time can cause a lot of damage. Just thinking the leak will have to be somewhat considerable to cause enough flow to be readable. Testing will tell the tale though.

My water supply company sends me an email when their meter detects a leak on my property. They use a very clever algorithm which takes into account the time of day and other factors to positively identify a leak.

No false alarms and the one time I really did have a leak, they told me before I found the wet patch.

I dont know any coding. i was hoping the project i got my idea from was kind of a copy paste project. but i was wrong.

KASSIMSAMJI - can you please show me how to change the code for it to work. and if not no worrys all the help is good help. thanks

DangerToMyself - i kind of have the same worry that you have, and i realy would like to test it. I was just kind of hoping it would give some kind of reading because of the formula if it spins 1 time i think it gives a reading. So no spin closed water circuit, a spin better check out if every things ok.

MorganS - Lucky you my water company still uses does analog ones, and some times they ask me over phone to see the readyng on it. they dont even bother to check it by them selfs some times. i would love some kind of system like yours, but i think i will have to wait on that.

I will try to putt this together im learning something and having fun doing it, i guess this is what is supposed to happen. Thanks all you guys.

PS: if there are any one o can make a code for it it would be wonderfull just saying :slight_smile: :slight_smile: :slight_smile:

// Crédito:
// - https://diyhacking.com/arduino-flow-rate-sensor
// - http://www.instructables.com/en/Flowmeter-NodeMcu-Counting-Litres/

#define ARDUINO_H
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h> 
#include <EEPROM.h> 
#define USE_SERIAL Serial
#include <ESP8266WiFi.h> 
#include <ESP8266HTTPClient.h>

// Variable init
const int buttonPin = D2; // variable for D2 pin
const int ledPin = D7;
char push_data[200]; //string used to send info to the server ThingSpeak
int addr = 0; //endereço eeprom
byte sensorInterrupt = 0; // 0 = digital pin 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;

//SSID and PASSWORD for the AP (swap the XXXXX for real ssid and password )
const char * ssid = "USER";
const char * password = "PASS";

//HTTP client init
HTTPClient http;

void setup() {
    Serial.begin(115200); // Start the Serial communication to send messages to the computer
    delay(10);
    Serial.println('\n');

    startWIFI();
  
    // Initialization of the variable “buttonPin” as INPUT (D2 pin)
    pinMode(buttonPin, INPUT);
  
    // Two types of blinking
    // 1: Connecting to Wifi
    // 2: Push data to the cloud
    pinMode(ledPin, OUTPUT); 
    
    pulseCount = 0;
    flowRate = 0.0;
    flowMilliLitres = 0;
    totalMilliLitres = 0;
    oldTime = 0;

    digitalWrite(buttonPin, HIGH);
    attachInterrupt(digitalPinToInterrupt(buttonPin), pulseCounter, RISING);

}

void loop() {
    if (WiFi.status() == WL_CONNECTED && (millis() - oldTime) > 1000) // Only process counters once per second
    {
        // Disable the interrupt while calculating flow rate and sending the value to
        // the host
        detachInterrupt(sensorInterrupt);

        // 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("."); // Print the decimal point
        // Determine the fractional part. The 10 multiplier gives us 1 decimal place.
        frac = (flowRate - int(flowRate)) * 10;
        Serial.print(frac, DEC); // Print the fractional part of the variable
        Serial.print("L/min");
        // Print the number of litres flowed in this second
        Serial.print("  Current Liquid Flowing: "); // Output separator
        Serial.print(flowMilliLitres);
        Serial.print("mL/Sec");

        // Print the cumulative total of litres flowed since starting
        Serial.print("  Output Liquid Quantity: "); // Output separator
        Serial.print(totalMilliLitres);
        Serial.println("mL");

        if (flowRate > 0) {
            digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
            delay(100);          

            // Replace <YOUR_API_KEY> with your EmonCMS API Key 
            sprintf(push_data, "http://emoncms.org/input/post?json={frac:%d.%d,flowml:%d,totalml:%d}&node=Penampung2&apikey=<APIKEYHERE>", int(flowRate), int(frac), flowMilliLitres, totalMilliLitres);
            Serial.printf("%s\n", push_data);
            http.begin(push_data);
            digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
            delay(100);              
            int httpCode = http.GET();
            // httpCode_code will be a negative number if there is an error
            Serial.print(httpCode);
            if (httpCode > 0) {
                digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
                delay(100);              
                // file found at server
                if (httpCode == HTTP_CODE_OK) {
                    String payload = http.getString();
                    Serial.print(" ");
                    Serial.print(payload);
                }
                digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
                delay(100);              
            } else {
                Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
            }
            http.end();
        }
        // 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);
    } else if (WiFi.status() != WL_CONNECTED) {
        startWIFI();
    }
}

/*
Insterrupt Service Routine
 */
ICACHE_RAM_ATTR void pulseCounter() {
    // Increment the pulse counter
    pulseCount++;
}

void startWIFI(void) {
    digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(100);              
    
    WiFi.begin(ssid, password); // Connect to the network
    Serial.print("Connecting to ");
    Serial.print(ssid);
    Serial.println(" ...");
    oldTime = 0;
    int i = 0;
    digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
    delay(100);         
    
    while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
        digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(2000);
        Serial.print(++i);
        Serial.print('.');
        digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
        delay(100);  
    }
    delay(2000);
    Serial.print('\n');
    Serial.print("Connection established!");
    Serial.print("IP address:\t");
    Serial.print(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer

}

Ok its done and working

It on test right now but code is ok and it links with emoncms.org so im happy with it, Dangertomyself i think that it read even a single spin, but that is in testing. thanks for the help.
This is where i found the new code - Measuring Water Usage with NodeMCU +WaterFlow Sensor · GitHub i had to make some changes because of the “ISR not in IRAM” error but all good now.

And i left all the code and the schematic so if any one is intrested its a good simple project for begginers

ser_res:

// Crédito:

// - https://diyhacking.com/arduino-flow-rate-sensor
// - http://www.instructables.com/en/Flowmeter-NodeMcu-Counting-Litres/

#define ARDUINO_H
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <EEPROM.h>
#define USE_SERIAL Serial
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

// Variable init
const int buttonPin = D2; // variable for D2 pin
const int ledPin = D7;
char push_data[200]; //string used to send info to the server ThingSpeak
int addr = 0; //endereço eeprom
byte sensorInterrupt = 0; // 0 = digital pin 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;

//SSID and PASSWORD for the AP (swap the XXXXX for real ssid and password )
const char * ssid = “USER”;
const char * password = “PASS”;

//HTTP client init
HTTPClient http;

void setup() {
Serial.begin(115200); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println(’\n’);

startWIFI();

// Initialization of the variable “buttonPin” as INPUT (D2 pin)
pinMode(buttonPin, INPUT);

// Two types of blinking
// 1: Connecting to Wifi
// 2: Push data to the cloud
pinMode(ledPin, OUTPUT); 

pulseCount = 0;
flowRate = 0.0;
flowMilliLitres = 0;
totalMilliLitres = 0;
oldTime = 0;

digitalWrite(buttonPin, HIGH);
attachInterrupt(digitalPinToInterrupt(buttonPin), pulseCounter, RISING);

}

void loop() {
if (WiFi.status() == WL_CONNECTED && (millis() - oldTime) > 1000) // Only process counters once per second
{
// Disable the interrupt while calculating flow rate and sending the value to
// the host
detachInterrupt(sensorInterrupt);

    // 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("."); // Print the decimal point
    // Determine the fractional part. The 10 multiplier gives us 1 decimal place.
    frac = (flowRate - int(flowRate)) * 10;
    Serial.print(frac, DEC); // Print the fractional part of the variable
    Serial.print("L/min");
    // Print the number of litres flowed in this second
    Serial.print("  Current Liquid Flowing: "); // Output separator
    Serial.print(flowMilliLitres);
    Serial.print("mL/Sec");

    // Print the cumulative total of litres flowed since starting
    Serial.print("  Output Liquid Quantity: "); // Output separator
    Serial.print(totalMilliLitres);
    Serial.println("mL");

    if (flowRate > 0) {
        digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(100);          

        // Replace <YOUR_API_KEY> with your EmonCMS API Key 
        sprintf(push_data, "http://emoncms.org/input/post?json={frac:%d.%d,flowml:%d,totalml:%d}&node=Penampung2&apikey=<APIKEYHERE>", int(flowRate), int(frac), flowMilliLitres, totalMilliLitres);
        Serial.printf("%s\n", push_data);
        http.begin(push_data);
        digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
        delay(100);              
        int httpCode = http.GET();
        // httpCode_code will be a negative number if there is an error
        Serial.print(httpCode);
        if (httpCode > 0) {
            digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
            delay(100);              
            // file found at server
            if (httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                Serial.print(" ");
                Serial.print(payload);
            }
            digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
            delay(100);              
        } else {
            Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }
        http.end();
    }
    // 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);
} else if (WiFi.status() != WL_CONNECTED) {
    startWIFI();
}

}

/*
Insterrupt Service Routine
*/
ICACHE_RAM_ATTR void pulseCounter() {
// Increment the pulse counter
pulseCount++;
}

void startWIFI(void) {
digitalWrite(ledPin, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100);

WiFi.begin(ssid, password); // Connect to the network
Serial.print("Connecting to ");
Serial.print(ssid);
Serial.println(" ...");
oldTime = 0;
int i = 0;
digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
delay(100);         

while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(2000);
    Serial.print(++i);
    Serial.print('.');
    digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
    delay(100);  
}
delay(2000);
Serial.print('\n');
Serial.print("Connection established!");
Serial.print("IP address:\t");
Serial.print(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer

}




Ok its done and working

It on test right now but code is ok and it links with emoncms.org so im happy with it, Dangertomyself i think that it read even a single spin, but that is in testing. thanks for the help. 
This is where i found the new code - https://gist.github.com/klanjabrik/367d7d370f27703425e7a77475b18b16 i had to make some changes because of the "ISR not in IRAM" error but all good now.


And i left all the code and the schematic so if any one is intrested its a good simple project for begginers

But what does it do with only 2 or 3 drips per minute? Is that enough to make it turn at all?