GPIO Interrupt for water meter

Moderator: leecollings

bram2202
Posts: 2
Joined: Saturday 07 February 2015 0:54
Target OS: Raspberry Pi / ODroid
Domoticz version: none
Location: NL (nb)
Contact:

GPIO Interrupt for water meter

Post by bram2202 »

Hello,

I'm trying to hook up my analog water meter to my Domoticz.

So i bought a metal Detector: http://www.dx.com/p/k1208065-metal-dete ... ack-151198

(using WiringPi)
-It now Pulls pin 40 to 3.3v
-set the pin internal to pull down
-set the interrupt to 'rising'
-Created a RFXmeter/counter (Idx15)
-Create a user variable

when i use json to send the total number it works:
/json.htm?type=command&param=udevice&15=IDX&svalue=520
but the next thing is to create a script (python?) who can handle the GPIO interrupt, add a number to a saved var and send the json.

but im stuck.

how can i create a script that start on boot, and triggers on interrupt. and uses a var that not losses its value on a reboot??


Could someone please help me? :roll:
bram2202
Posts: 2
Joined: Saturday 07 February 2015 0:54
Target OS: Raspberry Pi / ODroid
Domoticz version: none
Location: NL (nb)
Contact:

Re: GPIO Interrupt for water meter

Post by bram2202 »

Fixed it :D

Code: Select all

#!/usr/bin/env python
# PIN: 23  == GPIO4 (pin 16)

#imports
import time
import RPi.GPIO as GPIO
import json
import urllib2
import base64

#vars
GET_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=getuservariable&idx=1'
SET_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=udevice&idx=15&svalue='
SET_VAR_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=updateuservariable&idx=1&vname=Water&vtype=0&vvalue='
WATER_VALUE = 0
base64string = base64.encodestring('%s:%s' % ('username', 'password')).replace('\n', '')


# handle the trigger event
def Trigger (pin):
   if GPIO.input(23):
      global WATER_VALUE
      WATER_VALUE += 1
      # print WATER_VALUE

      POST_URL = SET_URL + str(WATER_VALUE)

      global base64string
      requestPost = urllib2.Request(POST_URL)
      requestPost.add_header("Authorization", "Basic %s " % base64string)
      resultPost = json.load(urllib2.urlopen(requestPost))
      # print resultPost

      POST_VAR = SET_VAR_URL + str(WATER_VALUE)

      requestVar = urllib2.Request(POST_VAR)
      requestVar.add_header("Authorization", "Basic %s " % base64string)
      resultVar = json.load(urllib2.urlopen(requestVar))
      # print resultVar


# main function
def main():

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.add_event_detect(23, GPIO.RISING, callback=Trigger, bouncetime=500)

    request = urllib2.Request(GET_URL)

    global base64string
    request.add_header("Authorization", "Basic %s " % base64string)
    result = json.load(urllib2.urlopen(request))

    global WATER_VALUE
    WATER_VALUE = int( result['result'][0]['Value'])
    print WATER_VALUE

    while True:
        time.sleep(1)

   # GPIO.cleanup()

if __name__=="__main__":
    main()


I used Python instead of C, because its easter to use web request with 'urllib2'.
And i use a var created in the Domoticz, easier to setup and readout after a power loss.
rc.local cals a script that launches this python on launch.

and set water divider to 1000 instead of 100, because the script is triggered every 1 liter.

if you have any questions or comments, please feel free to post them.
User avatar
sincze
Posts: 1299
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: GPIO Interrupt for water meter

Post by sincze »

:D nice. Hopefully no fake interrupts that will screw your water usage.
bram2202 wrote:Fixed it :D

Code: Select all

#!/usr/bin/env python
# PIN: 23  == GPIO4 (pin 16)

#imports
import time
import RPi.GPIO as GPIO
import json
import urllib2
import base64

#vars
GET_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=getuservariable&idx=1'
SET_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=udevice&idx=15&svalue='
SET_VAR_URL = 'http://192.168.1.205:8080/json.htm?type=command&param=updateuservariable&idx=1&vname=Water&vtype=0&vvalue='
WATER_VALUE = 0
base64string = base64.encodestring('%s:%s' % ('username', 'password')).replace('\n', '')


# handle the trigger event
def Trigger (pin):
   if GPIO.input(23):
      global WATER_VALUE
      WATER_VALUE += 1
      # print WATER_VALUE

      POST_URL = SET_URL + str(WATER_VALUE)

      global base64string
      requestPost = urllib2.Request(POST_URL)
      requestPost.add_header("Authorization", "Basic %s " % base64string)
      resultPost = json.load(urllib2.urlopen(requestPost))
      # print resultPost

      POST_VAR = SET_VAR_URL + str(WATER_VALUE)

      requestVar = urllib2.Request(POST_VAR)
      requestVar.add_header("Authorization", "Basic %s " % base64string)
      resultVar = json.load(urllib2.urlopen(requestVar))
      # print resultVar


