Shop OBEX P1 Docs P2 Docs Learn Events
Traffic Light System With Pedestrian Crossing — Parallax Forums

Traffic Light System With Pedestrian Crossing

SOMAVISOMAVI Posts: 3
edited 2018-11-02 18:40 in BASIC Stamp
Hi Everyone,

I am new to the world of PBASIC but am grateful for the principles I am learning.

I am working on a similar project to https://forums.parallax.com/discussion/169193/push-buttons-being-read-while-other-actions-are-being-performed and have reviewed the suggestions there already. My traffic lights are working and the pushbuttons are as well.

The additional condition for me is that while a pedestrian is crossing in one direction, the traffic sequence in the other direction must continue. This seems like trying to approximate parallel running whereas PBASIC seems to be procedural. Right now if a pedestrian is crossing in one lane(say E-W) , the other lane(N-S) finishes its current sequence and then its red lights remain on till the pedestrian in lane E-W finishes crossing. I need a way for the N-S even after finishing its current sequence to start all over again independent of the pedestrian crossing in E-W which is going on nonetheless.

I would appreciate any help I could get. My current code is below.

pedButtonEW VAR Word 'Button to press for pedestrian crossing
pedButtonNS VAR Word 'Button to press for pedestrian crossing
waitew VAR Byte 'variable to ensure crossing waits until the lane is red before beginning
waitns VAR Byte 'variable to ensure crossing waits until the lane is red before beginning
flash VAR Word  'variable for flashing LED at the later part of pedestrian crossing
rest VAR Word  ' variable to simulate pause using for loops
testew VAR Word 'variable to ensure cycle runs twice between consecutive pedestrian crossings
testns VAR Word 'variable to ensure cycle runs twice between consecutive pedestrian crossings
newew VAR Byte
newns VAR Byte

testew = 2
testns = 2
waitew = 0
waitns = 0

DO
  GOSUB begin
  GOSUB eastwestleft
  IF pedButtonEW = 1 THEN
    GOSUB crossingew
  ELSEIF pedButtonNS = 1 THEN
    GOSUB crossingns
  ENDIF
  GOSUB northsouth
  IF pedButtonEW = 1 THEN
    GOSUB crossingew
  ELSEIF pedButtonNS = 1 THEN
    GOSUB crossingns
  ENDIF
LOOP

begin:     'Beginning of Overall Sequence. All lights red.
  GOSUB check
  LOW 14
  HIGH 15
  HIGH 12
  HIGH 11
  HIGH 6
  FOR rest = 1 TO 200
    GOSUB check
    PAUSE 10
  NEXT
  IF pedButtonEW = 1 THEN
    GOSUB crossingew
  ELSEIF pedButtonNS = 1 THEN
    GOSUB crossingns
  ENDIF
  LOW 12
  LOW 11
RETURN

eastwestleft:   'Sequence for East-West-Left Turn lane
  waitew = 1
  newew = 1
  GOSUB check
  GOSUB ns
  LOW 12
  LOW 11
  LOW 13
  LOW 5
  LOW 10
  LOW 4
  HIGH 6
  HIGH 8
  HIGH 7
  FOR rest = 1 TO 100
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT

  'Left arrow goes To amber ANd THEn red
  LOW 7
  HIGH 10
  FOR rest = 1 TO 200
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT
  LOW 10
  HIGH 11
  FOR rest = 1 TO 200
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT

  'After 2-3 seconds (TO clear the intersection), the W-E light will go green
  LOW 6
  HIGH 4
  FOR rest = 1 TO 500
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT

  'After a WHILE, the W-E and E-W lights will go TO amber and THEN red together
  LOW 4
  LOW 8
  HIGH 5
  FOR rest = 1 TO 100 'PAUSE 1000
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT
  LOW 5
  LOW 10
  HIGH 6
  HIGH 12
  FOR rest = 1 TO 250
    GOSUB check
    GOSUB ns
    PAUSE 10
  NEXT
  waitew = 0
  testew = testew + 1
  newew = 0
RETURN

