Zik Mir
Zik Mir

Reputation: 69

Countdown timer in Python (Raspberry Pi for motor RPM)

I am trying to get a reasonable RPM for a motor. I have a photo-interrupter that sends 0 or 1 if it detects a slit that passes by. My code is as following, what I simply need is to time 0-60 seconds, and add the number of times 1 was detected and then divide it by 60, thus RPM.

I don't want to complicate my code, also keep in mind this is running on the Raspberry Pi with many other scripts and webservers running behind it. So not a bad code like "sleep" that would hog down the entire cpu.

I'm not looking for a crazy accurate measurement, just a reasonable idea of how fast the motor is spinning. Also does the slit size make a difference?

import RPi.GPIO as GPIO 
import time
signal = 21

from time import sleep     # this lets us have a time delay (see line 15)  
GPIO.setmode(GPIO.BCM)     # set up BCM GPIO numbering  
GPIO.setup(signal, GPIO.IN)    # set GPIO21 as input (signal)  
GPIO.add_event_detect(signal,GPIO.BOTH)


try:  
    while True:            # this will carry on until you hit CTRL+C  
        if GPIO.event_detected(signal): # if port 21 == 1  
            print "Port 21 is 1/HIGH/True - LED ON"  
            slit=slit+1       # Counts every time slit passes

        else:  
            print "Port 21 is 0/LOW/False - LED OFF"  
        rpm = slit/60         # Calculates the RPM
        sleep(0.0001)         # wait 0.0001 seconds  

finally:                   # this block will run no matter how the try block exits  
    GPIO.cleanup()         # clean up after yourself  

Upvotes: 0

Views: 1766

Answers (1)

JohanL
JohanL

Reputation: 6891

With the reservation that I have never programmed an RPi using Python, this is how I would try to tackle this task:

import RPi.GPIO as GPIO 
import threading

signal = 21
GPIO.setmode(GPIO.BCM)     # set up BCM GPIO numbering  
GPIO.setup(signal, GPIO.IN)    # set GPIO21 as input (signal)  

slit = 0
rpm = 0

def increment():
    global slit
    print("Port 21 is 1/HIGH/True - LED ON")
    slit += 1

def count_complete():
    global slit
    global rpm
    rpm = slit/60
    print ("RPM = {}".format(rpm))
    # reset and restart timer
    slit = 0
    t = threading.Timer(60, count_complete)

# Bounce will prevent double detection of the same rising edge (for 1 ms)
GPIO.add_event_detect(signal, GPIO.RISING, callback=increment, bounce=1)

t = threading.Timer(60, count_complete)
try:  
    while True:            # this will carry on until you hit CTRL+C
        pass               # Do nothing but wait for callbacks and timer
finally:                   # this block will run no matter how the try block exits
    t.cancel()             # stop the timer  
    GPIO.cleanup()         # clean up after yourself  

As can be seen, I have added a callback function that will be called as soon as a rising edge is detected on the input. Here the clit counter is incremented. The bounce parameter is a protection that will stop the program from counting the same slit double.

Setting at 1 ms means that the RPM needs to be (well) below 1,000 or some slits will be missed. It is possible to experiment by removing this. I don't know if it is possible to give a fractional bounce time. That may be another option.

The main program then, stats a timer, that lets the increment run for 1 minute, after which the RPM is calculated and the system reset and a new count is initiated. (There is a risk of a race condition here, where a slit interrupt may happen while doing the calculation. This could be missed in these cases.)

Then in the main loop, nothing happens, except for the program waiting for interrupts (slit, timer or ctrl-c). This is a busy wait, since it will continuously run the pass statement, doing nothing. Maybe there is a way to improve performance and make the program completely event driven, freeing up the CPU in the mean time, but that is a bigger task than I am attempting here.

Upvotes: 1

Related Questions