# main function
def main():

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.add_event_detect(23, GPIO.RISING, callback=Trigger, bouncetime=500)

    request = urllib2.Request(GET_URL)

    global base64string
    request.add_header("Authorization", "Basic %s " % base64string)
    result = json.load(urllib2.urlopen(request))

    global WATER_VALUE
    WATER_VALUE = int( result['result'][0]['Value'])
    print WATER_VALUE

    while True:
        time.sleep(1)

   # GPIO.cleanup()

if __name__=="__main__":
    main()


I used Python instead of C, because its easter to use web request with 'urllib2'.
And i use a var created in the Domoticz, easier to setup and readout after a power loss.
rc.local cals a script that launches this python on launch.

and set water divider to 1000 instead of 100, because the script is triggered every 1 liter.

if you have any questions or comments, please feel free to post them.
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
ThinkPad
Posts: 890
Joined: Tuesday 30 September 2014 8:49
Target OS: Linux
Domoticz version: beta
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by ThinkPad »

Instead of 192.x.x.x IP you can use 127.0.0.1 to make the script futureproof (if you ever change IP of your Pi).

Wish i could read my watermeter, but is some old thing with no shiny reflecting part or magnet unfortunately....
http://gathering.tweakers.net/forum/lis ... es/1596066
I am not active on this forum anymore.
Hesmink
Posts: 168
Joined: Monday 22 June 2015 10:48
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by Hesmink »

Hi,

How did you connect this sensor to your RPI?
Hesmink
Posts: 168
Joined: Monday 22 June 2015 10:48
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by Hesmink »

So, I almost got it working, up to the point that if I hold something metal to the sensor, the counter in Domoticz increases by 1.

I used the standard GPIO pins to connect the sensor, I connected brown to +5v, blue to ground and black to GPIO4.
Between black and blue I added a 10K resistor, as stated here: http://forum.arduino.cc/index.php?topic=210921.0

It detects metal, but it seems to weak to detect the revolving disk in the water meter.
Is there any way to increase detection? Does it need a higher voltage than 5V? Other resistor maybe?

Or is something else wrong?

Any help is appreciated.
Hesmink
Posts: 168
Joined: Monday 22 June 2015 10:48
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by Hesmink »

Hesmink wrote:So, I almost got it working, up to the point that if I hold something metal to the sensor, the counter in Domoticz increases by 1.

I used the standard GPIO pins to connect the sensor, I connected brown to +5v, blue to ground and black to GPIO4.
Between black and blue I added a 10K resistor, as stated here: http://forum.arduino.cc/index.php?topic=210921.0

It detects metal, but it seems to weak to detect the revolving disk in the water meter.
Is there any way to increase detection? Does it need a higher voltage than 5V? Other resistor maybe?

Or is something else wrong?

Any help is appreciated.
No ideas?
I'm no electrical genius, so any help is appreciated.
ThinkPad
Posts: 890
Joined: Tuesday 30 September 2014 8:49
Target OS: Linux
Domoticz version: beta
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by ThinkPad »

Maybe you can exchange the resistor for a different value? Or a potmeter so you can adjust it?
But i'm not sure how the sensor works, you don't want to blow up the sensor :lol:
I am not active on this forum anymore.
Hesmink
Posts: 168
Joined: Monday 22 June 2015 10:48
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: The Netherlands
Contact:

Re: GPIO Interrupt for water meter

Post by Hesmink »

ThinkPad wrote:Maybe you can exchange the resistor for a different value? Or a potmeter so you can adjust it?
But i'm not sure how the sensor works, you don't want to blow up the sensor :lol:
Well, it turns out either the detector is too weak, or the shiny part on the dial is not metal.
I first hooked the sensor up to 12v, and later to 15v, but not detection happens (just looking at the detection led on the sensor).
Any other metal object I try it on detects fine.

I have an Itron flodis.
multinet
Posts: 97
Joined: Friday 05 December 2014 22:52
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: GPIO Interrupt for water meter

Post by multinet »

Hello

i've modified the script with my IP and my GPIO

but when i start it i go this error :

Code: Select all

pi@raspberrypi ~/WORK $ sudo python ./counter.py 
Traceback (most recent call last):
  File "./counter.py", line 65, in <module>
    main()
  File "./counter.py", line 56, in main
    WATER_VALUE = int( result['result'][0]['Value'])
