Pulse monitoring on a Pi

I’ve done a fair bit of research on this without finding an answer I feel confident in and I’m pretty sure I’ll get a reliable answer here which others may find useful (might even add it to ‘Learn’) :smile:

I have a couple of 5V pulse sensors (one for gas and another for water) which I want to read using a Pi (Raspberry flavoured then Orange). I know 5V is bad for GPIO pins on a Pi but I cannot find a reliable explanation of how to connect the sensors (with the relevant resistors) and then setup the pins correctly to read the result (using python). I know this has an impact but, try as I might, I really do not understand Pull-up and Pull-down resistors (electronics never was my forte)! There is a lot of very poor advice out there :frowning:

I can’t help you there, but I can hopefully deal with some other points.

If you have a digital input, it can have two physical states (that may or may not be the same as the logical states, but that’s a separate issue). If the voltage is anywhere in between, undesirable things can happen. So there are two ways to get it into one of the desired states: You can wire it to the pole of a double-throw switch so that it connects directly to one voltage or the other, or you can wire it to a single-throw switch and ‘pull’ the input into the other state when the switch is open. So if you wire the switch between input and ground, so the switch pulls the input to 0 V when it’s closed, you need a “pull-up” resistor to pull the input up to 3.3 V or 5 V when the switch is open. And vice versa.

If your sensor runs off a separate voltage supply, i.e. it has 3 wires and (say) it needs 5 V and your input can only take 3.3 V, then you need to add a pair of resistors as a voltage divider to reduce the voltage that appears on the sensor’s output wire. If you look at the circuit diagram of the emonTx Shield, you’ll see exactly that on SCK line to the RFM12B - R7 and R20 (and the MOSI and SEL lines). SCK out of the processor is a 5 V signal, dropped down to 3.3 V required by the SCK input of the RFM12B.

1 Like

Hi Brian

Assuming you have a high/low or on/off signal that’s 3.3v compatible (safe and switching points) here a some notes that may help on using Python to read the pulses.

This is a basic Python script I mentioned on the Directly connecting to Optical Pulse Counter with RPi?
thread.

It is a little intermediate script I used to directly connect a couple of optical pulse senors to a RPi, I have also tested it with hall sensors for Gas and SO pulse output from both a generation meter and a water meter. hopefully most of it is self explanatory, there are some notes about setting it up with emonHub (it uses a socket interfacer which I am still writing some notes on and will post here on the forum when I can)

#!/usr/bin/env python2
"""

Modified as a initial test script to explore pulse counting on a Pi, this script
just posts via a socket interfacer but will be the basis for a in-built emonhub
pulse counting interfacer.

Currently counts pulses from 0 when started so a whaccumulator will be needed
in emoncms to handle resets until the current meter reading is persisted so that
counting can continue from where it left off, a manual correction technique
will be required to correct for any downtime too.

this will post no quicker than the interval set, if set for 5secs and pulse is
every 1sec then the current total will be sent each 5secs. if pulse was every
10secs then the update would be every 10 secs as no empty data is sent, ie when
the 10sec pulse occurs the ">5secs" allows imediate update. can be used in
conjunction with emonhub's reporter (send) interval if a short interval is
prefered so that more timestamps are recorded eg update every 1sec to emonhub
but only update emoncms every 10s etc

Add the following interfacer to emonhub.conf
        [[Pulse]]
        Type = EmonHubSocketInterfacer
        [[[init_settings]]]
                port_nb = 50012
        [[[runtimesettings]]]
                timestamped = True

and if needed open the port in the firewall
        sudo ufw allow 50012
        sudo ufw -f enable



see http://openenergymonitor.org/emon/node/1732#comment-27749 re gas pulses

"""
__author__ = 'Paul Burnell (pb66)'

try:
    import RPi.GPIO as GPIO
    rpi = True
except:
    rpi = False
    print('RPi.GPIO not installed')
import time
import socket

# a "pulse" is 1 unit of measurement use emonHub scales and unit to define
# eg if 100 pulses is 1m3, ie 0.01m3 each then emonHub should use scale = 0.01 and unit = m3
# Therefore "pulse_id" becomes an accumulating "total used" and should follow the meter reading

nodeid = 18
valueid = 1
bounce = 1
interval = 5
lastsend = 0
host = "localhost" #emonbase1"
port = 50012
pulse_pin1 = 13
pulse_pin2 = 15

pulse_id = {1:0,2:0}

def eventHandler1(channel):
    processpulse(1,GPIO.input(channel))
def eventHandler2(channel):
    processpulse(2,GPIO.input(channel))


#    print("event")

def processpulse(channel,status):
    global pulse_id
    global frame
    global lastsend
    if status: #GPIO.input(channel):
        pulse_id[channel] += 1
#        print("Channel "+ str(channel) + "  on : " + str(pulse_id[channel]))
#    else:
#       print("Channel "+ str(channel) + " off : " + str(pulse_id[channel]))

    t = time.time()
    f = ' '.join((str(t), str(nodeid), str(pulse_id[1]), str(pulse_id[2])))
    if t > (lastsend + interval):
        lastsend = t
#        print f
        send(f)



def send(f):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    f = f + '\r\n'
    s.send(f)
    s.close()

def main():
    if rpi:
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(pulse_pin1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pulse_pin1, GPIO.BOTH, callback=eventHandler1, bouncetime=bounce)
        GPIO.setup(pulse_pin2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pulse_pin2, GPIO.BOTH, callback=eventHandler2, bouncetime=bounce)

    while True:  # CTRL+C to break - requires graceful exit
        try:
            if rpi:
                continue
            else:
                processpulse()
#                time.sleep(0.3)
        except KeyboardInterrupt:
            GPIO.cleanup()       # clean up GPIO on CTRL+C exit

if __name__ == "__main__":
        main()

there is plenty of info out there on the RPi.GPIO module, much of which is scattered in examples and tutorials on many other sites.

Much like setting up an interrupt in Arduino you assign the pins and attach an event function

        GPIO.setup(pulse_pin1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pulse_pin1, GPIO.BOTH, callback=eventHandler1, bouncetime=bounce)

sets up a pin as an input with a pullup or pulldown resitor (pull down in this example) in the first line and then adds an event function called “eventHandler1” to that pin, that is called on “BOTH” a rising and falling signal (can be RISING, FALLING or BOTH) and the optional bounce time is 1ms (ie nigh on no bounce filter on this example)

See the inputs docs for for more info.

The RPi.GPIO is just one lib used there are several, this was(is) the officially RPF supported code, until more recently the gpiozero libs appeared and have become THE official RPF gpio tool but RPI.GPIO is (I believe) a more advanced lib (fully supported) and the gpiozero is a more user friendly approach,

See GPIO Zero: Developing a new friendly Python API for Physical Computing - Ben Nuttall

1 Like