OYPS
OYPS

Reputation: 81

Python GPIO triggering LED with Pushbutton

I am trying to control and LED on an Raspberry Pi.

I want the LED to light up when I push a button, and maintain that state until I push the button again.

I have implemented the code below and it works quite fine. However, I am getting problems when I am not fast enough in pushing the button or hold the button.

import RPi.GPIO as GPIO
from time import sleep

inpin = 16
outpin = 20

GPIO.setmode(GPIO.BCM)
counter = 0
GPIO.setup(outpin, GPIO.OUT)
GPIO.setup(inpin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

try:
        while True:

                if GPIO.input(inpin):
                        if counter == 0:
                                print "port is low"
                                GPIO.output(outpin, 0)
                                counter = 0

                        else:
                                print "port is high"
                                GPIO.output(outpin, 1)
                                counter = 1
                else:
                        if counter == 1:
                                print "port is low"
                                GPIO.output(outpin, 0)
                                counter = 0
                        else:
                                print "port is high"
                                GPIO.output(outpin, 1)
                                counter = 1
                sleep(0.1)

finally:
        GPIO.cleanup()

Implementing it the way "TessellatingHeckler" suggested works perfect. Even with multiple inputs and outputs it works perfect. Important things are the "elif" loops to guarantee fast changing states. Here's the working code:

import RPi.GPIO as GPIO
from time import sleep



GPIO.setmode(GPIO.BCM)
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(20, GPIO.OUT)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(27, GPIO.OUT)


btn1_button = 'up'
btn1_light = 'off'

btn2_button = 'up'
btn2_button = 'off'



def LED1(output):
    GPIO.output(20, output)

def LED2(output):
    GPIO.output(27, output)

while True:


######################## BUTTON 1 ########################
    if (btn1_button == 'up' and btn1_light == 'off'):
            if not  GPIO.input(16):
            print "LED1 ON"
            LED1(1)         
            btn1_button = 'down'
            btn1_light = 'on'


    elif (btn1_button == 'down' and btn1_light == 'on'):
            if GPIO.input(16):
            btn1_button = 'up'


    elif (btn1_button == 'up' and btn1_light == 'on'):
            if not GPIO.input(16):
            print "LED1 OFF"
            LED1(0)
            btn1_button = 'down'
            btn1_light = 'off'


    elif (btn1_button == 'down' and btn1_light == 'off'):
            if GPIO.input(16):
            btn1_button = 'up'

###########################################################

####################### BUTTON 2 ##########################
    if (btn2_button == 'up' and btn2_light == 'off'):
                if not  GPIO.input(17):
                        print "LED2 ON"
                        LED2(1)
                        btn2_button = 'down'
                        btn2_light = 'on'

    elif (btn2_button == 'down' and btn2_light == 'on'):
                if GPIO.input(17):
                        btn2_button = 'up'


    elif (btn2_button == 'up' and btn2_light == 'on'):
                if not GPIO.input(17):
                        print "LED2 OFF"
                        LED2(0)
                        btn2_button = 'down'
                        btn2_light = 'off'


    elif (btn2_button == 'down' and btn2_light == 'off'):
                if GPIO.input(17):
                        btn2_button = 'up'
    sleep(0.1)
###########################################################

GPIO.cleanup()

Upvotes: 3

Views: 6329

Answers (2)

sas
sas

Reputation: 21

Try the following, This eliminates "led-on" problem at start:

import RPi.GPIO as GPIO
from time import sleep

inpin = 11
outpin = 7

GPIO.setmode(GPIO.BOARD)
GPIO.setup(outpin, GPIO.OUT)
GPIO.setup(inpin, GPIO.IN)

x=0
y=0

try:

   while True:

     if (x==0   and   y==0):
           # wait for button press before changing anything
          if GPIO.input(inpin) == 0:
                 GPIO.output(outpin, 0)
                 x=0 
                 y=1
                 print("led off")

     elif (x==0  and   y==1):
           # stay in this state until button released
          if GPIO.input(inpin) == 1:
             x = 1

     elif (x==1   and   y==1):
          if GPIO.input(inpin) == 0:
                 GPIO.output(outpin, 1)
                 x=1
                 y=0
                 print("led on")

     elif (x==1 and   y==0):
          if GPIO.input(inpin) == 1:
             x=0
     sleep(0.1)


except KeyboardInterrupt:
          GPIO.output(outpin, 0)
          GPIO.cleanup()    

Upvotes: 1

TessellatingHeckler
TessellatingHeckler

Reputation: 28963

You wrote this plan in words:

  • Button pressed, light goes on
  • Button pressed, light goes off

But what you've written in code is more like:

  • 10x per second,
    • If the button is up, do nothing
    • If the button is pressed, toggle the light

Which is quite different. Hold the button for more than 1/10th of a second and it starts to go weird. What I meant by my comment is that your code goes through the loop and every time it tries to update everything; you're tracking what happened the last time through the loop, which means you can't hold anything more than one loop duration. Instead you need to separate the loop from the state tracking, so the state can stay the same over and over, and only let the dog eat the biscuit when the button changes.

It's the button which drives the system state, not time passing. The system can be in 4 possible states, like this:

(Button=Up, Light=Off) <----------------
        |                               |
        | Button pushed down            |
        \/                              |
(Button=Down, Light=/On/)               |
        |                               |
        | Button released               |
        \/                              |
(Button=Up, Light=On)                   |
        |                               |
        | Button pushed down            |
        \/                              |
(Button=Down, Light=/Off/)              |
        |                              / \
        | Button released               |
        |                               |
         -------------------------------

If you explicitly code those states and follow the sequence, let the button be the only thing allowing you to go from one to the next ... you can't possibly get any weird behaviour from holding the button too long. I hope. Your current code jumps from (Button=Down, Light=On) to (Button=Down, Light=Off) and back again.

My code here is untested, and I'm not completely sure which way the GPIO.input() goes when the button is pressed and released. I assume it's 0/False most of the time and 1/True when the button is pressed.

import RPi.GPIO as GPIO
from time import sleep

inpin = 16
outpin = 20

GPIO.setmode(GPIO.BCM)
GPIO.setup(outpin, GPIO.OUT)
GPIO.setup(inpin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

button='up'
light='off'

while True:

    if (button=='up'   and   light=='off'):
        # wait for button press before changing anything
        if not GPIO.input(inpin):
            GPIO.output(outpin, 1)
            button='down'; 
            light='on'

    elif (button=='down' and   light=='on'):
        # stay in this state until button released
        if GPIO.input(inpin):
            button='up'

    elif (button=='up'   and   light=='on'):
        if not GPIO.input(inpin):
            GPIO.output(outpin, 0)
            button='down'
            light='off'

    elif (button=='down' and   light=='off'):
        if GPIO.input(inpin):
            button='up'
    sleep(0.1)

So, button and light track the state of the system. Every time through the loop, only one of the if blocks will match, and it will mostly do nothing until it gets the button change that makes the state change to the next one.

First time through, the first block matches, it checks for a button press. It keeps doing that.

You press the button, now the first block lights the LED and updates the state.

Now, every time through the loop, (button=='down' and light=='on') matches. It's in state 2 and it stays in that state for as long as you press the button. Every time through the loop it looks for the button release, and that's the only thing that can trigger any state change.

etc.

Upvotes: 3

Related Questions