KeyError: 'result'
do you have any idea why ?

Thank you!
Multinet
PI 2 - Domoticz 2021.1
RFXCOM - RFXtrx433 USB 433.92MHz Transceiver (5 DIO 54755 + 2 DIO 54756 + 3 DIO 54798)
Z-Wave.Me ZME_UZB1 USB Stick (6 FGSD002 + 2 FGRM222 + 1 FGS223 + 1 FGMS001-ZW5 + 1 FGRGBWM441 + 1 FGBS001 + 2 FGFS101)
6 sondes DS18B20
JelleB
Posts: 15
Joined: Tuesday 23 February 2016 10:06
Target OS: Raspberry Pi / ODroid
Domoticz version: V2.4768
Contact:

Re: GPIO Interrupt for water meter

Post by JelleB »

Bram2202,

would you be able to share/give some more guidance on how to modify the script (other than ip adress, port, pin nr and GPIO nr)?

I guess the script will be stored in /scripts and made executable. I did all this and get the same errors as above user.

Also, how did you specify this counter as RFX? I have connected it as hardware: PI GPIO pin.

Were are the # of pulses stored?

Thanks in advance.

Jelle
RidingTheFlow
Posts: 72
Joined: Friday 11 March 2016 18:23
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Essex, UK
Contact:

Re: GPIO Interrupt for water meter

Post by RidingTheFlow »

Here is script I made based on above idea. This script is very resilient and should never miss pulses as long as it running (e.g. even if Domoticz stalls/stops it will keep accumulating counts and will send update to Domoticz when it can). Also it minimizes amount of code running inside interrupt handler, avoiding swamping Domoticz with update requests, high CPU usage or even missed updates if many pulses happen quickly.

