Shop OBEX P1 Docs P2 Docs Learn Events
Turn on 2 LEDs with 1 button — Parallax Forums

Turn on 2 LEDs with 1 button

Hi I'm quite new to microprocessors.
I have a basic stamp 2 and I am working on a program which turns turns two LEDs on an off with one button as follows: Button is pressed the first time and LED1 goes on. Button is pressed the second time and both LEDs are on. Button is pressed the third time and both LEDs go off. I played around all morning with "Button" and "count" etc but had not much luck as I am still lacking the required knowledge. Could somebody help me please.

Comments

  • How about you start with a program that makes the LED follow the button. That is, when the button is pushed the LED is "ON" and when the button is released the LED is "OFF". When that works, you can build on it.
  • In the BASIC Stamp editor, pull down the Help menu and open the "What's a Microcontroller?" PDF. Chapters 2 and 3 will get you going.
  • OK did you hookup the LED's correctlly to the pins and use a current limiting resistor
  • How about you start with a program that makes the LED follow the button. That is, when the button is pushed the LED is "ON" and when the button is released the LED is "OFF". When that works, you can build on it.

    I have got that far with the code but struggle with the part that keeps LEDs to stay on when the button is released. I don't expect you to write code for me, just need to be pointed into the right direction. Thanks
  • Jeff Haas wrote: »
    In the BASIC Stamp editor, pull down the Help menu and open the "What's a Microcontroller?" PDF. Chapters 2 and 3 will get you going.

    Thanks, l have been there but
    couldn't find any help that keeps the LEDs on when the button is released again. Even Google did not come up with any answers.
  • DigitalBob wrote: »
    OK did you hookup the LED's correctlly to the pins and use a current limiting resistor

    Yes, the hardware is all ready to go. Just need to complete the code. I feel like learning a new language. Its hard to get started but the penny will drop eventually. I purchased the basic stamp over than 10 years ago for a project that I never completed as I just didn't have the time and patients. Now semi retired I pulled it out of the drawer eager to give it another go.
  • Ok, if you run that little program it will show the LED status on the Debug terminal, you don't need any LEDs to try it out for starters.
  • This is where a little state-driven program is a good choice. I don't have a BS2 handy to test with, but this does compile.
    ' =========================================================================
    '
    '   File...... button_states.bs2 (not tested)
    '   Purpose...
    '   Author.... JonnyMac
    '   E-mail....
    '   Started...
    '   Updated... 07 DEC 2020
    '
    '   {$STAMP BS2}
    '   {$PBASIC 2.5}
    '
    ' =========================================================================
    
    
    ' -----[ Program Description ]---------------------------------------------
    
    
    ' -----[ Revision History ]------------------------------------------------
    
    
    ' -----[ I/O Definitions ]-------------------------------------------------
    
    BtnPin          PIN     15
    
    Led1            PIN     1
    Led0            PIN     0
    
    
    ' -----[ Constants ]-------------------------------------------------------
    
    IS_ON           CON     1                       ' for active-high in/out
    IS_OFF          CON     0
    
    YES             CON     1
    NO              CON     0
    
    
    ' -----[ Variables ]-------------------------------------------------------
    
    btnPress        VAR     Byte
    x               VAR     byte
    ledState        VAR     Byte
    
    
    
    ' -----[ Initialization ]--------------------------------------------------
    
    Reset:
      LOW Led0                                      ' start with LEDs off
      LOW Led1
    
    
    ' -----[ Program Code ]----------------------------------------------------
    
    Main:
      GOSUB Debounce
      IF (btnPress = YES) THEN
        ledState = ledState + 1
        IF (ledState = 3) THEN
          ledState = 0
        ENDIF
    
        IF (ledState = 0) THEN
          LOW Led0
          LOW Led1
        ELSEIF (ledState = 1) THEN
          HIGH Led0
          LOW Led1
        ELSE
          HIGH Led0
          HIGH Led1
        ENDIF
    
        PAUSE 250
      ENDIF
    
    
      GOTO Main
    
    
    ' -----[ Subroutines ]-----------------------------------------------------
    
    Debounce:
      btnPress = YES                                ' assume pressed
      FOR x = 1 TO 25                               ' debounce for 25ms
        PAUSE 1
        btnPress = btnPress & BtnPin                ' make sure it's still pressed
      NEXT
      RETURN
    
    
    ' -------------------------------------------------------------------------
    
    
    ' -----[ User Data ]-------------------------------------------------------
    
  • The code works very well, thanks a lot for your help Jon. Looking at the code it mostly makes sense to me now. The little debounce routine keeps me puzzled. But I played around with the timing and it does now what I want it to. If you hold the button too long the LEDs start flashing but it does not matter for the project. Our ceiling lamp in the living room has 3 light bulbs but only one switch. I want to be able turn on 1, 2 or 3 bulbs. So when I first switch it on only one light bulb lights up. This will also power up the controller. If I now turn the switch off and on quickly (quick enough that basic stamp does not lose power) the controller will read the off-on cycle (through a relay on pin 15) and which turn the next light bulb on. Same procedure for the third bulb.
    Once I have more practice, I might figure out a way to save the state of ledState to memory ( if at all possible with basic stamp2) so next time I turn the light on the same number of bulbs turn on as last time.
  • stefhorn wrote: »
    ...figure out a way to save the state of ledState to memory ( if at all possible with basic stamp2)...
    Sure can! Check out the WRITE & READ statements. The BS2 has 2k of memory for this sort of thing.
  • JonnyMacJonnyMac Posts: 8,912
    edited 2020-12-09 15:37
    The little debounce routine keeps me puzzled.
    The idea is that the button has to be pressed on entry to that subroutine and stay pressed for at least 25ms. If the button is released at any time, the state variable to will change to 0 and stay that way for the rest of the loop. This is why I set the state variable to 1 on entry. This is one (very simple) way to debounce a button.
    f you hold the button too long the LEDs start flashing...
    That can be fixed by a subroutine that forces the button to be released. This version has that, as well as READ and WRITE to save your state to non-volatile memory.
    ' =========================================================================
    '
    '   File...... button_states.bs2 (not tested)
    '   Purpose...
    '   Author.... JonnyMac
    '   E-mail....
    '   Started...
    '   Updated... 08 DEC 2020
    '
    '   {$STAMP BS2}
    '   {$PBASIC 2.5}
    '
    ' =========================================================================
    
    
    ' -----[ Program Description ]---------------------------------------------
    
    
    ' -----[ Revision History ]------------------------------------------------
    
    
    ' -----[ I/O Definitions ]-------------------------------------------------
    
    BtnPin          PIN     15
    
    Leds            VAR     OUTA                    ' control up to 4 outputs
    
    
    ' -----[ Constants ]-------------------------------------------------------
    
    IS_ON           CON     1                       ' for active-high in/out
    IS_OFF          CON     0
    
    YES             CON     1
    NO              CON     0
    
    
    ' -----[ Variables ]-------------------------------------------------------
    
    ledState        VAR     Byte
    btnPress        VAR     Byte
    timer           VAR     Byte
    
    
    ' -----[ Initialization ]--------------------------------------------------
    
    Reset:
      DIRA = %1111
    
      READ Saved, ledState                          ' get last saved state
      GOSUB Set_Leds                                ' update LEDs
    
    
    ' -----[ Program Code ]----------------------------------------------------
    
    Main:
      GOSUB Debounce_Button                         ' button pressed?
      IF (btnPress = YES) THEN
        ledState = ledState + 1                     ' update state, 0..3
        IF (ledState = 4) THEN
          ledState = 0
        ENDIF
        GOSUB Set_Leds                              ' update outputs
        WRITE Saved, ledState                       ' save for power-down
        GOSUB Release_Button                        ' force button release
      ENDIF
    
      ' other code here
    
      GOTO Main
    
    
    ' -----[ Subroutines ]-----------------------------------------------------
    
    Set_Leds:
      LOOKUP ledState, [%000, %001, %011, %111], Leds
      RETURN
    
    ' -------------------------------------------------------------------------
    
    Debounce_Button:
      btnPress = YES                                ' assume pressed
      FOR timer = 1 TO 25                           ' debounce for 25ms
        PAUSE 1
        btnPress = btnPress & BtnPin                ' make sure it's still pressed
      NEXT
      RETURN
    
    ' -------------------------------------------------------------------------
    
    Release_Button:
      timer = 0                                     ' reset timer
      DO WHILE (timer < 50)                         ' hold while button is pressed
        PAUSE 1
        IF (BtnPin = YES) THEN
          GOTO Release_Button
        ELSE
          timer = timer + 1
        ENDIF
      LOOP
      RETURN
    
    
    ' -----[ User Data ]-------------------------------------------------------
    
    Saved           DATA    0
    
  • Cluso99Cluso99 Posts: 18,066
    WARNING: I noticed you talk about controlling a ceiling light. I do hope you know about using high voltages as they can kill !!!
    If not, you need to know about interfacing to high voltages before you hook anything up.
  • Cluso99 wrote: »
    WARNING: I noticed you talk about controlling a ceiling light. I do hope you know about using high voltages as they can kill !!!
    If not, you need to know about interfacing to high voltages before you hook anything up.

    Thanks, I am intending to interface the 2 outputs as well as the input with high voltage relays?
  • Thanks for the improved version of the code. Looking at it makes me realize that there is a lot more to learn than I first thought. It makes we wonder, after I completed this project with the basic stamp, if learning Qbasic is the way to go would a more commonly used programming language such as C be a better option. I am not intending to become a professional programmer its just that programmable controllers have always fascinated me but I never had the time to learn more about them due to job and family commitments. Corona has changed all that and I am finding myself with a lot of time on my hands.
  • Cluso99Cluso99 Posts: 18,066
    Relays aren’t just the solution. You need isolation etc etc.
  • Cluso99 wrote: »
    Relays aren’t just the solution. You need isolation etc etc.

    My neighbor is an electrician, I will ask him to check for compliance.
  • kwinnkwinn Posts: 8,697
    stefhorn wrote: »
    Cluso99 wrote: »
    Relays aren’t just the solution. You need isolation etc etc.

    My neighbor is an electrician, I will ask him to check for compliance.

    I would definitely suggest using solid state relays between the BS2 and the lights to switch them on and off.
  • stefhorn wrote: »
    Thanks for the improved version of the code. Looking at it makes me realize that there is a lot more to learn than I first thought. It makes we wonder, after I completed this project with the basic stamp, if learning Qbasic is the way to go would a more commonly used programming language such as C be a better option. I am not intending to become a professional programmer its just that programmable controllers have always fascinated me but I never had the time to learn more about them due to job and family commitments. Corona has changed all that and I am finding myself with a lot of time on my hands.

    When I moved to Dallas in the late 90's I joined the Dallas Personal Robotics Group. The tag line on their web site was, "It's harder than it looks." I'd prefer to say, "There's more to it than you think -- if you want to do it well." We only see the skill of the professional athlete while on display, never the hours of practice to get to that level. Like many here, I've been programming for a long time and I tend to do things -- even the simple things -- as if they were a professional project. Taking simple things for granted, I've found, often leads to trouble.

    If you want to be an embedded programmer there are a number of languages you *could* learn -- but none are better or worse except for the way you want to do things. For the Propeller, I prefer Spin (its native language). Others prefer C. Many prefer BASIC. A few in the forums prefer Forth. All get the job done -- and that's what matters: getting the job done. I've been consulting with the Propeller since 2006 and never once has a client demanded which programming language I use; they only ask me to meet their operational requirements. The BASIC Stamp is only programmable in BASIC. If you decide to work with the Propeller, or other processors, your choice of programming tools will open up. While I focus on Spin, I've learned and understand the other languages as well, because working with them a bit causes me to think differently, and that can be beneficial when solving programming problems.
  • Tracy AllenTracy Allen Posts: 6,656
    edited 2020-12-12 17:26
    Since you're learning ways to do things, I'll suggest a different way to program the operation. The following program is a state machine, just one DO:LOOP, 5 instructions, with no subroutines or modes, and it uses math to compute the led state. Debouncing is provided by a PAUSE, where the minimum value depends on the pushbutton (some are bouncier than others), and the maximum value depends on user comfort while pressing the button.

    I got a ceiling light for our kitchen that has a function like yours built in, based on toggling the switch to cycle through the states. Instead of changing the number of bulbs lit, it changes the color temperature of the light. I wonder exactly how they do that.

    '{$STAMP BS2pe}
    '{$PBASIC 2.5}
    
    BtnPin          PIN     7
    
    leds      VAR  OUTA                    ' control 3 leds, on p0 to p2, patterns 000, 001, 011, 111
    state      VAR  NIB                     ' state increments from 0 to 3 and back to 0, to light 0,1,2 or 3 leds.
    oldBtn    VAR  BIT
    newBtn   VAR  BIT
    
    main:
      DIRA = %0111
      state = 1     ' initially one light on, could read from eeprom
      oldBtn = INA(BtnPin).     ' correction, should be IN0(BtnPin), see below in thread
      DO
        newbtn = IN0(BtnPin)
        state = (newBtn ^ oldBtn & oldBtn) + state // 4 ' computes left to right
        ' state increments when the button goes hi to low, returns to zero after 3
        ' DEBUG DEC state, TAB, BIN1 newbtn, TAB, BIN1 oldbtn,CR  ' to visualize the states, use PAUSE 100
        oldbtn = newbtn 
        leds = DCD state - 1  '  the DCD command chooses value 1,2,4,8, then subtract 1 to get 0,1,3,7 for the leds.
        PAUSE 5      ' this pause can be 5ms to 100ms, or it can hold other code to do other tasks.
      LOOP
    
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-12-09 19:57
    Solid state relays designed to switch mains voltages are good. You’ll want 1500V isolation at a minimum. You will also need isolation between the “hot” side (the side that switches the mains) and the driving side (the micro etc). And I do mean full isolation - no crossover tracks etc! And remember you need to cover the mains side so you cannot touch them by mistake.
    Just because you neighbour is an electrician doesn’t mean he understands electronics and the isolation necessary. But he will have a healthy regard for the mains because he knows it can kill.

    Photos before you hook it up might help but don’t expect to get authoritative advice. There are also legal requirements too.

    I’m just trying to ensure you’re not going to take unnecessary risks, or endanger anyone else.
  • I have a healthy regard for mains power as well as I have been electrocuted several times in my life through faulty appliances or sheer stupidity but live to tell the tail. Also the controller will be mounted inside the ceiling light which is 9 feet off the ground. But I certainly will follow your advice, who knows who will change the light bulbs in years to come.
  • Since you're learning ways to do things, I'll suggest a different way to program the operation. The following program is a state machine, just one DO:LOOP, 5 instructions, with no subroutines or modes, and it uses math to compute the led state. Debouncing is provided by a PAUSE, where the minimum value depends on the pushbutton (some are bouncier than others), and the maximum value depends on user comfort while pressing the button.

    I got a ceiling light for our kitchen that has a function like yours built in, based on toggling the switch to cycle through the states. Instead of changing the number of bulbs lit, it changes the color temperature of the light. I wonder exactly how they do that.

    '{$STAMP BS2pe}
    '{$PBASIC 2.5}
    
    BtnPin          PIN     7
    
    leds      VAR  OUTA                    ' control 3 leds, on p0 to p2, patterns 000, 001, 011, 111
    state      VAR  NIB                     ' state increments from 0 to 3 and back to 0, to light 0,1,2 or 3 leds.
    oldBtn    VAR  BIT
    newBtn   VAR  BIT
    
    main:
      DIRA = %0111
      state = 1     ' initially one light on, could read from eeprom
      oldBtn = INA(BtnPin)
      DO
        newbtn = IN0(BtnPin)
        state = (newBtn ^ oldBtn & oldBtn) + state // 4 ' computes left to right
        ' state increments when the button goes hi to low, returns to zero after 3
        ' DEBUG DEC state, TAB, BIN1 newbtn, TAB, BIN1 oldbtn,CR  ' to visualize the states, use PAUSE 100
        oldbtn = newbtn 
        leds = DCD state - 1  '  the DCD command chooses value 1,2,4,8, then subtract 1 to get 0,1,3,7 for the leds.
        PAUSE 5      ' this pause can be 5ms to 100ms, or it can hold other code to do other tasks.
      LOOP
    

    Thank you, another interesting solution. I am not trying to pretend that I understand all of it. One of the 3 light bulbs is directly connected to the light switch so only 2 bulbs have to be controlled. I managed to change that. I does not appear to read / write to memory which is not a big problem. I ordered the parts and hopefully have it all working by Xmas.
  • '{$STAMP BS2pe}
    '{$PBASIC 2.5}
    
    BtnPin  PIN   7            '<---****
    
    leds    VAR  OUTA
    state   VAR  NIB
    oldBtn  VAR  BIT
    newBtn  VAR  BIT
    
    main:
      DIRA = %0111
      state = 1
      oldBtn = INA(BtnPin)     '<---***
    
      DO
        newbtn = IN0(BtnPin)   '<---***
        state = (newBtn ^ oldBtn & oldBtn) + state // 4
        oldbtn = newbtn 
        leds = DCD state - 1
        PAUSE 5
      LOOP
    

    ...ok, so I attempted to unpack this bit of code knowing full well Mr. Allen's acumen in getting the BS to "dance". But, I'm stumped. I couldn't get any further than the use of INA and IN0 as used in the lines marked with "<---***".
    BtnPin
    
    is defined as PIN 7, which puts it into pin group INB?
    INA(BtnPin)
    
    ...I just don't understand. INA consists of pins 0-3. And I believe INA can be treated as an array. But does this mean BS pin 7 is wired to a pin in group INA? Or is it the *state* of BtnPin that's used as the index into the INA array?
    IN0(BtnPin)
    
    ...same confusion as above. Why is INA/IN0 involved when pin 7 is the button input? Again, is it the *state* of BtnPin the issue?

    After using the BS2 for a few years, I'm actually ashamed to ask these questions. Once they are clarified, I'll move to unpacking the "math" line.

    Methinks there may be other readers wondering the same thing, so I'll be the sacrificial question-asker.
  • Dave, thanks for asking. It made me refocus on my PBASIC chops, which unfortunately are getting rusty and tend to get caught in my spinning propellers! I always do enjoy writing PBASIC and using the Stamp when the occasion arises. It is so self-contained, fun and effective for all the things it can do.

    I could have written,
    oldbtn = btnPin   ' in the initialization
    and
    newbtn = btnPin    ' in the DO:LOOP
    
    that is better. I slipped on the PBASIC syntax options for the PIN specifier. This also works,
    oldbtn = IN0(btnPin)  ' in the initialization,  INA(btnPin) was a typo, sorry about the red herring!
    and
    newbtn = IN0(btnPin)   ' in the DO:LOOP
    

    The PIN specifier is rather tricky, and I have a whole page on it at this URL, BS2pbasic25.htm#PIN_examples. Sometimes, as in the first usage above, it is treated as reading the state of the pin, and in the second usage as an array index offset from IN0, it is treated as the pin number. The page also includes a summary of an email that Jeff Martin had sent me to clarify the logic of it.

    There is more about the math for pushbutton state machines at this URL.




  • Mr. Allen - thank you for the clarification. I was worried I understood less than I thought about the Stamp.

    A number of my projects involve switches; one has 10 push buttons. I'll review the method of reading them as you noted above.

    And thank you for the reminder of the wealth of Stamp information on your site.
    It is so self-contained, fun and effective for all the things it can do.
    Agreed.
  • The advantage of that approach is that it can scan multiple buttons or binary signals in parallel, flagging the changes for counting or actions.

    I had occasion to pick up an old project, a BASIC Stamp to read the output from 20 rain gages and two anemometers. All switch actions to be counted. I'd prototyped it on the Stamp. But I ended up using the same algorithm in PIC chips, in order to conserves power, using the wake up on change feature, and also to isolate the exposed sensors from the Stamp core. This was used for a study in forest hydrology, looking at rainfall distribution on the forest edge and under individual trees. The photo has RJ11 connectors for the rain gages, the PIC chips by each row, and the Stamp and support circuits on the plug-in board.



    432 x 432 - 69K
  • I ended up using Tracy Allen's Code and it almost works perfectly. However the relay board I am using switches the relays on when the input signal is low.
    So changed the code a little:

    '{$STAMP BS2}
    '{$PBASIC 2.5}

    BtnPin PIN 7

    leds VAR OUTA ' control 3 leds, on p0 to p2, patterns 000, 001, 011, 111
    state VAR Nib ' state increments from 0 to 3 and back to 0, to light 0,1,2 or 3 leds.
    oldBtn VAR Bit
    newBtn VAR Bit

    main:
    HIGH 01 'to stop relay 1 clicking on and off when controller is first powered up
    HIGH 00 ' as above

    DIRA = %0111
    state = 2 ' initially both bulbs are off
    oldbtn = IN0(btnPin)
    DO
    newbtn = IN0(BtnPin)
    state = ((newBtn ^ oldBtn & oldBtn) + state) // 3
    PAUSE 100
    'oldbtn = newbtn
    leds = DCD state -1
    PAUSE 5 ' this pause can be 5ms to 100ms, or it can hold other code to do other tasks.
    LOOP

    However, to make it all work as intended, I need the state sequence reversed to 2, 1, 0, 2....... but can`t figure out how.
  • Tracy AllenTracy Allen Posts: 6,656
    edited 2020-12-19 06:35
    One solution would be to flip all the bits at the end, so you have,
    leds = ~(DCD state - 1)
    The ~ operator on the Stamp simply inverts all the bits, so as the state goes 0,1,2, the leds go
    %00    %01    %11   old way without the ~
    %11    %10    %00    new way with the ~ inversion of bits.
    
    In any case, the program can set an initial state for when the power first comes on.

    Alternatively, you could use the LOOKUP command, like Jon did in the program he posted above.
    LOOKUP state, [%11, %01, %00], Leds
    
    That is a clearer way to do it. I enjoy tricky math, but it is hard to explain even to one's self when coming back to it later. With LOOKUP, there is no confusion, and the order of states can be anything you need.

    Another alternative would be to run through the states backward, leds = DCD (2 - state) - 1. So state goes 0,1,2 and 2-state goes 2,1,0.

    I see that the line, 'oldbtn = newbtn is commented out. No, that has to be there, memory of the past state, or the logic won't work.


  • Tracy Allen, the ~ operator was the answer and its working perfectly now.
    Thank you everyone for helping me with my first Basic Stamp project.
Sign In or Register to comment.