northsouth: 'Sequence for North-South Turn lane
  waitns = 1
  newns = 1
  LOW 4
  LOW 7
  LOW 8
  LOW 14
  LOW 15
  HIGH 13
  HIGH 12
  HIGH 6
  HIGH 11
  FOR rest = 1 TO 500  'Pause 5 seconds
    GOSUB check
    GOSUB ew
    PAUSE 10
  NEXT
  'After a WHILE, the N-S and S-N lights will go TO amber and THEN red together
  LOW 13
  HIGH 14
  FOR rest = 1 TO 100
    GOSUB check
    GOSUB ew
    PAUSE 10
  NEXT
  LOW 14
  HIGH 15
  GOSUB check
  GOSUB ew
  waitns = 0
  testns = testns + 1
  newns = 0
RETURN

check:       'subroutine to check if pushbutton is pressed
  IF (IN9 = 1) AND (pedButtonEW = 0)THEN
    pedButtonEW = 1
  ELSE
     'For Debug testing
  ENDIF
  IF (IN0 = 1) AND (pedButtonNS = 0) THEN
    pedButtonNS = 1
  ELSE
    'for Debug testing
  ENDIF
RETURN

crossingew:   'subroutine to allow pedestrian to cross the East-West lane
  IF waitew = 0 AND testew >= 2 THEN
    HIGH 3
    LOW 2
    LOW 15
    LOW 10
    LOW 5
    pedButtonEW = 0
    GOSUB northsouth
    PAUSE 14000
    FOR flash = 1 TO 20
      HIGH 3
      LOW 2
      PAUSE 248
      LOW 3
      LOW 2
      PAUSE 248
    NEXT
    LOW 3
    HIGH 2
    testew = 0
  ELSEIF waitew = 0 AND testew < 2 THEN
    pedButtonEW = 0
  ELSE

  ENDIF
RETURN


crossingns:   'subroutine to allow pedestrian to cross the North-South lane
  IF waitns = 0 AND testns >= 2 THEN
    HIGH 1
    LOW 14
    LOW 13
    pedButtonNS = 0
    GOSUB eastwestleft
    PAUSE 5500
    FOR flash = 1 TO 20
      HIGH 1
      PAUSE 249
      LOW 1
      PAUSE 249
    NEXT
    testns = 0
  ELSEIF waitns = 0 AND testns < 2 THEN
    pedButtonNS = 0
  ELSE
  ENDIF
RETURN

ew: ' call pedestrian crossing subroutine
  IF pedButtonEW = 1 THEN
      GOSUB crossingew
  ENDIF
RETURN

ns: 'call pedestrian crossing subroutine
  IF pedButtonNS = 1 THEN
      GOSUB crossingns
  ENDIF
RETURN

Comments

  • It looks like it's time to change the design to that of multiple state machines. Currently, you're keeping track of what you're doing by the location in your program. That won't work any more. A state machine is usually drawn as a graph with nodes that represent actions to be done (essentially at one moment of time) and transitions from one node to another with these transitions conditioned in your case by pushbuttons. You number the states and label the code for the state with a label like STATE00: or STATE01:. The current state number is kept in each of two variables so you can have two parallel state machines running. There's another arbitrary variable available that could be used to keep track of "time to go" for a delay or action. This looks like:
    variableA  var  byte ' arbitrary variable for 1st state machine
    variableB  var  byte '  these might be used for delay counters
    variableX  var  byte
    nextStateA  var  byte ' next state for 1st state machine
    nextStateB  var  byte ' next state for 2nd state machine
    nextStateX  var  byte ' holds current state during state machine execution
    
     nextStateA = 0 ' both machines start at 00
     nextStateB = 0
    mainLoop:
     nextStateX = nextStateA ' default action is to repeat same state
     variableX = variableA
     on nextStateA gosub state00,state01,state02 ' and so on
     variableA = variableX
     nextStateA = nextStateX ' save new state for next cycle
     nextStateX = nextStateB ' default action is to repeat same state
     variableX = variableB
     on nextStateB gosub state00,state01,state02
     nextStateB = nextStateX ' save new state for next cycle
     variableB = variableX
     goto mainLoop
    
    STATE00: ' initial state
     ' initialize the I/O pins
     nextStateX = 1 ' on to next state
     return
    
    STATE01: ' check button x pressed
     if INS[???] = 1 then
       nextStateX = 2
       return
     endif
     ' any other checks needed?
     return ' default is to repeat this state next time
    
    STATE02: ' check button x released
     if INS[???] = 0 then
       nextStateX = 3
       return
     endif
     ' any other checks?
     return
    
    STATE03: ' button x was pressed and released
     ' initialize variable for 10 second delay in 'ticks' of 100ms
     variableX = 10 * 100
     nextStateX = 4 ' next state does counting
     return
    
    STATE04: ' delay for 10 seconds
     variableX = variableX - 1
     if variableX > 0 then nextStateX = 1 else pause 100
     return
    
    I'm sure this seems overly complicated, but once you change the problem description so that it seems like you have two or more independent controllers running in parallel, this ends up being simpler than pretty much anything else. Do lookup "finite state machine" in Wikipedia
  • SOMAVI, you just got about a million bucks worth of advice and tutoring for free. Study it carefully. Treasure it forever.
  • SOMAVI, you just got about a million bucks worth of advice and tutoring for free. Study it carefully. Treasure it forever.

    +1

  • oops. "variableX = 10 * 100" should be "variableX = 100"

    It also helps if you group I/O pins with one set of pushbuttons and LEDs in the first set of I/O pins (0-7) and the other set of pushbuttons and LEDs in the second set of I/O pins (8-15). This way you can have another variable, say "iobank" that's set to 0 before the 1st "on ... gosub" and set to 8 before the 2nd "on ... gosub". Your I/O statements can add "iobank" to the I/O pin number like: "if INS.bit(button+iobank) = 1 then"
  • Thank you so much. I appreciate this. I will work on it and get back as soon as possible.
    Mike Green wrote: »
    It looks like it's time to change the design to that of multiple state machines. Currently, you're keeping track of what you're doing by the location in your program. That won't work any more. A state machine is usually drawn as a graph with nodes that represent actions to be done (essentially at one moment of time) and transitions from one node to another with these transitions conditioned in your case by pushbuttons. You number the states and label the code for the state with a label like STATE00: or STATE01:. The current state number is kept in each of two variables so you can have two parallel state machines running. There's another arbitrary variable available that could be used to keep track of "time to go" for a delay or action. This looks like:
    variableA  var  byte ' arbitrary variable for 1st state machine
    variableB  var  byte '  these might be used for delay counters
    variableX  var  byte
    nextStateA  var  byte ' next state for 1st state machine
    nextStateB  var  byte ' next state for 2nd state machine
    nextStateX  var  byte ' holds current state during state machine execution
    
     nextStateA = 0 ' both machines start at 00
     nextStateB = 0
    mainLoop:
     nextStateX = nextStateA ' default action is to repeat same state
     variableX = variableA
     on nextStateA gosub state00,state01,state02 ' and so on
     variableA = variableX
     nextStateA = nextStateX ' save new state for next cycle
     nextStateX = nextStateB ' default action is to repeat same state
     variableX = variableB
     on nextStateB gosub state00,state01,state02
     nextStateB = nextStateX ' save new state for next cycle
     variableB = variableX
     goto mainLoop
    
    STATE00: ' initial state
     ' initialize the I/O pins
     nextStateX = 1 ' on to next state
     return
    
    STATE01: ' check button x pressed
     if INS[???] = 1 then
       nextStateX = 2
       return
     endif
     ' any other checks needed?
     return ' default is to repeat this state next time
    
    STATE02: ' check button x released
     if INS[???] = 0 then
       nextStateX = 3
       return
     endif
     ' any other checks?
     return
    
    STATE03: ' button x was pressed and released
     ' initialize variable for 10 second delay in 'ticks' of 100ms
     variableX = 10 * 100
     nextStateX = 4 ' next state does counting
     return
    
    STATE04: ' delay for 10 seconds
     variableX = variableX - 1
     if variableX > 0 then nextStateX = 1 else pause 100
     return
    
    I'm sure this seems overly complicated, but once you change the problem description so that it seems like you have two or more independent controllers running in parallel, this ends up being simpler than pretty much anything else. Do lookup "finite state machine" in Wikipedia

  • I realize this though I probably will understand the gravity more once I start using this in code.

    I will do my best to make the investment worth it.
    SOMAVI, you just got about a million bucks worth of advice and tutoring for free. Study it carefully. Treasure it forever.

Sign In or Register to comment.