You can adjust delay inside main loop to whatever *maximum* frequency you want Domoticz to be updated (script won't even bother trying to update if counter does not change). It will never miss pulses so can be whatever you want, but 30 secs is reasonable considering 1 minute is generally lowest precision you can get from Domoticz reports anyway.

This script requires that you make "Virtual Incremental Counter" in "Dummy" device on Domoticz and set their IDs as GAS_IDX & ELEC_IDX inside script
Set GAS_GPIO and ELEC_GPIO to your input GPIOs

Code: Select all

#!/usr/bin/env python

#imports
import time
import RPi.GPIO as GPIO
import json
import urllib2
import threading

#vars
GET_URL = 'http://localhost:8080/json.htm?type=devices&rid=%d'
SET_URL = 'http://localhost:8080/json.htm?type=command&param=udevice&idx=%d&svalue=%d'
GAS_DELTA = 0
GAS_IDX = 3
GAS_GPIO = 26
GAS_COUNTER_LOCK = threading.Lock()
ELEC_DELTA = 0
ELEC_IDX = 4
ELEC_GPIO = 16
ELEC_COUNTER_LOCK = threading.Lock()

def gpio_intr(pin):
    if pin == GAS_GPIO:
        global GAS_DELTA
        with GAS_COUNTER_LOCK:
            GAS_DELTA += 1
        print 'Gas counter tick: %d' % GAS_DELTA

    if pin == ELEC_GPIO:
        global ELEC_DELTA
        with ELEC_COUNTER_LOCK:
            ELEC_DELTA += 1
        print 'Electricity counter tick: %d' % ELEC_DELTA

def main():
    global GAS_DELTA
    global ELEC_DELTA

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GAS_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.add_event_detect(GAS_GPIO, GPIO.RISING, callback=gpio_intr, bouncetime=100)
    GPIO.setup(ELEC_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.add_event_detect(ELEC_GPIO, GPIO.RISING, callback=gpio_intr, bouncetime=100)

    while True:
        time.sleep(5)

        GAS_DELTA_POST = GAS_DELTA

        if GAS_DELTA_POST != 0:
            try:
                res = json.load(urllib2.urlopen(SET_URL % (GAS_IDX, GAS_DELTA_POST)))
                if res['status'] != 'OK':
                    raise Exception('Domoticz json error')
                with GAS_COUNTER_LOCK:
                    GAS_DELTA -= GAS_DELTA_POST
            except Exception as e:
                print e

        ELEC_DELTA_POST = ELEC_DELTA

        if ELEC_DELTA_POST != 0:
            try:
                res = json.load(urllib2.urlopen(SET_URL % (ELEC_IDX, ELEC_DELTA_POST)))
                if res['status'] != 'OK':
                    raise Exception('Domoticz json error')
                with ELEC_COUNTER_LOCK:
                    ELEC_DELTA -= ELEC_DELTA_POST
            except Exception as e:
                print e

        print 'Gas delta %d; Elec delta %d' % (GAS_DELTA_POST, ELEC_DELTA_POST)

if __name__=="__main__":
    main() 
To set your "actual" meter values, issue "fake update" to meter via JSON, or run script with initial xxx_DELTA set to appropriate values - but only do this *once*, normally script must start with deltas set to zero, or it will increase meter every time it restarts.

Startup of script & output logging is up to you.
E.g. you can start it up on boot with cron & disable unnecessary print statements (or configure Python logging to log to file).

Update: tabs fixed.
JelleB
Posts: 15
Joined: Tuesday 23 February 2016 10:06
Target OS: Raspberry Pi / ODroid
Domoticz version: V2.4768
Contact:

Re: GPIO Interrupt for water meter

Post by JelleB »

Ridingthe flow,
this works with me. However, when opening the water tap, I see the counter increments but also get some 'Domoticz Json error' warnings. What could this mean?

Also, how do I get the counter values as nicely configured variable in the Utilities tab of the domoticz web interface?
RidingTheFlow
Posts: 72
Joined: Friday 11 March 2016 18:23
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Essex, UK
Contact:

Re: GPIO Interrupt for water meter

Post by RidingTheFlow »

Did you follow the bold text instructions to create a Dummy hardware and virtual increment counters?

Script will not create an actual device for you, you need to create virtual device manually (then you will see it in UI), and put device ID in the script so script will update it.

P.S. Maybe you made device of wrong type. You need to make "Increment Counter", only this one will work with this script. After you created increment counter you can change its subtype to "water".
JelleB
Posts: 15
Joined: Tuesday 23 February 2016 10:06
Target OS: Raspberry Pi / ODroid
Domoticz version: V2.4768
Contact:

Re: GPIO Interrupt for water meter

Post by JelleB »

Yes I did and replaced 'GAS' with 'WATER'.

Indeed the Virtual increment counter shows up as watermeter and data 0 kWh. Should I select another dummy, to get water in liters or can i edit the properties of the dummy?

IDX should be the IDX of the dummy device (not the hardware IDX)
RidingTheFlow
Posts: 72
Joined: Friday 11 March 2016 18:23
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Essex, UK
Contact:

Re: GPIO Interrupt for water meter

Post by RidingTheFlow »

Click "edit" button on device, and set "type" to "water". This should change device icon to water droplet and it should show m^3.
Device "Idx" is from "devices" table.
JelleB
Posts: 15
Joined: Tuesday 23 February 2016 10:06
Target OS: Raspberry Pi / ODroid
Domoticz version: V2.4768
Contact:

Re: GPIO Interrupt for water meter

Post by JelleB »

Got it.
When I start the script manually it prints the delta and the ticks and increments the liters in domoticz.

I added the automatic start of the scriot to crontab:

@reboot python /home/pi/domoticz/scripts/watermeter.py &

However, upon reboot, the counter is not incremented and the meter in the dashboard shows 'last seen ...10 min ago or something. Also the total liters is not shown.
What can be wrong?
RidingTheFlow
Posts: 72
Joined: Friday 11 March 2016 18:23
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Essex, UK
Contact:

Re: GPIO Interrupt for water meter

Post by RidingTheFlow »

You need root to run script and you don't need & for crontab.
Try

Code: Select all

@reboot sudo python /home/pi/domoticz/scripts/watermeter.py
Check after reboot if script is running with

Code: Select all

ps ax
JelleB
Posts: 15
Joined: Tuesday 23 February 2016 10:06
Target OS: Raspberry Pi / ODroid
Domoticz version: V2.4768
Contact:

Re: GPIO Interrupt for water meter

Post by JelleB »

Thanks, removed the & from crontab and checked the scripts is runnng.

The sensor on the watermeter nicely blinks every liter, however Domoticz reports I have used 20 m3 since yesterday. This does not make sense.

How can I monitor what the script sends back to domoticz (run print statements in the terminal?) and how can I reset the incremetal counter to 0 ?
RidingTheFlow
Posts: 72
Joined: Friday 11 March 2016 18:23
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Essex, UK
Contact:

Re: GPIO Interrupt for water meter

Post by RidingTheFlow »

The data is being posted to domoticz near line json.load(urllib2.urlopen(SET_URL ....
You can put a print there to see what it post and when.

You can't really "reset"a counter, many things go crazy if counter tries to go backwards. You will need to delete a counter and create a new one.
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests